diff -Nru a/CREDITS b/CREDITS
--- a/CREDITS	Mon Oct  7 11:25:26 2002
+++ b/CREDITS	Mon Oct  7 11:25:26 2002
@@ -1081,6 +1081,11 @@
 D: Transformed old user space bdflush into 1st kernel thread - kflushd.
 D: Many other patches, documentation files, mini kernels, utilities, ...
 
+N: Masanori GOTO
+E: gotom@debian.or.jp
+D: Workbit NinjaSCSI-32Bi/UDE driver
+S: Japan
+
 N: John E. Gotts
 E: jgotts@linuxsavvy.com
 D: kernel hacker
@@ -3337,6 +3342,12 @@
 S: POB 1822 
 S: Socorro NM, 87801
 S: USA
+
+N: Hiroshi YOKOTA
+E: yokota@netlab.is.tsukuba.ac.jp
+D: Workbit NinjaSCSI-3/32Bi PCMCIA driver
+D: Workbit NinjaSCSI-32Bi/UDE driver
+S: Japan
 
 N: Eric Youngdale
 E: eric@andante.org
diff -Nru a/Documentation/Changes b/Documentation/Changes
--- a/Documentation/Changes	Mon Oct  7 11:25:39 2002
+++ b/Documentation/Changes	Mon Oct  7 11:25:39 2002
@@ -31,7 +31,7 @@
 Eine deutsche Version dieser Datei finden Sie unter
 <http://www.stefan-winter.de/Changes-2.4.0.txt>.
 
-Last updated: January 22, 2002
+Last updated: October 1st, 2002
 
 Chris Ricker (kaboom@gatech.edu or chris.ricker@genetics.utah.edu).
 
@@ -60,7 +60,8 @@
 o  pcmcia-cs              3.1.21                  # cardmgr -V
 o  PPP                    2.4.0                   # pppd --version
 o  isdn4k-utils           3.1pre1                 # isdnctrl 2>&1|grep version
-			  
+o  procps                 2.0.9                   # ps --version
+
 Kernel compilation
 ==================
 
@@ -80,9 +81,7 @@
 will need to be fixed in order to use these compilers. In any case, using
 pgcc instead of plain gcc is just asking for trouble.
 
-Note that gcc 2.7.2.3 and  gcc 2.91.66 (egcs-1.1.2) are no longer supported
-kernel compilers. The kernel no longer works around bugs in these versions,
-and, in fact, will refuse to be compiled with it.
+gcc 2.91.66 (egcs-1.1.2) continues to be supported for SPARC64 requirements.
 
 The Red Hat gcc 2.96 compiler subtree can also be used to build this tree.
 You should ensure you use gcc-2.96-74 or later. gcc-2.96-54 will not build
diff -Nru a/Documentation/DMA-mapping.txt b/Documentation/DMA-mapping.txt
--- a/Documentation/DMA-mapping.txt	Mon Oct  7 11:25:35 2002
+++ b/Documentation/DMA-mapping.txt	Mon Oct  7 11:25:35 2002
@@ -323,16 +323,12 @@
 
 	struct pci_pool *pool;
 
-	pool = pci_pool_create(name, dev, size, align, alloc, flags);
+	pool = pci_pool_create(name, dev, size, align, alloc);
 
 The "name" is for diagnostics (like a kmem_cache name); dev and size
 are as above.  The device's hardware alignment requirement for this
 type of data is "align" (which is expressed in bytes, and must be a
-power of two).  The flags are SLAB_ flags as you'd pass to
-kmem_cache_create.  Not all flags are understood, but SLAB_POISON may
-help you find driver bugs.  If you call this in a non- sleeping
-context (f.e. in_interrupt is true or while holding SMP locks), pass
-SLAB_ATOMIC.  If your device has no boundary crossing restrictions,
+power of two).  If your device has no boundary crossing restrictions,
 pass 0 for alloc; passing 4096 says memory allocated from this pool
 must not cross 4KByte boundaries (but at that time it may be better to
 go for pci_alloc_consistent directly instead).
diff -Nru a/Documentation/DocBook/Makefile b/Documentation/DocBook/Makefile
--- a/Documentation/DocBook/Makefile	Mon Oct  7 11:25:35 2002
+++ b/Documentation/DocBook/Makefile	Mon Oct  7 11:25:35 2002
@@ -154,7 +154,6 @@
 OUT     :=      $(patsubst %.sgml, %.out, $(BOOKS))
 
 clean:
-	@echo 'Cleaning up (DocBook)'
 	@rm -f core *~
 	@rm -f $(BOOKS)
 	@rm -f $(DVI) $(AUX) $(TEX) $(LOG) $(OUT)
@@ -162,7 +161,6 @@
 	@rm -f $(C-procfs-example)
 
 mrproper:
-	@echo 'Making mrproper (DocBook)'
 	@rm -f $(PS) $(PDF)
 	@rm -f -r $(HTML) $(patsubst %.html,%,$(HTML))
 
diff -Nru a/Documentation/filesystems/ext3.txt b/Documentation/filesystems/ext3.txt
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/Documentation/filesystems/ext3.txt	Mon Oct  7 11:26:06 2002
@@ -0,0 +1,139 @@
+
+Ext3 Filesystem
+===============
+
+ext3 was originally released in September 1999. Written by Stephen Tweedie
+for 2.2 branch, and ported to 2.4 kernels by Peter Braam, Andreas Dilger, 
+Andrew Morton, Alexander Viro, Ted Ts'o and Stephen Tweedie.
+
+ext3 is ext2 filesystem enhanced with journalling capabilities. 
+
+Options
+=======
+
+When mounting an ext3 filesystem, the following option are accepted:
+(*) == default
+
+jounal=update		Update the ext3 file system's journal to the 
+			current format.
+
+journal=inum		When a journal already exists, this option is 
+			ignored. Otherwise, it specifies the number of
+			the inode which will represent the ext3 file
+			system's journal file.
+
+bsddf 		(*)	Make 'df' act like BSD.
+minixdf			Make 'df' act like Minix.
+
+check=none		Don't do extra checking of bitmaps on mount.
+nocheck		
+
+debug			Extra debugging information is sent to syslog.
+
+noload			Don't load the journal on mounting.
+
+errors=remount-ro(*)	Remount the filesystem read-only on an error.
+errors=continue		Keep going on a filesystem error.
+errors=panic		Panic and halt the machine if an error occurs.
+
+grpid			Give objects the same group ID as their creator.
+bsdgroups		
+
+nogrpid		(*)	New objects have the group ID of their creator.
+sysvgroups
+
+resgid=n		The group ID which may use the reserved blocks.
+
+resuid=n		The user ID which may use the reserved blocks.
+
+sb=n			Use alternate superblock at this location.
+
+data=journal		All data are commited into the journal prior 
+			to being written into the main file system.
+		
+data=ordered	(*)	All data are forced directly out to the main file 
+			system prior to its metadata being commited to 
+			the journal.
+		
+data=writeback  	Data ordering is not preserved, data may be 
+			written into the main file system after its
+			metadata has been committed to the journal.
+
+quota			Quota options are currently silently ignored.
+noquota			(see fs/ext3/super.c, line 594)
+grpquota
+usrquota
+
+
+Specification
+=============
+ext3 shares all disk implementation with ext2 filesystem, and add
+transactions capabilities to ext2.  Journaling is done by the
+Journaling block device layer.
+
+Journaling Block Device layer
+-----------------------------
+The Journaling Block Device layer (JBD) isn't ext3 specific.  It was
+design to add journaling capabilities on a block device.  The ext3
+filesystem code will inform the JBD of modifications it is performing
+(Call a transaction).  the journal support the transactions start and
+stop, and in case of crash, the journal can replayed the transactions
+to put the partition on a consistant state fastly.
+
+handles represent a single atomic update to a filesystem.  JBD can
+handle external journal on a block device.
+
+Data Mode
+---------
+There's 3 different data modes:
+
+* writeback mode
+In data=writeback mode, ext3 does not journal data at all.  This mode
+provides a similar level of journaling as XFS, JFS, and ReiserFS in its
+default mode - metadata journaling.  A crash+recovery can cause
+incorrect data to appear in files which were written shortly before the
+crash.  This mode will typically provide the best ext3 performance.
+
+* ordered mode
+In data=ordered mode, ext3 only officially journals metadata, but it
+logically groups metadata and data blocks into a single unit called a
+transaction.  When it's time to write the new metadata out to disk, the
+associated data blocks are written first.  In general, this mode
+perform slightly slower than writeback but significantly faster than
+journal mode.
+
+* journal mode
+data=journal mode provides full data and metadata journaling.  All new
+data is written to the journal first, and then to its final location. 
+In the event of a crash, the journal can be replayed, bringing both
+data and metadata into a consistent state.  This mode is the slowest
+except when data needs to be read from and written to disk at the same
+time where it outperform all others mode.
+
+Compatibility
+-------------
+
+Ext2 partitions can be easily convert to ext3, with `tune2fs -j <dev>`.
+ Ext3 is fully compatible with Ext2.  Ext3 partitions can easily be
+mounted as Ext2.
+
+External Tools
+==============
+see manual pages to know more.
+
+tune2fs: 	create a ext3 journal on a ext2 partition with the -j flags
+mke2fs: 	create a ext3 partition with the -j flags
+debugfs: 	ext2 and ext3 file system debugger
+
+References
+==========
+
+kernel source:	file:/usr/src/linux/fs/ext3
+		file:/usr/src/linux/fs/jbd
+
+programs: 	http://e2fsprogs.sourceforge.net
+
+useful link:
+		http://www.zip.com.au/~akpm/linux/ext3/ext3-usage.html
+		http://www-106.ibm.com/developerworks/linux/library/l-fs7/
+		http://www-106.ibm.com/developerworks/linux/library/l-fs8/
diff -Nru a/Documentation/filesystems/proc.txt b/Documentation/filesystems/proc.txt
--- a/Documentation/filesystems/proc.txt	Mon Oct  7 11:25:28 2002
+++ b/Documentation/filesystems/proc.txt	Mon Oct  7 11:25:28 2002
@@ -191,6 +191,7 @@
 ..............................................................................
  File        Content                                           
  apm         Advanced power management info                    
+ buddyinfo   Kernel memory allocator information (see text)	(2.5)
  bus         Directory containing bus specific information     
  cmdline     Kernel command line                               
  cpuinfo     Info about the CPU                                
@@ -275,7 +276,7 @@
   ERR:       2155
 
 NMI is incremented in this case because every timer interrupt generates a NMI
-(Non Maskable Interrupt) which is used by the NMI Watchdog to detect lookups.
+(Non Maskable Interrupt) which is used by the NMI Watchdog to detect lockups.
 
 LOC is the local interrupt counter of the internal APIC of every CPU.
 
@@ -325,6 +326,25 @@
 Linux uses  slab  pools for memory management above page level in version 2.2.
 Commonly used  objects  have  their  own  slab  pool (such as network buffers,
 directory cache, and so on).
+
+..............................................................................
+
+> cat /proc/buddyinfo
+
+Node 0, zone      DMA      0      4      5      4      4      3 ...
+Node 0, zone   Normal      1      0      0      1    101      8 ...
+Node 0, zone  HighMem      2      0      0      1      1      0 ...
+
+Memory fragmentation is a problem under some workloads, and buddyinfo is a 
+useful tool for helping diagnose these problems.  Buddyinfo will give you a 
+clue as to how big an area you can safely allocate, or why a previous
+allocation failed.
+
+Each column represents the number of pages of a certain order which are 
+available.  In this case, there are 0 chunks of 2^0*PAGE_SIZE available in 
+ZONE_DMA, 4 chunks of 2^1*PAGE_SIZE in ZONE_DMA, 101 chunks of 2^4*PAGE_SIZE 
+availble in ZONE_NORMAL, etc... 
+
 
 1.3 IDE devices in /proc/ide
 ----------------------------
diff -Nru a/Documentation/filesystems/xfs.txt b/Documentation/filesystems/xfs.txt
--- a/Documentation/filesystems/xfs.txt	Mon Oct  7 11:25:37 2002
+++ b/Documentation/filesystems/xfs.txt	Mon Oct  7 11:25:37 2002
@@ -29,9 +29,6 @@
 	The preferred buffered I/O size can also be altered on an
 	individual file basis using the ioctl(2) system call.
 
-  dmapi/xdsm
-	Enable the DMAPI (Data Management API) event callouts.
-
   irixsgid
 	Do not inherit the ISGID bit on subdirectories of ISGID
 	directories, if the process creating the subdirectory
diff -Nru a/Documentation/networking/tuntap.txt b/Documentation/networking/tuntap.txt
--- a/Documentation/networking/tuntap.txt	Mon Oct  7 11:25:32 2002
+++ b/Documentation/networking/tuntap.txt	Mon Oct  7 11:25:32 2002
@@ -7,43 +7,71 @@
   FreeBSD TAP driver 
   Copyright (c) 1999-2000 Maksim Yevmenkin <m_evmenkin@yahoo.com>
 
+  Revision of this document 2002 by Florian Thiel <florian.thiel@gmx.net>
+
 1. Description
   TUN/TAP provides packet reception and transmission for user space programs. 
-  It can be viewed as a simple Point-to-Point or Ethernet device, which 
-  instead of receiving packets from a physical media, receives them from 
+  It can be seen as a simple Point-to-Point or Ethernet device, which,
+  instead of receiving packets from physical media, receives them from 
   user space program and instead of sending packets via physical media 
   writes them to the user space program. 
 
-  When a program opens /dev/net/tun, driver creates and registers corresponding
-  net device tunX or tapX. After a program closed above devices, driver will 
-  automatically delete tunXX or tapXX device and all routes corresponding to it.
-
-  This package(http://vtun.sourceforge.net/tun) contains two simple example 
-  programs how to use tun and tap devices. Both programs works like 
-  bridge between two network interfaces.
+  In order to use the driver a program has to open /dev/net/tun and issue a
+  corresponding ioctl() to register a network device with the kernel. A network
+  device will appear as tunXX or tapXX, depending on the options chosen. When
+  the program closes the file descriptor, the network device and all
+  corresponding routes will disappear.
+
+  Depending on the type of device chosen the userspace program has to read/write
+  IP packets (with tun) or ethernet frames (with tap). Which one is being used
+  depends on the flags given with the ioctl().
+
+  The package from http://vtun.sourceforge.net/tun contains two simple examples
+  for how to use tun and tap devices. Both programs work like a bridge between
+  two network interfaces.
   br_select.c - bridge based on select system call.
   br_sigio.c  - bridge based on async io and SIGIO signal.
-  However the best example is VTun http://vtun.sourceforge.net :))  
+  However, the best example is VTun http://vtun.sourceforge.net :))
 
 2. Configuration 
   Create device node:
+     mkdir /dev/net (if it doesn't exist already)
      mknod /dev/net/tun c 10 200
+  
+  Set permissions:
+     e.g. chmod 0700 /dev/net/tun
+     if you want the device only accesible by root. Giving regular users the
+     right to assign network devices is NOT a good idea. Users could assign
+     bogus network interfaces to trick firewalls or administrators.
 
   Driver module autoloading
      Make sure that "Kernel module loader" - module auto-loading support is enabled 
      in your kernel. 
 
-     Add following line to the /etc/modules.conf:
+     Add the following line to the /etc/modules.conf:
 	alias char-major-10-200 tun
-     
-     Run:
+     and run
         depmod -a 
-
-     Driver will be automatically loaded when application access /dev/net/tun.
+  
+  Manual loading 
+     insert the module by hand:
+        modprobe tun
+
+  If you do it the latter way, you have to load the module every time you
+  need it, if you do it the other way it will be automatically loaded when
+  /dev/net/tun is being opened.
 
 3. Program interface 
   3.1 Network device allocation:
 
+  char *dev should be the name of the device with a format string (e.g.
+  "tun%d"), but (as far as I can see) this can be any valid network device name.
+  Note that the character pointer becomes overwritten with the real device name
+  (e.g. "tun0")
+
+  #include <linux/if.h>
+  #include <linux/if_tun.h>
+
   int tun_alloc(char *dev)
   {
       struct ifreq ifr;
@@ -79,65 +107,44 @@
 
 Universal TUN/TAP device driver Frequently Asked Question.
    
-1. What is the TUN ?
-The TUN is Virtual Point-to-Point network device.
-TUN driver was designed as low level kernel support for
-IP tunneling. It provides to userland application
-two interfaces:
-  - /dev/tunX	- character device;
-  - tunX	- virtual Point-to-Point interface.
-
-Userland application can write IP frame to /dev/tunX
-and kernel will receive this frame from tunX interface. 
-In the same time every frame that kernel writes to tunX 
-interface can be read by userland application from /dev/tunX
-device.
-
-2. What is the TAP ?
-The TAP is a Virtual Ethernet network device.
-TAP driver was designed as low level kernel support for
-Ethernet tunneling. It provides to userland application
-two interfaces:
-  - /dev/tapX	- character device;
-  - tapX	- virtual Ethernet interface.
-
-Userland application can write Ethernet frame to /dev/tapX
-and kernel will receive this frame from tapX interface. 
-In the same time every frame that kernel writes to tapX 
-interface can be read by userland application from /dev/tapX
-device.
-
-3. What platforms are supported by TUN/TAP driver ?
+1. What platforms are supported by TUN/TAP driver ?
 Currently driver has been written for 3 Unices:
    Linux kernels 2.2.x, 2.4.x 
    FreeBSD 3.x, 4.x, 5.x
    Solaris 2.6, 7.0, 8.0
 
-4. What is TUN/TAP driver used for?
+2. What is TUN/TAP driver used for?
 As mentioned above, main purpose of TUN/TAP driver is tunneling. 
 It is used by VTun (http://vtun.sourceforge.net).
 
-5. How does Virtual network device actually work ? 
+Another interesting application using TUN/TAP is pipsecd
+(http://perso.enst.fr/~beyssac/pipsec/), an userspace IPSec
+implementation that can use complete kernel routing (unlike FreeS/WAN).
+
+3. How does Virtual network device actually work ? 
 Virtual network device can be viewed as a simple Point-to-Point or
 Ethernet device, which instead of receiving packets from a physical 
 media, receives them from user space program and instead of sending 
 packets via physical media sends them to the user space program. 
 
 Let's say that you configured IPX on the tap0, then whenever 
-kernel sends any IPX packet to tap0, it is passed to the application
-(VTun for example). Application encrypts, compresses and sends it to 
-the other side over TCP or UDP. Application on other side decompress 
-and decrypts them and write packet to the TAP device, kernel handles 
-the packet like it came from real physical device.
+the kernel sends an IPX packet to tap0, it is passed to the application
+(VTun for example). The application encrypts, compresses and sends it to 
+the other side over TCP or UDP. The application on the other side decompresses
+and decrypts the data received and writes the packet to the TAP device, 
+the kernel handles the packet like it came from real physical device.
 
-6. What is the difference between TUN driver and TAP driver?
+4. What is the difference between TUN driver and TAP driver?
 TUN works with IP frames. TAP works with Ethernet frames.
 
-7. What is the difference between BPF and TUN/TAP driver?
-BFP is a advanced packet filter. It can be attached to existing
-network interface. It does not provide virtual network interface.
-TUN/TAP driver does provide virtual network interface and it is possible
+This means that you have to read/write IP packets when you are using tun and
+ethernet frames when using tap.
+
+5. What is the difference between BPF and TUN/TAP driver?
+BFP is an advanced packet filter. It can be attached to existing
+network interface. It does not provide a virtual network interface.
+A TUN/TAP driver does provide a virtual network interface and it is possible
 to attach BPF to this interface.
 
-8. Does TAP driver support kernel Ethernet bridging?
+6. Does TAP driver support kernel Ethernet bridging?
 Yes. Linux and FreeBSD drivers support Ethernet bridging. 
diff -Nru a/Documentation/s390/CommonIO b/Documentation/s390/CommonIO
--- a/Documentation/s390/CommonIO	Mon Oct  7 11:25:39 2002
+++ b/Documentation/s390/CommonIO	Mon Oct  7 11:25:39 2002
@@ -123,9 +123,6 @@
 
 * /proc/chpids
 
-  This entry will only show up if you specified CONFIG_CHSC=y during kernel
-  config.
-
   This entry serves a dual purpose:
  
   - show which chpids are currently known to Linux and their status (online,
diff -Nru a/Documentation/sound/oss/Maestro3 b/Documentation/sound/oss/Maestro3
--- a/Documentation/sound/oss/Maestro3	Mon Oct  7 11:25:37 2002
+++ b/Documentation/sound/oss/Maestro3	Mon Oct  7 11:25:37 2002
@@ -71,9 +71,17 @@
 tell the driver to print minimal debugging information as it runs.  This
 can be collected with 'dmesg' or through the klogd daemon.
 
-The other is 'external_amp', which tells the driver to attempt to enable
+One is 'external_amp', which tells the driver to attempt to enable
 an external amplifier.  This defaults to '1', you can tell the driver
 not to bother enabling such an amplifier by setting it to '0'.
+
+And the last is 'gpio_pin', which tells the driver which GPIO pin number
+the external amp uses (0-15), The Allegro uses 8 by default, all others 1.
+If everything loads correctly and seems to be working but you get no sound, 
+try tweaking this value. 
+
+Systems known to need a different value
+        Panasonic ToughBook CF-72: gpio_pin=13 
 
 Power Management
 ----------------
diff -Nru a/Documentation/swsusp.txt b/Documentation/swsusp.txt
--- a/Documentation/swsusp.txt	Mon Oct  7 11:25:25 2002
+++ b/Documentation/swsusp.txt	Mon Oct  7 11:25:25 2002
@@ -156,6 +156,23 @@
 - do IDE cdroms need some kind of support?
 - IDE CD-RW -- how to deal with that?
 
+FAQ:
+
+Q: well, suspending a server is IMHO a really stupid thing,
+but... (Diego Zuccato):
+
+A: You bought new UPS for your server. How do you install it without
+bringing machine down? Suspend to disk, rearrange power cables,
+resume.
+
+You have your server on UPS. Power died, and UPS is indicating 30
+seconds to failure. What do you do? Suspend to disk.
+
+Ethernet card in your server died. You want to replace it. Your
+server is not hotplug capable. What do you do? Suspend to disk,
+replace ethernet card, resume. If you are fast your users will not
+even see broken connections.
+
 Any other idea you might have tell me!
 
 Contacting the author
diff -Nru a/Documentation/vm/hugetlbpage.txt b/Documentation/vm/hugetlbpage.txt
--- a/Documentation/vm/hugetlbpage.txt	Mon Oct  7 11:25:26 2002
+++ b/Documentation/vm/hugetlbpage.txt	Mon Oct  7 11:25:26 2002
@@ -25,13 +25,13 @@
       memory (mapped by hugeTLBs) in their address space.  When a process
       forks, then children share the same physical memory with their parent.
 
-      For the cases when an application wishes to keep the huge pages
-      private, the key value of 0 is defined.  In this case kernel allocates
-      hugetlb pages to the process that are not shareable across different
-      processes.  These segments are marked private for the process.  These
-      segments are not copied to children's address space on forks.
-
-AKPM: So what is present at that address within the child?
+      For the cases when an application wishes to keep the huge
+      pages private, the key value of 0 is defined.  In this case
+      kernel allocates hugetlb pages to the process that are not
+      shareable across different processes.  These segments are marked
+      private for the process.  These segments are not copied to
+      children's address space on forks - the child will have no
+      mapping for these virtual addresses.
 
       The key manangement (and assignment) part is left to user
       applications.
diff -Nru a/MAINTAINERS b/MAINTAINERS
--- a/MAINTAINERS	Mon Oct  7 11:25:35 2002
+++ b/MAINTAINERS	Mon Oct  7 11:25:35 2002
@@ -1166,6 +1166,14 @@
 W:	http://www.netlab.is.tsukuba.ac.jp/~yokota/izumi/ninja/
 S:	Maintained
 
+NINJA SCSI-32Bi/UDE PCI/CARDBUS SCSI HOST ADAPTER DRIVER
+P:	GOTO Masanori
+M:	gotom@debian.or.jp
+P:	YOKOTA Hiroshi
+M:	yokota@netlab.is.tsukuba.ac.jp
+W:	http://www.netlab.is.tsukuba.ac.jp/~yokota/izumi/ninja/
+S:	Maintained
+
 NON-IDE/NON-SCSI CDROM DRIVERS [GENERAL] (come on, crew - mark your responsibility)
 P:	Eberhard Moenkeberg
 M:	emoenke@gwdg.de
@@ -1432,6 +1440,12 @@
 M:	Kai.Makisara@metla.fi
 L:	linux-scsi@vger.kernel.org
 S:	Maintained
+
+SCx200 CPU SUPPORT
+P:	Christer Weinigel
+M:	christer@weinigel.se
+W:	http://www.weinigel.se
+S:	Supported
 
 SGI VISUAL WORKSTATION 320 AND 540
 P:	Bent Hagemark
diff -Nru a/Makefile b/Makefile
--- a/Makefile	Mon Oct  7 11:25:25 2002
+++ b/Makefile	Mon Oct  7 11:25:25 2002
@@ -1,6 +1,6 @@
 VERSION = 2
 PATCHLEVEL = 5
-SUBLEVEL = 40
+SUBLEVEL = 41
 EXTRAVERSION =
 
 # *DOCUMENTATION*
@@ -52,13 +52,6 @@
 
 all:	vmlinux
 
-#	Print entire command lines instead of short version
-#	For now, leave the default
-
-ifndef KBUILD_VERBOSE
-  KBUILD_VERBOSE = 1
-endif
-
 # 	Decide whether to build built-in, modular, or both.
 #	Normally, just do built-in.
 
@@ -88,7 +81,7 @@
   KBUILD_MODULES := 1
 endif
 
-export KBUILD_MODULES KBUILD_BUILTIN
+export KBUILD_MODULES KBUILD_BUILTIN KBUILD_VERBOSE
 
 # Beautify output
 # ---------------------------------------------------------------------------
@@ -105,12 +98,19 @@
 # If it is set to "silent_", nothing wil be printed at all, since
 # the variable $(silent_cmd_cc_o_c) doesn't exist.
 
+#	For now, leave verbose as default
+
+ifndef KBUILD_VERBOSE
+  KBUILD_VERBOSE = 1
+endif
+
+MAKEFLAGS += --no-print-directory
+
 #	If the user wants quiet mode, echo short versions of the commands 
-#	only and suppress the 'Entering/Leaving directory' messages
+#	only
 
 ifneq ($(KBUILD_VERBOSE),1)
   quiet=quiet_
-  MAKEFLAGS += --no-print-directory
 endif
 
 #	If the user is running make -s (silent mode), suppress echoing of
@@ -120,11 +120,18 @@
   quiet=silent_
 endif
 
-export quiet
+export quiet KBUILD_VERBOSE
 
-#
-# Include the make variables (CC, etc...)
-#
+#	Paths to obj / src tree
+
+src	:= .
+obj	:= .
+srctree := .
+objtree := .
+
+export srctree objtree
+
+# 	Make variables (CC, etc...)
 
 AS		= $(CROSS_COMPILE)as
 LD		= $(CROSS_COMPILE)ld
@@ -145,24 +152,21 @@
 AFLAGS_MODULE   = $(MODFLAGS)
 CFLAGS_KERNEL	=
 AFLAGS_KERNEL	=
-EXPORT_FLAGS    =
 NOSTDINC_FLAGS  = -nostdinc -iwithprefix include
 
+CPPFLAGS	:= -D__KERNEL__ -Iinclude
+CFLAGS 		:= $(CPPFLAGS) -Wall -Wstrict-prototypes -Wno-trigraphs -O2 \
+	  	   -fomit-frame-pointer -fno-strict-aliasing -fno-common
+AFLAGS		:= -D__ASSEMBLY__ $(CPPFLAGS)
+
 export	VERSION PATCHLEVEL SUBLEVEL EXTRAVERSION KERNELRELEASE ARCH \
 	CONFIG_SHELL TOPDIR HOSTCC HOSTCFLAGS CROSS_COMPILE AS LD CC \
 	CPP AR NM STRIP OBJCOPY OBJDUMP MAKE MAKEFILES GENKSYMS PERL
 
-export CPPFLAGS EXPORT_FLAGS NOSTDINC_FLAGS OBJCOPYFLAGS
+export CPPFLAGS NOSTDINC_FLAGS OBJCOPYFLAGS LDFLAGS
 export CFLAGS CFLAGS_KERNEL CFLAGS_MODULE 
 export AFLAGS AFLAGS_KERNEL AFLAGS_MODULE
 
-src	:= .
-obj	:= .
-srctree := $(TOPDIR)
-objtree := $(TOPDIR)
-
-export srctree objtree
-
 # The temporary file to save gcc -MD generated dependencies must not
 # contain a comma
 depfile = $(subst $(comma),_,$(@D)/.$(@F).d)
@@ -183,10 +187,46 @@
 
 .PHONY: scripts
 scripts:
-	@$(MAKE) -C scripts
+	+@$(call descend,scripts,)
+
+# Objects we will link into vmlinux / subdirs we need to visit
+# ---------------------------------------------------------------------------
+
+init-y		:= init/
+drivers-y	:= drivers/ sound/
+net-y		:= net/
+libs-y		:= lib/
+core-y		:=
+SUBDIRS		:=
 
 ifeq ($(filter $(noconfig_targets),$(MAKECMDGOALS)),)
 
+include-config := 1
+
+-include .config
+
+endif
+
+include arch/$(ARCH)/Makefile
+
+core-y		+= kernel/ mm/ fs/ ipc/ security/
+
+SUBDIRS		+= $(patsubst %/,%,$(filter %/, $(init-y) $(init-m) \
+		     $(core-y) $(core-m) $(drivers-y) $(drivers-m) \
+		     $(net-y) $(net-m) $(libs-y) $(libs-m)))
+
+ALL_SUBDIRS     := $(SUBDIRS) $(patsubst %/,%,$(filter %/, $(init-n) $(init-) \
+		     $(core-n) $(core-) $(drivers-n) $(drivers-) \
+		     $(net-n) $(net-) $(libs-n) $(libs-)))
+
+init-y		:= $(patsubst %/, %/built-in.o, $(init-y))
+core-y		:= $(patsubst %/, %/built-in.o, $(core-y))
+drivers-y	:= $(patsubst %/, %/built-in.o, $(drivers-y))
+net-y		:= $(patsubst %/, %/built-in.o, $(net-y))
+libs-y		:= $(patsubst %/, %/lib.a, $(libs-y))
+
+ifdef include-config
+
 # Here goes the main Makefile
 # ===========================================================================
 #
@@ -197,8 +237,6 @@
 
 #	In this section, we need .config
 
--include .config
-
 #	If .config doesn't exist - tough luck
 
 .config: arch/$(ARCH)/config.in $(shell find . -name Config.in)
@@ -215,6 +253,10 @@
 	@echo '***'
 	@exit 1
 
+ifdef CONFIG_MODULES
+export EXPORT_FLAGS := -DEXPORT_SYMTAB
+endif
+
 #
 # INSTALL_PATH specifies where to place the updated kernel and system map
 # images.  Uncomment if you want to place them anywhere other than root.
@@ -231,57 +273,6 @@
 MODLIB	:= $(INSTALL_MOD_PATH)/lib/modules/$(KERNELRELEASE)
 export MODLIB
 
-#
-# standard CFLAGS
-#
-
-CPPFLAGS := -D__KERNEL__ -I$(objtree)/include
-
-CFLAGS := $(CPPFLAGS) -Wall -Wstrict-prototypes -Wno-trigraphs -O2 \
-	  -fomit-frame-pointer -fno-strict-aliasing -fno-common
-AFLAGS := -D__ASSEMBLY__ $(CPPFLAGS)
-
-ifdef CONFIG_MODULES
-EXPORT_FLAGS := -DEXPORT_SYMTAB
-endif
-
-# Link components for vmlinux
-# ---------------------------------------------------------------------------
-
-init-y		:= init/
-drivers-y	:= drivers/ sound/
-networks-y	:= net/
-libs-y		:= lib/
-
-CORE_FILES	:= kernel/built-in.o mm/built-in.o fs/built-in.o \
-		   ipc/built-in.o security/built-in.o
-SUBDIRS		+= kernel mm fs ipc security
-
-include arch/$(ARCH)/Makefile
-
-SUBDIRS		+= $(patsubst %/,%,$(filter %/, $(init-y) $(init-m)))
-INIT		+= $(patsubst %/, %/built-in.o, $(init-y))
-
-SUBDIRS		+= $(patsubst %/,%,$(filter %/, $(core-y) $(core-m)))
-CORE_FILES	:= $(patsubst %/, %/built-in.o, $(core-y)) $(CORE_FILES)
-
-SUBDIRS		+= $(patsubst %/,%,$(filter %/, $(drivers-y) $(drivers-m)))
-DRIVERS		+= $(patsubst %/, %/built-in.o, $(drivers-y))
-
-SUBDIRS		+= $(patsubst %/,%,$(filter %/, $(networks-y) $(networks-m)))
-NETWORKS	+= $(patsubst %/, %/built-in.o, $(networks-y))
-
-SUBDIRS		+= $(patsubst %/,%,$(filter %/, $(libs-y) $(libs-m)))
-LIBS		+= $(patsubst %/, %/lib.a, $(libs-y))
-
-export	NETWORKS DRIVERS LIBS HEAD LDFLAGS MAKEBOOT
-
-# boot target
-# ---------------------------------------------------------------------------
-
-boot: vmlinux
-	@$(MAKE) -C arch/$(ARCH)/boot
-
 # Build vmlinux
 # ---------------------------------------------------------------------------
 
@@ -292,60 +283,71 @@
 #       we cannot yet know if we will need to relink vmlinux.
 #	So we descend into init/ inside the rule for vmlinux again.
 
-vmlinux-objs := $(HEAD) $(INIT) $(CORE_FILES) $(LIBS) $(DRIVERS) $(NETWORKS)
+vmlinux-objs := $(HEAD) $(init-y) $(core-y) $(libs-y) $(drivers-y) $(net-y)
 
-quiet_cmd_link_vmlinux = LD      $@
-define cmd_link_vmlinux
-	$(LD) $(LDFLAGS) $(LDFLAGS_vmlinux) $(HEAD) $(INIT) \
+quiet_cmd_vmlinux__ = LD      $@
+define cmd_vmlinux__
+	$(LD) $(LDFLAGS) $(LDFLAGS_vmlinux) $(HEAD) $(init-y) \
 	--start-group \
-	$(CORE_FILES) \
-	$(LIBS) \
-	$(DRIVERS) \
-	$(NETWORKS) \
+	$(core-y) \
+	$(libs-y) \
+	$(drivers-y) \
+	$(net-y) \
 	--end-group \
-	$(filter $(kallsyms.o),$^) \
+	$(filter .tmp_kallsyms%,$^) \
 	-o $@
 endef
 
 #	set -e makes the rule exit immediately on error
 
-define rule_vmlinux
+define rule_vmlinux__
 	set -e
-	echo '  Generating build number'
-	. scripts/mkversion > .tmp_version
-	mv -f .tmp_version .version
-	+$(MAKE) -C init
-	$(call cmd,link_vmlinux)
-	echo 'cmd_$@ := $(cmd_link_vmlinux)' > $(@D)/.$(@F).cmd
+	$(if $(filter .tmp_kallsyms%,$^),,
+	  echo '  Generating build number'
+	  . scripts/mkversion > .tmp_version
+	  mv -f .tmp_version .version
+	  +$(call descend,init,)
+	)
+	$(call cmd,vmlinux__)
+	echo 'cmd_$@ := $(cmd_vmlinux__)' > $(@D)/.$(@F).cmd
+endef
+
+define rule_vmlinux
+	$(rule_vmlinux__)
 	$(NM) $@ | grep -v '\(compiled\)\|\(\.o$$\)\|\( [aUw] \)\|\(\.\.ng$$\)\|\(LASH[RL]DI\)' | sort > System.map
 endef
 
 LDFLAGS_vmlinux += -T arch/$(ARCH)/vmlinux.lds.s
 
 #	Generate section listing all symbols and add it into vmlinux
+#	It's a three stage process:
+#	o .tmp_vmlinux1 has all symbols and sections, but __kallsyms is
+#	  empty
+#	  Running kallsyms on that gives as .tmp_kallsyms1.o with
+#	  the right size
+#	o .tmp_vmlinux2 now has a __kallsyms section of the right size,
+#	  but due to the added section, some addresses have shifted
+#	  From here, we generate a correct .tmp_kallsyms2.o
+#	o The correct .tmp_kallsyms2.o is linked into the final vmlinux.
 
 ifdef CONFIG_KALLSYMS
 
-kallsyms.o := .tmp_kallsyms.o
+kallsyms.o := .tmp_kallsyms2.o
 
 quiet_cmd_kallsyms = KSYM    $@
 cmd_kallsyms = $(KALLSYMS) $< > $@
 
-.tmp_kallsyms.o: .tmp_vmlinux
+.tmp_kallsyms1.o: .tmp_vmlinux1
 	$(call cmd,kallsyms)
 
-# 	After generating .tmp_vmlinux just like vmlinux, decrement the version
-#	number again, so the final vmlinux gets the same one.
-#	Ignore return value of 'expr'.
-
-define rule_.tmp_vmlinux
-	$(rule_vmlinux)
-	if expr 0`cat .version` - 1 > .tmp_version; then true; fi
-	mv -f .tmp_version .version
-endef
+.tmp_kallsyms2.o: .tmp_vmlinux2
+	$(call cmd,kallsyms)
 
-.tmp_vmlinux: $(vmlinux-objs) arch/$(ARCH)/vmlinux.lds.s FORCE
-	$(call if_changed_rule,.tmp_vmlinux)
+.tmp_vmlinux1: $(vmlinux-objs) arch/$(ARCH)/vmlinux.lds.s FORCE
+	$(call if_changed_rule,vmlinux__)
+
+.tmp_vmlinux2: $(vmlinux-objs) .tmp_kallsyms1.o arch/$(ARCH)/vmlinux.lds.s FORCE
+	$(call if_changed_rule,vmlinux__)
 
 endif
 
@@ -363,7 +365,7 @@
 
 .PHONY: $(SUBDIRS)
 $(SUBDIRS): .hdepend prepare
-	@$(MAKE) -C $@
+	+@$(call descend,$@,)
 
 #	Things we need done before we descend to build or make
 #	module versions are listed in "prepare"
@@ -377,7 +379,7 @@
 
 AFLAGS_vmlinux.lds.o += -P -C -U$(ARCH)
 
-arch/$(ARCH)/vmlinux.lds.s: arch/$(ARCH)/vmlinux.lds.S scripts FORCE
+arch/$(ARCH)/vmlinux.lds.s: %.s: %.S scripts FORCE
 	$(call if_changed_dep,as_s_S)
 
 targets += arch/$(ARCH)/vmlinux.lds.s
@@ -386,17 +388,17 @@
 # ---------------------------------------------------------------------------
 
 %.s: %.c FORCE
-	@$(MAKE) -C $(@D) $(@F)
+	+@$(call descend,$(@D),$@)
 %.i: %.c FORCE
-	@$(MAKE) -C $(@D) $(@F)
+	+@$(call descend,$(@D),$@)
 %.o: %.c FORCE
-	@$(MAKE) -C $(@D) $(@F)
+	+@$(call descend,$(@D),$@)
 %.lst: %.c FORCE
-	@$(MAKE) -C $(@D) $(@F)
+	+@$(call descend,$(@D),$@)
 %.s: %.S FORCE
-	@$(MAKE) -C $(@D) $(@F)
+	+@$(call descend,$(@D),$@)
 %.o: %.S FORCE
-	@$(MAKE) -C $(@D) $(@F)
+	+@$(call descend,$(@D),$@)
 
 # 	FIXME: The asm symlink changes when $(ARCH) changes. That's
 #	hard to detect, but I suppose "make mrproper" is a good idea
@@ -481,7 +483,7 @@
 	$(update-if-changed)
 
 $(patsubst %,_sfdep_%,$(SUBDIRS)): FORCE
-	@$(MAKE) -C $(patsubst _sfdep_%, %, $@) fastdep
+	+@$(call descend,$(patsubst _sfdep_%,%,$@),fastdep)
 
 else # !CONFIG_MODVERSIONS
 
@@ -499,7 +501,7 @@
 #	Build modules
 
 ifdef CONFIG_MODVERSIONS
-MODFLAGS += -include $(objtree)/include/linux/modversions.h
+MODFLAGS += -include include/linux/modversions.h
 endif
 
 .PHONY: modules
@@ -533,7 +535,7 @@
 
 .PHONY: $(patsubst %, _modinst_%, $(SUBDIRS))
 $(patsubst %, _modinst_%, $(SUBDIRS)) :
-	@$(MAKE) -C $(patsubst _modinst_%, %, $@) modules_install
+	+@$(call descend,$(patsubst _modinst_%,%,$@),modules_install)
 
 else # CONFIG_MODULES
 
@@ -597,7 +599,7 @@
 	rpm -ta $(TOPDIR)/../$(KERNELPATH).tar.gz ; \
 	rm $(TOPDIR)/../$(KERNELPATH).tar.gz
 
-else # ifeq ($(filter $(noconfig_targets),$(MAKECMDGOALS)),)
+else # ifdef include-config
 
 ifeq ($(filter-out $(noconfig_targets),$(MAKECMDGOALS)),)
 
@@ -631,11 +633,11 @@
 	make_with_config
 
 xconfig:
-	@$(MAKE) -C scripts kconfig.tk
+	+@$(call descend,scripts,scripts/kconfig.tk)
 	wish -f scripts/kconfig.tk
 
 menuconfig:
-	@$(MAKE) -C scripts lxdialog
+	+@$(call descend,scripts,lxdialog)
 	$(CONFIG_SHELL) $(src)/scripts/Menuconfig arch/$(ARCH)/config.in
 
 config:
@@ -704,7 +706,7 @@
 	.version .config* config.in config.old \
 	.menuconfig.log \
 	include/asm \
-	.hdepend $(TOPDIR)/include/linux/modversions.h \
+	.hdepend include/linux/modversions.h \
 	tags TAGS kernel.spec \
 	.tmp*
 
@@ -712,11 +714,7 @@
 MRPROPER_DIRS += \
 	.tmp_export-objs \
 	include/config \
-	$(TOPDIR)/include/linux/modules
-
-#	That's our way to know about arch specific cleanup.
-
-include arch/$(ARCH)/Makefile
+	include/linux/modules
 
 clean:	archclean
 	@echo 'Cleaning up'
@@ -725,7 +723,7 @@
 		-name .\*.tmp -o -name .\*.d \) -type f -print \
 		| grep -v lxdialog/ | xargs rm -f
 	@rm -f $(CLEAN_FILES)
-	@$(MAKE) -f Documentation/DocBook/Makefile clean
+	+@$(call descend,Documentation/DocBook,clean)
 
 mrproper: clean archmrproper
 	@echo 'Making mrproper'
@@ -734,8 +732,8 @@
 		-type f -print | xargs rm -f
 	@rm -rf $(MRPROPER_DIRS)
 	@rm -f $(MRPROPER_FILES)
-	@$(MAKE) -C scripts mrproper
-	@$(MAKE) -f Documentation/DocBook/Makefile mrproper
+	+@$(call descend,scripts,mrproper)
+	+@$(call descend,Documentation/DocBook,mrproper)
 
 distclean: mrproper
 	@echo 'Making distclean'
@@ -822,7 +820,7 @@
 # Documentation targets
 # ---------------------------------------------------------------------------
 sgmldocs psdocs pdfdocs htmldocs: scripts
-	@$(MAKE) -f Documentation/DocBook/Makefile $@
+	+@$(call descend,Documentation/DocBook,$@)
 
 # Scripts to check various things for consistency
 # ---------------------------------------------------------------------------
@@ -852,10 +850,11 @@
 	$(MAKE) $@
 
 endif # ifeq ($(filter-out $(noconfig_targets),$(MAKECMDGOALS)),)
-endif # ifeq ($(filter $(noconfig_targets),$(MAKECMDGOALS)),)
+endif # ifdef include-config
 
 # FIXME Should go into a make.lib or something 
 # ===========================================================================
+
 echo_target = $@
 
 a_flags = -Wp,-MD,$(depfile) $(AFLAGS) $(NOSTDINC_FLAGS) \
@@ -909,5 +908,12 @@
 	fi
 endef
 
+#	$(call descend,<dir>,<target>)
+#	Recursively call a sub-make in <dir> with target <target> 
+
+ifeq ($(KBUILD_VERBOSE),1)
+descend = echo '$(MAKE) -f $(1)/Makefile $(2)';
+endif
+descend += $(MAKE) -f $(1)/Makefile obj=$(1) $(2)
 
 FORCE:
diff -Nru a/Rules.make b/Rules.make
--- a/Rules.make	Mon Oct  7 11:25:32 2002
+++ b/Rules.make	Mon Oct  7 11:25:32 2002
@@ -8,23 +8,38 @@
 empty   :=
 space   := $(empty) $(empty)
 
-# Figure out paths
+# Some bug traps
 # ---------------------------------------------------------------------------
-# Find the path relative to the toplevel dir, $(RELDIR), and express
-# the toplevel dir as a relative path from this dir, $(TOPDIR_REL)
 
-ifeq ($(findstring $(TOPDIR),$(CURDIR)),)
-  # Can only happen when something is built out of tree
-  RELDIR := $(CURDIR)
-  TOPDIR_REL := $(TOPDIR)
-else
-  RELDIR := $(subst $(TOPDIR)/,,$(CURDIR))
-  TOPDIR_REL := $(subst $(space),,$(foreach d,$(subst /, ,$(RELDIR)),../))
+ifdef O_TARGET
+$(error kbuild: $(obj)/Makefile - Usage of O_TARGET := $(O_TARGET) is obsolete in 2.5. Please fix!)
+endif
+
+ifdef L_TARGET
+ifneq ($(L_TARGET),lib.a)
+$(warning kbuild: $(obj)/Makefile - L_TARGET := $(L_TARGET) target shall be renamed to lib.a. Please fix!)
+endif
+endif
+
+ifdef list-multi
+$(warning kbuild: $(obj)/Makefile - list-multi := $(list-multi) is obsolete in 2.5. Please fix!)
 endif
 
 # Some paths for the Makefiles to use
 # ---------------------------------------------------------------------------
 
+# FIXME. For now, we leave it possible to use make -C or make -f
+# to do work in subdirs.
+
+ifndef obj
+obj = .
+CFLAGS := $(patsubst -I%,-I$(TOPDIR)/%,$(patsubst -I$(TOPDIR)/%,-I%,$(CFLAGS)))
+AFLAGS := $(patsubst -I%,-I$(TOPDIR)/%,$(patsubst -I$(TOPDIR)/%,-I%,$(AFLAGS)))
+endif
+
+# For use in the quiet output
+echo_target = $@
+
 # Usage:
 #
 # $(obj)/target.o                     : target.o in the build dir
@@ -41,12 +56,7 @@
 # We don't support separate source / object yet, so these are just
 # placeholders for now
 
-obj := .
-src := .
-
-# For use in the quiet output
-
-echo_target = $(RELDIR)/$@
+src := $(obj)
 
 # Figure out what we need to build from the various variables
 # ===========================================================================
@@ -84,16 +94,8 @@
 __obj-m = $(filter-out export.o,$(obj-m))
 
 # if $(foo-objs) exists, foo.o is a composite object 
-__multi-used-y := $(sort $(foreach m,$(__obj-y), $(if $($(m:.o=-objs)), $(m))))
-__multi-used-m := $(sort $(foreach m,$(__obj-m), $(if $($(m:.o=-objs)), $(m))))
-
-# FIXME: Rip this out later
-# Backwards compatibility: if a composite object is listed in
-# $(list-multi), skip it here, since the Makefile will have an explicit
-# link rule for it
-
-multi-used-y := $(filter-out $(list-multi),$(__multi-used-y))
-multi-used-m := $(filter-out $(list-multi),$(__multi-used-m))
+multi-used-y := $(sort $(foreach m,$(__obj-y), $(if $($(m:.o=-objs)), $(m))))
+multi-used-m := $(sort $(foreach m,$(__obj-m), $(if $($(m:.o=-objs)), $(m))))
 
 # Build list of the parts of our composite objects, our composite
 # objects depend on those (obviously)
@@ -111,6 +113,21 @@
 # Only build module versions for files which are selected to be built
 export-objs := $(filter $(export-objs),$(real-objs-y) $(real-objs-m))
 
+# Add subdir path
+
+EXTRA_TARGETS	:= $(addprefix $(obj)/,$(EXTRA_TARGETS))
+obj-y		:= $(addprefix $(obj)/,$(obj-y))
+obj-m		:= $(addprefix $(obj)/,$(obj-m))
+export-objs	:= $(addprefix $(obj)/,$(export-objs))
+subdir-obj-y	:= $(addprefix $(obj)/,$(subdir-obj-y))
+real-objs-y	:= $(addprefix $(obj)/,$(real-objs-y))
+real-objs-m	:= $(addprefix $(obj)/,$(real-objs-m))
+multi-used-y	:= $(addprefix $(obj)/,$(multi-used-y))
+multi-used-m	:= $(addprefix $(obj)/,$(multi-used-m))
+multi-objs-y	:= $(addprefix $(obj)/,$(multi-objs-y))
+multi-objs-m	:= $(addprefix $(obj)/,$(multi-objs-m))
+subdir-ym	:= $(addprefix $(obj)/,$(subdir-ym))
+
 # The temporary file to save gcc -MD generated dependencies must not
 # contain a comma
 depfile = $(subst $(comma),_,$(@D)/.$(@F).d)
@@ -136,15 +153,15 @@
 # If we don't export any symbols in this dir, just descend
 # ---------------------------------------------------------------------------
 
-fastdep: sub_dirs
-	@echo -n
+fastdep: $(subdir-ym)
+	@/bin/true
 
 else
 
 # This sets version suffixes on exported symbols
 # ---------------------------------------------------------------------------
 
-MODVERDIR := $(TOPDIR)/include/linux/modules/$(RELDIR)
+MODVERDIR := include/linux/modules/
 
 #
 # Added the SMP separator to stop module accidents between uniprocessor
@@ -159,7 +176,7 @@
 
 #	Don't include modversions.h, we're just about to generate it here.
 
-CFLAGS_MODULE := $(filter-out -include $(HPATH)/linux/modversions.h,$(CFLAGS_MODULE))
+CFLAGS_MODULE := $(filter-out -include linux/modversions.h,$(CFLAGS_MODULE))
 
 $(addprefix $(MODVERDIR)/,$(real-objs-y:.o=.ver)): modkern_cflags := $(CFLAGS_KERNEL)
 $(addprefix $(MODVERDIR)/,$(real-objs-m:.o=.ver)): modkern_cflags := $(CFLAGS_MODULE)
@@ -174,7 +191,7 @@
 # files (fix-dep filters them), so touch modversions.h if any of the .ver
 # files changes
 
-quiet_cmd_cc_ver_c = MKVER   include/linux/modules/$(RELDIR)/$*.ver
+quiet_cmd_cc_ver_c = MKVER   include/linux/modules/$*.ver
 cmd_cc_ver_c = $(CPP) $(c_flags) $< | $(GENKSYMS) $(genksyms_smp_prefix) \
 		 -k $(VERSION).$(PATCHLEVEL).$(SUBLEVEL) > $@.tmp
 
@@ -206,9 +223,9 @@
 
 targets := $(addprefix $(MODVERDIR)/,$(export-objs:.o=.ver))
 
-fastdep: $(targets) sub_dirs
-	@mkdir -p $(TOPDIR)/.tmp_export-objs/modules/$(RELDIR)
-	@touch $(addprefix $(TOPDIR)/.tmp_export-objs/modules/$(RELDIR)/,$(export-objs:.o=.ver))
+fastdep: $(targets) $(subdir-ym)
+	@mkdir -p $(dir $(addprefix .tmp_export-objs/modules/,$(export-objs:.o=.ver)))
+	@touch $(addprefix .tmp_export-objs/modules/,$(export-objs:.o=.ver))
 
 endif # export-objs 
 
@@ -219,15 +236,17 @@
 # Installing modules
 # ==========================================================================
 
+quiet_cmd_modules_install = INSTALL $(obj-m)
+cmd_modules_install = mkdir -p $(MODLIB)/kernel/$(obj); \
+		      cp $(obj-m) $(MODLIB)/kernel/$(obj)
+
 .PHONY: modules_install
 
-modules_install: sub_dirs
+modules_install: $(subdir-ym)
 ifneq ($(obj-m),)
-	@echo Installing modules in $(MODLIB)/kernel/$(RELDIR)
-	@mkdir -p $(MODLIB)/kernel/$(RELDIR)
-	@cp $(obj-m) $(MODLIB)/kernel/$(RELDIR)
+	$(call cmd,modules_install)
 else
-	@echo -n
+	@/bin/true
 endif
 
 else # ! modules_install
@@ -241,15 +260,18 @@
 
 ifndef O_TARGET
 ifndef L_TARGET
-O_TARGET := built-in.o
+O_TARGET := $(obj)/built-in.o
+endif
 endif
+
+ifdef L_TARGET
+L_TARGET := $(obj)/$(L_TARGET)
 endif
 
-#	The echo suppresses the "Nothing to be done for first_rule"
 first_rule: $(if $(KBUILD_BUILTIN),$(O_TARGET) $(L_TARGET) $(EXTRA_TARGETS)) \
 	    $(if $(KBUILD_MODULES),$(obj-m)) \
-	    sub_dirs
-	@echo -n
+	    $(subdir-ym)
+	@/bin/true
 
 # Compile C sources (.c)
 # ---------------------------------------------------------------------------
@@ -324,7 +346,7 @@
 # ---------------------------------------------------------------------------
 
 # To build objects in subdirs, we need to descend into the directories
-$(sort $(subdir-obj-y)): sub_dirs ;
+$(sort $(subdir-obj-y)): $(subdir-ym) ;
 
 #
 # Rule to compile a set of .o files into one .o file
@@ -360,7 +382,7 @@
 #
 
 quiet_cmd_link_multi = LD      $(echo_target)
-cmd_link_multi = $(LD) $(LDFLAGS) $(EXTRA_LDFLAGS) -r -o $@ $(filter $($(basename $@)-objs),$^)
+cmd_link_multi = $(LD) $(LDFLAGS) $(EXTRA_LDFLAGS) -r -o $@ $(filter $(addprefix $(obj)/,$($(subst $(obj)/,,$(@:.o=-objs)))),$^)
 
 # We would rather have a list of rules like
 # 	foo.o: $(foo-objs)
@@ -380,6 +402,10 @@
 host-progs-single     := $(foreach m,$(host-progs),$(if $($(m)-objs),,$(m)))
 host-progs-multi      := $(foreach m,$(host-progs),$(if $($(m)-objs),$(m)))
 host-progs-multi-objs := $(foreach m,$(host-progs-multi),$($(m)-objs))
+host-progs	      := $(addprefix $(obj)/,$(host-progs))
+host-progs-single     := $(addprefix $(obj)/,$(host-progs-single))
+host-progs-multi      := $(addprefix $(obj)/,$(host-progs-multi))
+host-progs-multi-objs := $(addprefix $(obj)/,$(host-progs-multi-objs))
 
 quiet_cmd_host_cc__c  = HOSTCC  $(echo_target)
 cmd_host_cc__c        = $(HOSTCC) -Wp,-MD,$(depfile) \
@@ -397,7 +423,7 @@
 	$(call if_changed_dep,host_cc_o_c)
 
 quiet_cmd_host_cc__o  = HOSTLD  $(echo_target)
-cmd_host_cc__o        = $(HOSTCC) $(HOSTLDFLAGS) -o $@ $($@-objs) \
+cmd_host_cc__o        = $(HOSTCC) $(HOSTLDFLAGS) -o $@ $(addprefix $(obj)/,$($(subst $(obj)/,,$@)-objs)) \
 			$(HOST_LOADLIBES)
 
 $(host-progs-multi): %: $(host-progs-multi-objs) FORCE
@@ -432,7 +458,7 @@
 # ---------------------------------------------------------------------------
 
 quiet_cmd_ld = LD      $(echo_target)
-cmd_ld = $(LD) $(LDFLAGS) $(EXTRA_LDFLAGS) $(LDFLAGS_$@) \
+cmd_ld = $(LD) $(LDFLAGS) $(EXTRA_LDFLAGS) $(LDFLAGS_$(@F)) \
 	       $(filter-out FORCE,$^) -o $@ 
 
 # Objcopy
@@ -454,12 +480,10 @@
 # Descending
 # ---------------------------------------------------------------------------
 
-.PHONY: sub_dirs $(subdir-ym)
-
-sub_dirs: $(subdir-ym)
+.PHONY: $(subdir-ym)
 
 $(subdir-ym):
-	@$(MAKE) -C $@ $(MAKECMDGOALS)
+	+@$(call descend,$@,$(MAKECMDGOALS))
 
 # Add FORCE to the prequisites of a target to force it to be always rebuilt.
 # ---------------------------------------------------------------------------
@@ -517,13 +541,6 @@
   include $(cmd_files)
 endif
 
-# Emacs compile mode works best with relative paths to find files (OK
-# if verbose, as it tracks the make[1] entries and exits, etc.)
-
-ifneq ($(KBUILD_VERBOSE),1)
-  filter-output = 2>&1 | sed 's \(^[^/][A-Za-z0-9_./-]*:[ 0-9]\) $(RELDIR)/\1 '
-endif
-
 # function to only execute the passed command if necessary
 
 if_changed = $(if $(strip $? \
@@ -543,7 +560,7 @@
 			  $(filter-out $(cmd_$@),$(cmd_$(1)))),\
 	@set -e; \
 	$(if $($(quiet)cmd_$(1)),echo '  $($(quiet)cmd_$(1))';) \
-	$(cmd_$(1)) $(filter-output); \
+	$(cmd_$(1)); \
 	$(TOPDIR)/scripts/fixdep $(depfile) $@ $(TOPDIR) '$(cmd_$(1))' > $(@D)/.$(@F).tmp; \
 	rm -f $(depfile); \
 	mv -f $(@D)/.$(@F).tmp $(@D)/.$(@F).cmd)
@@ -581,3 +598,10 @@
         $(2)
 endef
 
+#	$(call descend,<dir>,<target>)
+#	Recursively call a sub-make in <dir> with target <target> 
+
+ifeq ($(KBUILD_VERBOSE),1)
+descend = echo '$(MAKE) -f $(1)/Makefile $(2)';
+endif
+descend += $(MAKE) -f $(1)/Makefile obj=$(1) $(2)
diff -Nru a/arch/alpha/Makefile b/arch/alpha/Makefile
--- a/arch/alpha/Makefile	Mon Oct  7 11:25:36 2002
+++ b/arch/alpha/Makefile	Mon Oct  7 11:25:36 2002
@@ -95,6 +95,8 @@
 core-$(CONFIG_MATHEMU)  += arch/alpha/math-emu/
 libs-y			+= arch/alpha/lib/
 
+export libs-y
+
 MAKEBOOT = $(MAKE) -C arch/alpha/boot
 
 rawboot:
diff -Nru a/arch/alpha/boot/Makefile b/arch/alpha/boot/Makefile
--- a/arch/alpha/boot/Makefile	Mon Oct  7 11:25:26 2002
+++ b/arch/alpha/boot/Makefile	Mon Oct  7 11:25:26 2002
@@ -20,7 +20,7 @@
 TARGETS = vmlinux.gz tools/objstrip # also needed by aboot & milo
 VMLINUX = $(TOPDIR)/vmlinux
 OBJSTRIP = tools/objstrip
-LIBS := $(patsubst lib/%,$(TOPDIR)/lib/%,$(LIBS))
+LIBS := $(addprefix $(TOPDIR)/,$(libs-y))
 
 all:	$(TARGETS)
 	@echo Ready to install kernel in $(shell pwd)/vmlinux.gz
diff -Nru a/arch/alpha/kernel/signal.c b/arch/alpha/kernel/signal.c
--- a/arch/alpha/kernel/signal.c	Mon Oct  7 11:25:35 2002
+++ b/arch/alpha/kernel/signal.c	Mon Oct  7 11:25:35 2002
@@ -63,7 +63,7 @@
 		unsigned long block, unblock;
 
 		newmask &= _BLOCKABLE;
-		spin_lock_irq(&current->sigmask_lock);
+		spin_lock_irq(&current->sig->siglock);
 		oldmask = current->blocked.sig[0];
 
 		unblock = oldmask & ~newmask;
@@ -76,7 +76,7 @@
 			sigemptyset(&current->blocked);
 		current->blocked.sig[0] = newmask;
 		recalc_sigpending();
-		spin_unlock_irq(&current->sigmask_lock);
+		spin_unlock_irq(&current->sig->siglock);
 
 		(&regs)->r0 = 0;		/* special no error return */
 	}
@@ -150,11 +150,11 @@
 	sigset_t oldset;
 
 	mask &= _BLOCKABLE;
-	spin_lock_irq(&current->sigmask_lock);
+	spin_lock_irq(&current->sig->siglock);
 	oldset = current->blocked;
 	siginitset(&current->blocked, mask);
 	recalc_sigpending();
-	spin_unlock_irq(&current->sigmask_lock);
+	spin_unlock_irq(&current->sig->siglock);
 
 	while (1) {
 		current->state = TASK_INTERRUPTIBLE;
@@ -177,11 +177,11 @@
 		return -EFAULT;
 
 	sigdelsetmask(&set, ~_BLOCKABLE);
-	spin_lock_irq(&current->sigmask_lock);
+	spin_lock_irq(&current->sig->siglock);
 	oldset = current->blocked;
 	current->blocked = set;
 	recalc_sigpending();
-	spin_unlock_irq(&current->sigmask_lock);
+	spin_unlock_irq(&current->sig->siglock);
 
 	while (1) {
 		current->state = TASK_INTERRUPTIBLE;
@@ -284,10 +284,10 @@
 		goto give_sigsegv;
 
 	sigdelsetmask(&set, ~_BLOCKABLE);
-	spin_lock_irq(&current->sigmask_lock);
+	spin_lock_irq(&current->sig->siglock);
 	current->blocked = set;
 	recalc_sigpending();
-	spin_unlock_irq(&current->sigmask_lock);
+	spin_unlock_irq(&current->sig->siglock);
 
 	if (restore_sigcontext(&frame->sc, regs, sw))
 		goto give_sigsegv;
@@ -315,10 +315,10 @@
 		goto give_sigsegv;
 
 	sigdelsetmask(&set, ~_BLOCKABLE);
-	spin_lock_irq(&current->sigmask_lock);
+	spin_lock_irq(&current->sig->siglock);
 	current->blocked = set;
 	recalc_sigpending();
-	spin_unlock_irq(&current->sigmask_lock);
+	spin_unlock_irq(&current->sig->siglock);
 
 	if (restore_sigcontext(&frame->uc.uc_mcontext, regs, sw))
 		goto give_sigsegv;
@@ -546,11 +546,11 @@
 		ka->sa.sa_handler = SIG_DFL;
 
 	if (!(ka->sa.sa_flags & SA_NODEFER)) {
-		spin_lock_irq(&current->sigmask_lock);
+		spin_lock_irq(&current->sig->siglock);
 		sigorsets(&current->blocked,&current->blocked,&ka->sa.sa_mask);
 		sigaddset(&current->blocked,sig);
 		recalc_sigpending();
-		spin_unlock_irq(&current->sigmask_lock);
+		spin_unlock_irq(&current->sig->siglock);
 	}
 }
 
diff -Nru a/arch/alpha/lib/Makefile b/arch/alpha/lib/Makefile
--- a/arch/alpha/lib/Makefile	Mon Oct  7 11:25:39 2002
+++ b/arch/alpha/lib/Makefile	Mon Oct  7 11:25:39 2002
@@ -2,6 +2,7 @@
 # Makefile for alpha-specific library files..
 #
 
+EXTRA_AFLAGS := $(CFLAGS)
 L_TARGET := lib.a
 
 # Many of these routines have implementations tuned for ev6.
diff -Nru a/arch/alpha/lib/ev6-stxncpy.S b/arch/alpha/lib/ev6-stxncpy.S
--- a/arch/alpha/lib/ev6-stxncpy.S	Mon Oct  7 11:25:36 2002
+++ b/arch/alpha/lib/ev6-stxncpy.S	Mon Oct  7 11:25:36 2002
@@ -205,35 +205,30 @@
 
 	cmpbge	zero, t6, t8	# E :
 	beq	a2, $u_eocfin	# U :
-	nop
+	lda	t6, -1		# E :
 	nop
 
 	bne	t8, $u_final	# U :
-	lda	t6, -1		# E : mask out the bits we have
-	mskql	t6, a1, t6	# U :   already seen (stall)
+	mskql	t6, a1, t6	# U : mask out bits already seen
 	stq_u	t0, 0(a0)	# L : store first output word
+	or      t6, t2, t2	# E : (stall)
 
-	or      t6, t2, t2		# E :
-	cmpbge	zero, t2, t8		# E : find nulls in second partial (stall)
-	addq	a0, 8, a0		# E :
-	subq	a2, 1, a2		# E :
-
+	cmpbge	zero, t2, t8	# E : find nulls in second partial
+	addq	a0, 8, a0	# E :
+	subq	a2, 1, a2	# E :
 	bne	t8, $u_late_head_exit	# U :
+
 	/* Finally, we've got all the stupid leading edge cases taken care
 	   of and we can set up to enter the main loop.  */
 	extql	t2, a1, t1	# U : position hi-bits of lo word
+	beq	a2, $u_eoc	# U :
 	ldq_u	t2, 8(a1)	# L : read next high-order source word
 	addq	a1, 8, a1	# E :
 
-	cmpbge	zero, t2, t8	# E : (stall)
-	beq	a2, $u_eoc	# U :
-	nop
-	nop
-
-	bne	t8, $u_eos	# e1    :
-	nop
-	nop
+	extqh	t2, a1, t0	# U : position lo-bits of hi word (stall)
+	cmpbge	zero, t2, t8	# E :
 	nop
+	bne	t8, $u_eos	# U :
 
 	/* Unaligned copy main loop.  In order to avoid reading too much,
 	   the loop is structured to detect zeros in aligned source words.
@@ -243,6 +238,7 @@
 	   to run as fast as possible.
 
 	   On entry to this basic block:
+	   t0 == the shifted low-order bits from the current source word
 	   t1 == the shifted high-order bits from the previous source word
 	   t2 == the unshifted current source word
 
@@ -250,25 +246,20 @@
 
 	.align 4
 $u_loop:
-	extqh	t2, a1, t0	# U : extract high bits for current word
-	addq	a1, 8, a1	# E :
-	extql	t2, a1, t3	# U : extract low bits for next time
+	or	t0, t1, t0	# E : current dst word now complete
+	subq	a2, 1, a2	# E : decrement word count
+	extql	t2, a1, t1	# U : extract low bits for next time
 	addq	a0, 8, a0	# E :
 
-	or	t0, t1, t0	# E : current dst word now complete
+	stq_u	t0, -8(a0)	# U : save the current word
+	beq	a2, $u_eoc	# U :
 	ldq_u	t2, 0(a1)	# U : Latency=3 load high word for next time
-	stq_u	t0, -8(a0)	# U : save the current word (stall)
-	mov	t3, t1		# E :
+	addq	a1, 8, a1	# E :
 
-	subq	a2, 1, a2	# E :
-	cmpbge	zero, t2, t8	# E : test new word for eos (2 cycle stall for data)
-	beq	a2, $u_eoc	# U : (stall)
+	extqh	t2, a1, t0	# U : extract low bits (2 cycle stall)
+	cmpbge	zero, t2, t8	# E : test new word for eos
 	nop
-
 	beq	t8, $u_loop	# U :
-	nop
-	nop
-	nop
 
 	/* We've found a zero somewhere in the source word we just read.
 	   If it resides in the lower half, we have one (probably partial)
@@ -276,11 +267,12 @@
 	   have one full and one partial word left to write out.
 
 	   On entry to this basic block:
+	   t0 == the shifted low-order bits from the current source word
 	   t1 == the shifted high-order bits from the previous source word
 	   t2 == the unshifted current source word.  */
 $u_eos:
-	extqh	t2, a1, t0	# U :
-	or	t0, t1, t0	# E : first (partial) source word complete (stall)
+	or	t0, t1, t0	# E : first (partial) source word complete
+	nop
 	cmpbge	zero, t0, t8	# E : is the null in this first bit? (stall)
 	bne	t8, $u_final	# U : (stall)
 
@@ -318,17 +310,26 @@
 1:	stq_u	t0, 0(a0)	# L :
 	ret	(t9)		# L0 : Latency=3
 
-$u_eoc:				# end-of-count
-	extqh	t2, a1, t0	# U :
-	or	t0, t1, t0	# E : (stall)
-	cmpbge	zero, t0, t8	# E : (stall)
+	  /* Got to end-of-count before end of string.  
+	     On entry to this basic block:
+	     t1 == the shifted high-order bits from the previous source word  */
+$u_eoc:
+	and	a1, 7, t6	# E : avoid final load if possible
+	sll	t10, t6, t6	# U : (stall)
+	and	t6, 0xff, t6	# E : (stall)
+	bne	t6, 1f		# U : (stall)
+
+	ldq_u	t2, 8(a1)	# L : load final src word
 	nop
+	extqh	t2, a1, t0	# U : extract low bits for last word (stall)
+	or	t1, t0, t1	# E : (stall)
+
+	cmpbge	zero, t1, t8	# E :
+	mov	t1, t0		# E :
 
 $u_eocfin:			# end-of-count, final word
 	or	t10, t8, t8	# E :
 	br	$u_final	# L0 : Latency=3
-	nop
-	nop
 
 	/* Unaligned copy entry point.  */
 	.align 4
@@ -349,9 +350,7 @@
 	mskql	t6, a0, t6	# U :
 	nop
 	nop
-	nop
-1:
-	subq	a1, t4, a1	# E : sub dest misalignment from src addr
+1:	subq	a1, t4, a1	# E : sub dest misalignment from src addr
 
 	/* If source misalignment is larger than dest misalignment, we need
 	   extra startup checks to avoid SEGV.  */
@@ -396,4 +395,3 @@
 	nop
 
 	.end __stxncpy
-
diff -Nru a/arch/alpha/lib/stxncpy.S b/arch/alpha/lib/stxncpy.S
--- a/arch/alpha/lib/stxncpy.S	Mon Oct  7 11:25:39 2002
+++ b/arch/alpha/lib/stxncpy.S	Mon Oct  7 11:25:39 2002
@@ -164,26 +164,29 @@
 	or	t0, t6, t6	# e1    : mask original data for zero test
 	cmpbge	zero, t6, t8	# e0    :
 	beq	a2, $u_eocfin	# .. e1 :
-	bne	t8, $u_final	# e1    :
+	lda	t6, -1		# e0    :
+	bne	t8, $u_final	# .. e1 :
 
-	lda	t6, -1			# e1    : mask out the bits we have
-	mskql	t6, a1, t6		# e0    :   already seen
-	stq_u	t0, 0(a0)		# e0    : store first output word
-	or      t6, t2, t2		# .. e1 :
-	cmpbge	zero, t2, t8		# e0    : find nulls in second partial
-	addq	a0, 8, a0		# .. e1 :
-	subq	a2, 1, a2		# e0    :
+	mskql	t6, a1, t6	# e0    : mask out bits already seen
+	nop			# .. e1 :
+	stq_u	t0, 0(a0)	# e0    : store first output word
+	or      t6, t2, t2	# .. e1 :
+	cmpbge	zero, t2, t8	# e0    : find nulls in second partial
+	addq	a0, 8, a0	# .. e1 :
+	subq	a2, 1, a2	# e0    :
 	bne	t8, $u_late_head_exit	# .. e1 :
 
 	/* Finally, we've got all the stupid leading edge cases taken care
 	   of and we can set up to enter the main loop.  */
 
 	extql	t2, a1, t1	# e0    : position hi-bits of lo word
-	ldq_u	t2, 8(a1)	# .. e1 : read next high-order source word
-	addq	a1, 8, a1	# e0    :
-	cmpbge	zero, t2, t8	# e1 (stall)
-	beq	a2, $u_eoc	# e1    :
-	bne	t8, $u_eos	# e1    :
+	beq	a2, $u_eoc	# .. e1 :
+	ldq_u	t2, 8(a1)	# e0    : read next high-order source word
+	addq	a1, 8, a1	# .. e1 :
+	extqh	t2, a1, t0	# e0    : position lo-bits of hi word (stall)
+	cmpbge	zero, t2, t8	# .. e1 :
+	nop			# e0    :
+	bne	t8, $u_eos	# .. e1 :
 
 	/* Unaligned copy main loop.  In order to avoid reading too much,
 	   the loop is structured to detect zeros in aligned source words.
@@ -193,6 +196,7 @@
 	   to run as fast as possible.
 
 	   On entry to this basic block:
+	   t0 == the shifted low-order bits from the current source word
 	   t1 == the shifted high-order bits from the previous source word
 	   t2 == the unshifted current source word
 
@@ -200,18 +204,18 @@
 
 	.align 3
 $u_loop:
-	extqh	t2, a1, t0	# e0    : extract high bits for current word
-	addq	a1, 8, a1	# .. e1 :
-	extql	t2, a1, t3	# e0    : extract low bits for next time
-	addq	a0, 8, a0	# .. e1 :
 	or	t0, t1, t0	# e0    : current dst word now complete
-	ldq_u	t2, 0(a1)	# .. e1 : load high word for next time
-	stq_u	t0, -8(a0)	# e0    : save the current word
-	mov	t3, t1		# .. e1 :
-	subq	a2, 1, a2	# e0    :
-	cmpbge	zero, t2, t8	# .. e1 : test new word for eos
-	beq	a2, $u_eoc	# e1    :
-	beq	t8, $u_loop	# e1    :
+	subq	a2, 1, a2	# .. e1 : decrement word count
+	stq_u	t0, 0(a0)	# e0    : save the current word
+	addq	a0, 8, a0	# .. e1 :
+	extql	t2, a1, t1	# e0    : extract high bits for next time
+	beq	a2, $u_eoc	# .. e1 :
+	ldq_u	t2, 0(a1)	# e0    : load high word for next time
+	addq	a1, 8, a1	# .. e1 :
+	nop			# e0    :
+	cmpbge	zero, t2, t8	# e1    : test new word for eos (stall)
+	extqh	t2, a1, t0	# e0    : extract low bits for current word
+	beq	t8, $u_loop	# .. e1 :
 
 	/* We've found a zero somewhere in the source word we just read.
 	   If it resides in the lower half, we have one (probably partial)
@@ -219,12 +223,12 @@
 	   have one full and one partial word left to write out.
 
 	   On entry to this basic block:
+	   t0 == the shifted low-order bits from the current source word
 	   t1 == the shifted high-order bits from the previous source word
 	   t2 == the unshifted current source word.  */
 $u_eos:
-	extqh	t2, a1, t0	# e0    :
-	or	t0, t1, t0	# e1    : first (partial) source word complete
-
+	or	t0, t1, t0	# e0    : first (partial) source word complete
+	nop			# .. e1 :
 	cmpbge	zero, t0, t8	# e0    : is the null in this first bit?
 	bne	t8, $u_final	# .. e1 (zdb)
 
@@ -260,10 +264,22 @@
 1:	stq_u	t0, 0(a0)	# e0    :
 	ret	(t9)		# .. e1 :
 
-$u_eoc:				# end-of-count
-	extqh	t2, a1, t0
-	or	t0, t1, t0
-	cmpbge	zero, t0, t8
+	/* Got to end-of-count before end of string.  
+	   On entry to this basic block:
+	   t1 == the shifted high-order bits from the previous source word  */
+$u_eoc:
+	and	a1, 7, t6	# e1    :
+	sll	t12, t6, t6	# e0    :
+	and	t6, 0xff, t6	# e0    :
+	bne	t6, 1f		# .. e1 :
+
+	ldq_u	t2, 8(a1)	# e0    : load final src word
+	nop			# .. e1 :
+	extqh	t2, a1, t0	# e0    : extract low bits for last word
+	or	t1, t0, t1	# e1    :
+
+1:	cmpbge	zero, t1, t7
+	mov	t1, t0
 
 $u_eocfin:			# end-of-count, final word
 	or	t10, t8, t8
diff -Nru a/arch/i386/Config.help b/arch/i386/Config.help
--- a/arch/i386/Config.help	Mon Oct  7 11:25:25 2002
+++ b/arch/i386/Config.help	Mon Oct  7 11:25:25 2002
@@ -1058,3 +1058,12 @@
   absence of features.
 
   For more information take a look at Documentation/swsusp.txt.
+
+CONFIG_SCx200
+  This provides basic support for the National Semiconductor SCx200 
+  processor.  Right now this is just a driver for the GPIO pins.
+
+  If you don't know what to do here, say N.
+
+  This support is also available as a module.  If compiled as a
+  module, it will be called scx200.o.
diff -Nru a/arch/i386/Makefile b/arch/i386/Makefile
--- a/arch/i386/Makefile	Mon Oct  7 11:25:31 2002
+++ b/arch/i386/Makefile	Mon Oct  7 11:25:31 2002
@@ -25,65 +25,23 @@
 # prevent gcc from keeping the stack 16 byte aligned
 CFLAGS += $(shell if $(CC) -mpreferred-stack-boundary=2 -S -o /dev/null -xc /dev/null >/dev/null 2>&1; then echo "-mpreferred-stack-boundary=2"; fi)
 
-ifdef CONFIG_M386
-CFLAGS += -march=i386
-endif
-
-ifdef CONFIG_M486
-CFLAGS += -march=i486
-endif
-
-ifdef CONFIG_M586
-CFLAGS += -march=i586
-endif
-
-ifdef CONFIG_M586TSC
-CFLAGS += -march=i586
-endif
-
-ifdef CONFIG_M586MMX
-CFLAGS += -march=i586
-endif
-
-ifdef CONFIG_M686
-CFLAGS += -march=i686
-endif
-
-ifdef CONFIG_MPENTIUMIII
-CFLAGS += -march=i686
-endif
-
-ifdef CONFIG_MPENTIUM4
-CFLAGS += -march=i686
-endif
+cflags-$(CONFIG_M386)		+= -march=i386
+cflags-$(CONFIG_M486)		+= -march=i486
+cflags-$(CONFIG_M586)		+= -march=i586
+cflags-$(CONFIG_M586TSC)	+= -march=i586
+cflags-$(CONFIG_M586MMX)	+= -march=i586
+cflags-$(CONFIG_M686)		+= -march=i686
+cflags-$(CONFIG_MPENTIUMIII)	+= -march=i686
+cflags-$(CONFIG_MPENTIUM4)	+= -march=i686
+cflags-$(CONFIG_MK6)		+= $(shell if $(CC) -march=k6 -S -o /dev/null -xc /dev/null >/dev/null 2>&1; then echo "-march=k6"; else echo "-march=i586"; fi)
+cflags-$(CONFIG_MK7)		+= $(shell if $(CC) -march=athlon -S -o /dev/null -xc /dev/null >/dev/null 2>&1; then echo "-march=athlon"; else echo "-march=i686 -malign-functions=4"; fi) 
+cflags-$(CONFIG_MCRUSOE)	+= -march=i686 -malign-functions=0 -malign-jumps=0 -malign-loops=0
+cflags-$(CONFIG_MWINCHIPC6)	+= -march=i586
+cflags-$(CONFIG_MWINCHIP2)	+= -march=i586
+cflags-$(CONFIG_MWINCHIP3D)	+= -march=i586
+cflags-$(CONFIG_MCYRIXIII)	+= -march=i586
 
-ifdef CONFIG_MK6
-CFLAGS += $(shell if $(CC) -march=k6 -S -o /dev/null -xc /dev/null >/dev/null 2>&1; then echo "-march=k6"; else echo "-march=i586"; fi)
-endif
-
-ifdef CONFIG_MK7
-CFLAGS += $(shell if $(CC) -march=athlon -S -o /dev/null -xc /dev/null >/dev/null 2>&1; then echo "-march=athlon"; else echo "-march=i686 -malign-functions=4"; fi) 
-endif
-
-ifdef CONFIG_MCRUSOE
-CFLAGS += -march=i686 -malign-functions=0 -malign-jumps=0 -malign-loops=0
-endif
-
-ifdef CONFIG_MWINCHIPC6
-CFLAGS += -march=i586
-endif
-
-ifdef CONFIG_MWINCHIP2
-CFLAGS += -march=i586
-endif
-
-ifdef CONFIG_MWINCHIP3D
-CFLAGS += -march=i586
-endif
-
-ifdef CONFIG_MCYRIXIII
-CFLAGS += -march=i586
-endif
+CFLAGS += $(cflags-y)
 
 ifdef CONFIG_VISWS
 MACHINE	:= mach-visws
@@ -94,47 +52,39 @@
 HEAD := arch/i386/kernel/head.o arch/i386/kernel/init_task.o
 
 libs-y 					+= arch/i386/lib/
-core-y					+= arch/i386/kernel/ arch/i386/mm/ arch/i386/$(MACHINE)/
+core-y					+= arch/i386/kernel/ arch/i386/mm/ \
+					   arch/i386/$(MACHINE)/
 drivers-$(CONFIG_MATH_EMULATION)	+= arch/i386/math-emu/
 drivers-$(CONFIG_PCI)			+= arch/i386/pci/
 
-CFLAGS += -I$(TOPDIR)/arch/i386/$(MACHINE)
-AFLAGS += -I$(TOPDIR)/arch/i386/$(MACHINE)
+CFLAGS += -Iarch/i386/$(MACHINE)
+AFLAGS += -Iarch/i386/$(MACHINE)
 
-MAKEBOOT = +$(MAKE) -C arch/$(ARCH)/boot
+makeboot = $(call descend,arch/i386/boot,$(1))
 
 .PHONY: zImage bzImage compressed zlilo bzlilo zdisk bzdisk install \
 		clean archclean archmrproper
 
 all: bzImage
 
-zImage: vmlinux
-	@$(MAKEBOOT) zImage
+BOOTIMAGE=arch/i386/boot/bzImage
+zImage zlilo zdisk: BOOTIMAGE=arch/i386/boot/zImage
 
-bzImage: vmlinux
-	@$(MAKEBOOT) bzImage
+zImage bzImage: vmlinux
+	+@$(call makeboot,$(BOOTIMAGE))
 
 compressed: zImage
 
-zlilo: vmlinux
-	@$(MAKEBOOT) BOOTIMAGE=zImage zlilo
-
-tmp:
-	@$(MAKEBOOT) BOOTIMAGE=bzImage zlilo
-bzlilo: vmlinux
-	@$(MAKEBOOT) BOOTIMAGE=bzImage zlilo
-
-zdisk: vmlinux
-	@$(MAKEBOOT) BOOTIMAGE=zImage zdisk
+zlilo bzlilo: vmlinux
+	+@$(call makeboot,BOOTIMAGE=$(BOOTIMAGE) zlilo)
 
-bzdisk: vmlinux
-	@$(MAKEBOOT) BOOTIMAGE=bzImage zdisk
+zdisk bzdisk: vmlinux
+	+@$(call makeboot,BOOTIMAGE=$(BOOTIMAGE) zdisk)
 
 install: vmlinux
-	@echo 'Cleaning up (arch)'
-	@$(MAKEBOOT) BOOTIMAGE=bzImage install
+	+@$(call makeboot,BOOTIMAGE=$(BOOTIMAGE) install)
 
 archclean:
-	@$(MAKEBOOT) clean
+	+@$(call makeboot,clean)
 
 archmrproper:
diff -Nru a/arch/i386/boot/Makefile b/arch/i386/boot/Makefile
--- a/arch/i386/boot/Makefile	Mon Oct  7 11:25:32 2002
+++ b/arch/i386/boot/Makefile	Mon Oct  7 11:25:32 2002
@@ -25,7 +25,7 @@
 
 #RAMDISK := -DRAMDISK=512
 
-EXTRA_TARGETS	:= vmlinux.bin bvmlinux.bin bootsect bootsect.o \
+EXTRA_TARGETS	:= vmlinux.bin bootsect bootsect.o \
 		   setup setup.o zImage bzImage
 
 host-progs	:= tools/build
@@ -38,30 +38,32 @@
 
 # ---------------------------------------------------------------------------
 
-zImage:  IMAGE_OFFSET := 0x1000
-zImage:  EXTRA_AFLAGS := -traditional $(SVGA_MODE) $(RAMDISK)
-bzImage: IMAGE_OFFSET := 0x100000
-bzImage: EXTRA_AFLAGS := -traditional $(SVGA_MODE) $(RAMDISK) -D__BIG_KERNEL__
-bzImage: BUILDFLAGS   := -b
-
-quiet_cmd_image = BUILD  $(RELDIR)/$@
-cmd_image = tools/build $(BUILDFLAGS) bootsect setup vmlinux.bin \
-			$(ROOT_DEV) > $@
+$(obj)/zImage:  IMAGE_OFFSET := 0x1000
+$(obj)/zImage:  EXTRA_AFLAGS := -traditional $(SVGA_MODE) $(RAMDISK)
+$(obj)/bzImage: IMAGE_OFFSET := 0x100000
+$(obj)/bzImage: EXTRA_AFLAGS := -traditional $(SVGA_MODE) $(RAMDISK) -D__BIG_KERNEL__
+$(obj)/bzImage: BUILDFLAGS   := -b
+
+quiet_cmd_image = BUILD   $(echo_target)
+cmd_image = $(obj)/tools/build $(BUILDFLAGS) $(obj)/bootsect $(obj)/setup \
+	    $(obj)/vmlinux.bin $(ROOT_DEV) > $@
 
-zImage bzImage: bootsect setup vmlinux.bin tools/build FORCE
+$(obj)/zImage $(obj)/bzImage: $(obj)/bootsect $(obj)/setup \
+			      $(obj)/vmlinux.bin $(obj)/tools/build FORCE
 	$(call if_changed,image)
 
-vmlinux.bin: compressed/vmlinux FORCE
+$(obj)/vmlinux.bin: $(obj)/compressed/vmlinux FORCE
 	$(call if_changed,objcopy)
 
 LDFLAGS_bootsect := -Ttext 0x0 -s --oformat binary
 LDFLAGS_setup	 := -Ttext 0x0 -s --oformat binary -e begtext
 
-setup bootsect: %: %.o FORCE
+$(obj)/setup $(obj)/bootsect: %: %.o FORCE
 	$(call if_changed,ld)
 
-compressed/vmlinux: FORCE
-	@$(MAKE) IMAGE_OFFSET=$(IMAGE_OFFSET) -C compressed vmlinux
+$(obj)/compressed/vmlinux: FORCE
+	+@$(call descend,$(obj)/compressed,IMAGE_OFFSET=$(IMAGE_OFFSET) \
+		$(obj)/compressed/vmlinux)
 
 
 zdisk: $(BOOTIMAGE)
@@ -71,18 +73,16 @@
 	if [ -f $(INSTALL_PATH)/vmlinuz ]; then mv $(INSTALL_PATH)/vmlinuz $(INSTALL_PATH)/vmlinuz.old; fi
 	if [ -f $(INSTALL_PATH)/System.map ]; then mv $(INSTALL_PATH)/System.map $(INSTALL_PATH)/System.old; fi
 	cat $(BOOTIMAGE) > $(INSTALL_PATH)/vmlinuz
-	cp $(TOPDIR)/System.map $(INSTALL_PATH)/
+	cp System.map $(INSTALL_PATH)/
 	if [ -x /sbin/lilo ]; then /sbin/lilo; else /etc/lilo/install; fi
 
 install: $(BOOTIMAGE)
-	sh -x ./install.sh $(KERNELRELEASE) $(BOOTIMAGE) $(TOPDIR)/System.map "$(INSTALL_PATH)"
+	sh $(src)/install.sh $(KERNELRELEASE) $(BOOTIMAGE) System.map "$(INSTALL_PATH)"
 
 clean:
 	@echo 'Cleaning up (boot)'
-	@rm -f tools/build vmlinux.bin bvmlinux.bin
-	@rm -f setup bootsect zImage compressed/vmlinux.out
-	@rm -f bsetup bbootsect bzImage compressed/bvmlinux.out
-	@$(MAKE) -C compressed clean
+	@rm -f $(host-progs) $(EXTRA_TARGETS)
+	+@$(call descend,$(obj)/compressed) clean
 
 archhelp:
 	@echo  '* bzImage	- Compressed kernel image (arch/$(ARCH)/boot/bzImage)'
diff -Nru a/arch/i386/boot/compressed/Makefile b/arch/i386/boot/compressed/Makefile
--- a/arch/i386/boot/compressed/Makefile	Mon Oct  7 11:25:26 2002
+++ b/arch/i386/boot/compressed/Makefile	Mon Oct  7 11:25:26 2002
@@ -11,19 +11,20 @@
 
 LDFLAGS_vmlinux := -Ttext $(IMAGE_OFFSET) -e startup_32
 
-vmlinux: head.o misc.o piggy.o FORCE
+$(obj)/vmlinux: $(obj)/head.o $(obj)/misc.o $(obj)/piggy.o FORCE
 	$(call if_changed,ld)
 
-vmlinux.bin: $(TOPDIR)/vmlinux FORCE
+$(obj)/vmlinux.bin: vmlinux FORCE
 	$(call if_changed,objcopy)
 
-vmlinux.bin.gz: vmlinux.bin FORCE
+$(obj)/vmlinux.bin.gz: $(obj)/vmlinux.bin FORCE
 	$(call if_changed,gzip)
 
 LDFLAGS_piggy.o := -r --format binary --oformat elf32-i386 -T
 
-piggy.o: vmlinux.scr vmlinux.bin.gz FORCE
+$(obj)/piggy.o: $(obj)/vmlinux.scr $(obj)/vmlinux.bin.gz FORCE
 	$(call if_changed,ld)
 
 clean:
-	@rm -f vmlinux vmlinux.bin vmlinux.bin.gz
+	@echo 'Cleaning up (boot/compressed)'
+	@rm -f $(EXTRA_TARGETS)
diff -Nru a/arch/i386/config.in b/arch/i386/config.in
--- a/arch/i386/config.in	Mon Oct  7 11:25:29 2002
+++ b/arch/i386/config.in	Mon Oct  7 11:25:29 2002
@@ -293,6 +293,8 @@
    fi
 fi
 
+tristate 'NatSemi SCx200 support' CONFIG_SCx200
+
 source drivers/pci/Config.in
 
 bool 'EISA support' CONFIG_EISA
diff -Nru a/arch/i386/kernel/Makefile b/arch/i386/kernel/Makefile
--- a/arch/i386/kernel/Makefile	Mon Oct  7 11:25:26 2002
+++ b/arch/i386/kernel/Makefile	Mon Oct  7 11:25:26 2002
@@ -29,4 +29,7 @@
 
 EXTRA_AFLAGS   := -traditional
 
+export-objs += scx200.o
+obj-$(CONFIG_SCx200)		+= scx200.o
+
 include $(TOPDIR)/Rules.make
diff -Nru a/arch/i386/kernel/apic.c b/arch/i386/kernel/apic.c
--- a/arch/i386/kernel/apic.c	Mon Oct  7 11:25:40 2002
+++ b/arch/i386/kernel/apic.c	Mon Oct  7 11:25:40 2002
@@ -813,24 +813,9 @@
 
 static void setup_APIC_timer(unsigned int clocks)
 {
-	unsigned int slice, t0, t1;
 	unsigned long flags;
-	int delta;
 
-	local_save_flags(flags);
-	local_irq_enable();
-	/*
-	 * ok, Intel has some smart code in their APIC that knows
-	 * if a CPU was in 'hlt' lowpower mode, and this increases
-	 * its APIC arbitration priority. To avoid the external timer
-	 * IRQ APIC event being in synchron with the APIC clock we
-	 * introduce an interrupt skew to spread out timer events.
-	 *
-	 * The number of slices within a 'big' timeslice is NR_CPUS+1
-	 */
-
-	slice = clocks / (NR_CPUS+1);
-	printk("cpu: %d, clocks: %d, slice: %d\n", smp_processor_id(), clocks, slice);
+	local_irq_save(flags);
 
 	/*
 	 * Wait for IRQ0's slice:
@@ -838,22 +823,6 @@
 	wait_8254_wraparound();
 
 	__setup_APIC_LVTT(clocks);
-
-	t0 = apic_read(APIC_TMICT)*APIC_DIVISOR;
-	/* Wait till TMCCT gets reloaded from TMICT... */
-	do {
-		t1 = apic_read(APIC_TMCCT)*APIC_DIVISOR;
-		delta = (int)(t0 - t1 - slice*(smp_processor_id()+1));
-	} while (delta >= 0);
-	/* Now wait for our slice for real. */
-	do {
-		t1 = apic_read(APIC_TMCCT)*APIC_DIVISOR;
-		delta = (int)(t0 - t1 - slice*(smp_processor_id()+1));
-	} while (delta < 0);
-
-	__setup_APIC_LVTT(clocks);
-
-	printk("CPU%d<T0:%d,T1:%d,D:%d,S:%d,C:%d>\n", smp_processor_id(), t0, t1, delta, slice, clocks);
 
 	local_irq_restore(flags);
 }
diff -Nru a/arch/i386/kernel/bluesmoke.c b/arch/i386/kernel/bluesmoke.c
--- a/arch/i386/kernel/bluesmoke.c	Mon Oct  7 11:25:29 2002
+++ b/arch/i386/kernel/bluesmoke.c	Mon Oct  7 11:25:29 2002
@@ -9,7 +9,7 @@
 #include <linux/smp.h>
 #include <linux/config.h>
 #include <linux/irq.h>
-#include <linux/tqueue.h>
+#include <linux/workqueue.h>
 #include <linux/interrupt.h>
 
 #include <asm/processor.h> 
@@ -306,15 +306,13 @@
 	add_timer (&mce_timer);
 } 
 
-static struct tq_struct mce_task = { 
-	.routine = do_mce_timer	
-};
+static DECLARE_WORK(mce_work, do_mce_timer, NULL);
 
 static void mce_timerfunc (unsigned long data)
 {
 #ifdef CONFIG_SMP
 	if (num_online_cpus() > 1) { 
-		schedule_task(&mce_task); 
+		schedule_work(&mce_work); 
 		return;
 	}
 #endif
diff -Nru a/arch/i386/kernel/cpu/cpufreq/longhaul.c b/arch/i386/kernel/cpu/cpufreq/longhaul.c
--- a/arch/i386/kernel/cpu/cpufreq/longhaul.c	Mon Oct  7 11:25:25 2002
+++ b/arch/i386/kernel/cpu/cpufreq/longhaul.c	Mon Oct  7 11:25:25 2002
@@ -26,6 +26,7 @@
 #include <linux/init.h>
 #include <linux/cpufreq.h>
 #include <linux/slab.h>
+#include <linux/string.h>
 
 #include <asm/msr.h>
 #include <asm/timex.h>
diff -Nru a/arch/i386/kernel/i386_ksyms.c b/arch/i386/kernel/i386_ksyms.c
--- a/arch/i386/kernel/i386_ksyms.c	Mon Oct  7 11:25:35 2002
+++ b/arch/i386/kernel/i386_ksyms.c	Mon Oct  7 11:25:35 2002
@@ -33,6 +33,9 @@
 extern void dump_thread(struct pt_regs *, struct user *);
 extern spinlock_t rtc_lock;
 
+/* This is definitely a GPL-only symbol */
+EXPORT_SYMBOL_GPL(cpu_gdt_table);
+
 #if defined(CONFIG_APM_MODULE)
 extern void machine_real_restart(unsigned char *, int);
 EXPORT_SYMBOL(machine_real_restart);
diff -Nru a/arch/i386/kernel/ioport.c b/arch/i386/kernel/ioport.c
--- a/arch/i386/kernel/ioport.c	Mon Oct  7 11:25:35 2002
+++ b/arch/i386/kernel/ioport.c	Mon Oct  7 11:25:35 2002
@@ -56,6 +56,7 @@
 {
 	struct thread_struct * t = &current->thread;
 	struct tss_struct * tss;
+	unsigned long *bitmap = NULL;
 	int ret = 0;
 
 	if ((from + num <= from) || (from + num > IO_BITMAP_SIZE*32))
@@ -63,15 +64,12 @@
 	if (turn_on && !capable(CAP_SYS_RAWIO))
 		return -EPERM;
 
-	tss = init_tss + get_cpu();
-
 	/*
 	 * If it's the first ioperm() call in this thread's lifetime, set the
 	 * IO bitmap up. ioperm() is much less timing critical than clone(),
 	 * this is why we delay this operation until now:
 	 */
 	if (!t->ts_io_bitmap) {
-		unsigned long *bitmap;
 		bitmap = kmalloc(IO_BITMAP_BYTES, GFP_KERNEL);
 		if (!bitmap) {
 			ret = -ENOMEM;
@@ -83,20 +81,19 @@
 		 */
 		memset(bitmap, 0xff, IO_BITMAP_BYTES);
 		t->ts_io_bitmap = bitmap;
-		/*
-		 * this activates it in the TSS
-		 */
-		tss->bitmap = IO_BITMAP_OFFSET;
 	}
 
+	tss = init_tss + get_cpu();
+	if (bitmap)
+		tss->bitmap = IO_BITMAP_OFFSET;	/* Activate it in the TSS */
+
 	/*
 	 * do it in the per-thread copy and in the TSS ...
 	 */
 	set_bitmap(t->ts_io_bitmap, from, num, !turn_on);
 	set_bitmap(tss->io_bitmap, from, num, !turn_on);
-
-out:
 	put_cpu();
+out:
 	return ret;
 }
 
diff -Nru a/arch/i386/kernel/mca.c b/arch/i386/kernel/mca.c
--- a/arch/i386/kernel/mca.c	Mon Oct  7 11:25:36 2002
+++ b/arch/i386/kernel/mca.c	Mon Oct  7 11:25:36 2002
@@ -106,6 +106,8 @@
 /*
  * Motherboard register spinlock. Untested on SMP at the moment, but
  * are there any MCA SMP boxes?
+ *
+ * Yes - Alan
  */
 static spinlock_t mca_lock = SPIN_LOCK_UNLOCKED;
 
diff -Nru a/arch/i386/kernel/scx200.c b/arch/i386/kernel/scx200.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/arch/i386/kernel/scx200.c	Mon Oct  7 11:25:41 2002
@@ -0,0 +1,128 @@
+/* linux/arch/i386/kernel/scx200.c 
+
+   Copyright (c) 2001,2002 Christer Weinigel <wingel@nano-system.com>
+
+   National Semiconductor SCx200 support. */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+
+#include <linux/scx200.h>
+#include <linux/scx200.h>
+
+#define NAME "scx200"
+
+MODULE_AUTHOR("Christer Weinigel <wingel@nano-system.com>");
+MODULE_DESCRIPTION("NatSemi SCx200 Driver");
+MODULE_LICENSE("GPL");
+
+unsigned scx200_gpio_base = 0;
+long scx200_gpio_shadow[2];
+
+spinlock_t scx200_gpio_lock = SPIN_LOCK_UNLOCKED;
+static spinlock_t scx200_gpio_config_lock = SPIN_LOCK_UNLOCKED;
+
+u32 scx200_gpio_configure(int index, u32 mask, u32 bits)
+{
+	u32 config, new_config;
+	unsigned long flags;
+
+	spin_lock_irqsave(&scx200_gpio_config_lock, flags);
+
+	outl(index, scx200_gpio_base + 0x20);
+	config = inl(scx200_gpio_base + 0x24);
+
+	new_config = (config & mask) | bits;
+	outl(new_config, scx200_gpio_base + 0x24);
+
+	spin_unlock_irqrestore(&scx200_gpio_config_lock, flags);
+
+	return config;
+}
+
+void scx200_gpio_dump(unsigned index)
+{
+	u32 config = scx200_gpio_configure(index, ~0, 0);
+	printk(KERN_DEBUG "GPIO%02u: 0x%08lx", index, (unsigned long)config);
+	
+	if (config & 1) 
+		printk(" OE"); /* output enabled */
+	else
+		printk(" TS"); /* tristate */
+	if (config & 2) 
+		printk(" PP"); /* push pull */
+	else
+		printk(" OD"); /* open drain */
+	if (config & 4) 
+		printk(" PUE"); /* pull up enabled */
+	else
+		printk(" PUD"); /* pull up disabled */
+	if (config & 8) 
+		printk(" LOCKED"); /* locked */
+	if (config & 16) 
+		printk(" LEVEL"); /* level input */
+	else
+		printk(" EDGE"); /* edge input */
+	if (config & 32) 
+		printk(" HI"); /* trigger on rising edge */
+	else
+		printk(" LO"); /* trigger on falling edge */
+	if (config & 64) 
+		printk(" DEBOUNCE"); /* debounce */
+	printk("\n");
+}
+
+int __init scx200_init(void)
+{
+	struct pci_dev *bridge;
+	int bank;
+	unsigned base;
+
+	printk(KERN_INFO NAME ": NatSemi SCx200 Driver\n");
+
+	if ((bridge = pci_find_device(PCI_VENDOR_ID_NS, 
+				      PCI_DEVICE_ID_NS_SCx200_BRIDGE,
+				      NULL)) == NULL)
+		return -ENODEV;
+
+	base = pci_resource_start(bridge, 0);
+	printk(KERN_INFO NAME ": GPIO base 0x%x\n", base);
+
+	if (request_region(base, SCx200_GPIO_SIZE, "NatSemi SCx200 GPIO") == 0) {
+		printk(KERN_ERR NAME ": can't allocate I/O for GPIOs\n");
+		return -EBUSY;
+	}
+
+	scx200_gpio_base = base;
+
+	/* read the current values driven on the GPIO signals */
+	for (bank = 0; bank < 2; ++bank)
+		scx200_gpio_shadow[bank] = inl(scx200_gpio_base + 0x10 * bank);
+
+	return 0;
+}
+
+void __exit scx200_cleanup(void)
+{
+	release_region(scx200_gpio_base, SCx200_GPIO_SIZE);
+}
+
+module_init(scx200_init);
+module_exit(scx200_cleanup);
+
+EXPORT_SYMBOL(scx200_gpio_base);
+EXPORT_SYMBOL(scx200_gpio_shadow);
+EXPORT_SYMBOL(scx200_gpio_lock);
+EXPORT_SYMBOL(scx200_gpio_configure);
+EXPORT_SYMBOL(scx200_gpio_dump);
+
+/*
+    Local variables:
+        compile-command: "make -k -C ../../.. SUBDIRS=arch/i386/kernel modules"
+        c-basic-offset: 8
+    End:
+*/
diff -Nru a/arch/i386/kernel/traps.c b/arch/i386/kernel/traps.c
--- a/arch/i386/kernel/traps.c	Mon Oct  7 11:25:26 2002
+++ b/arch/i386/kernel/traps.c	Mon Oct  7 11:25:26 2002
@@ -144,7 +144,7 @@
 	while (((long) stack & (THREAD_SIZE-1)) != 0) {
 		addr = *stack++;
 		if (kernel_text_address(addr)) {
-			printk(" [<%08lx>]", addr);
+			printk(" [<%08lx>] ", addr);
 			print_symbol("%s\n", addr);
 		}
 	}
@@ -189,7 +189,9 @@
  */
 void dump_stack(void)
 {
-	show_stack(0);
+	unsigned long stack;
+
+	show_trace(&stack);
 }
 
 void show_registers(struct pt_regs *regs)
diff -Nru a/arch/i386/mm/hugetlbpage.c b/arch/i386/mm/hugetlbpage.c
--- a/arch/i386/mm/hugetlbpage.c	Mon Oct  7 11:25:39 2002
+++ b/arch/i386/mm/hugetlbpage.c	Mon Oct  7 11:25:39 2002
@@ -44,24 +44,22 @@
 static struct page *
 alloc_hugetlb_page(void)
 {
-	struct list_head *curr, *head;
+	int i;
 	struct page *page;
 
 	spin_lock(&htlbpage_lock);
-
-	head = &htlbpage_freelist;
-	curr = head->next;
-
-	if (curr == head) {
+	if (list_empty(&htlbpage_freelist)) {
 		spin_unlock(&htlbpage_lock);
 		return NULL;
 	}
-	page = list_entry(curr, struct page, list);
-	list_del(curr);
+
+	page = list_entry(htlbpage_freelist.next, struct page, list);
+	list_del(&page->list);
 	htlbpagemem--;
 	spin_unlock(&htlbpage_lock);
 	set_page_count(page, 1);
-	memset(page_address(page), 0, HPAGE_SIZE);
+	for (i = 0; i < (HPAGE_SIZE/PAGE_SIZE); ++i)
+		clear_highpage(&page[i]);
 	return page;
 }
 
diff -Nru a/arch/i386/vmlinux.lds.S b/arch/i386/vmlinux.lds.S
--- a/arch/i386/vmlinux.lds.S	Mon Oct  7 11:25:26 2002
+++ b/arch/i386/vmlinux.lds.S	Mon Oct  7 11:25:26 2002
@@ -77,13 +77,13 @@
   . = ALIGN(32);
   .data.cacheline_aligned : { *(.data.cacheline_aligned) }
 
-  __bss_start = .;		/* BSS */
-  .bss : { *(.bss) }
-  __bss_stop = .; 
-
   __start___kallsyms = .;     /* All kernel symbols */
   __kallsyms : { *(__kallsyms) }
   __stop___kallsyms = .;
+
+  __bss_start = .;		/* BSS */
+  .bss : { *(.bss) }
+  __bss_stop = .; 
 
   _end = . ;
 
diff -Nru a/arch/m68k/atari/stram.c b/arch/m68k/atari/stram.c
--- a/arch/m68k/atari/stram.c	Mon Oct  7 11:25:36 2002
+++ b/arch/m68k/atari/stram.c	Mon Oct  7 11:25:36 2002
@@ -1046,13 +1046,7 @@
 	.release =	stram_release,
 };
 
-static struct gendisk stram_disk = {
-	.major = STRAM_MAJOR,
-	.first_minor = STRAM_MINOR,
-	.minor_shift = 0,
-	.fops = &stram_fops,
-	.disk_name = "stram"
-};
+static struct gendisk *stram_disk;
 
 int __init stram_device_init(void)
 {
@@ -1063,15 +1057,24 @@
 	if (!max_swap_size)
 		/* swapping not enabled */
 		return -ENXIO;
+	stram_disk = alloc_disk();
+	if (!stram_disk)
+		return -ENOMEM;
 
 	if (register_blkdev( STRAM_MAJOR, "stram", &stram_fops)) {
 		printk(KERN_ERR "stram: Unable to get major %d\n", STRAM_MAJOR);
+		put_disk(stram_disk);
 		return -ENXIO;
 	}
 
 	blk_init_queue(BLK_DEFAULT_QUEUE(STRAM_MAJOR), do_stram_request);
-	set_capacity(&stram_disk, (swap_end - swap_start)/512);
-	add_disk(&stram_disk);
+	stram_disk->major = STRAM_MAJOR;
+	stram_disk->first_minor = STRAM_MINOR;
+	stram_disk->minor_shift = 0;
+	stram_disk->fops = &stram_fops;
+	sprintf(stram_disk->disk_name, "stram");
+	set_capacity(stram_disk, (swap_end - swap_start)/512);
+	add_disk(stram_disk);
 	return 0;
 }
 
diff -Nru a/arch/s390/Config.help b/arch/s390/Config.help
--- a/arch/s390/Config.help	Mon Oct  7 11:25:36 2002
+++ b/arch/s390/Config.help	Mon Oct  7 11:25:36 2002
@@ -160,24 +160,10 @@
   Select "vm_reader" if you are running under VM/ESA and want
   to IPL the image from the emulated card reader.
 
-CONFIG_FAST_IRQ
-  Select this option in order to get the interrupts processed faster
-  on your S/390 or zSeries machine.  If selected, after an interrupt
-  is processed, the channel subsystem will be asked for other pending
-  interrupts which will also be processed before leaving the interrupt
-  context.  This speeds up the I/O a lot. Say "Y".
-
 CONFIG_MACHCHK_WARNING
   Select this option if you want the machine check handler on IBM S/390 or 
   zSeries to process warning machine checks (e.g. on power failures). 
   If unsure, say "Y".
-
-CONFIG_CHSC
-  Select this option if you want the s390 common I/O layer to use information
-  obtained by channel subsystem calls. This will enable Linux to process link
-  failures and resource accessibility events. Moreover, if you have procfs
-  enabled, you'll be able to toggle chpids logically offline and online. Even
-  if you don't understand what this means, you should say "Y".
 
 CONFIG_PROCESS_DEBUG
   Say Y to print all process fault locations to the console.  This is
diff -Nru a/arch/s390/config.in b/arch/s390/config.in
--- a/arch/s390/config.in	Mon Oct  7 11:25:36 2002
+++ b/arch/s390/config.in	Mon Oct  7 11:25:36 2002
@@ -17,22 +17,20 @@
 source init/Config.in
 
 mainmenu_option next_comment
+comment 'Base setup'
 comment 'Processor type and features'
 bool 'Symmetric multi-processing support' CONFIG_SMP
 bool 'IEEE FPU emulation' CONFIG_MATHEMU
-endmenu
 
-mainmenu_option next_comment
-comment 'Base setup'
-bool 'Fast IRQ handling' CONFIG_FAST_IRQ
+comment 'I/O subsystem configuration'
 bool 'Process warning machine checks' CONFIG_MACHCHK_WARNING
-bool 'Use chscs for Common I/O' CONFIG_CHSC
-
 tristate 'QDIO support' CONFIG_QDIO
-  if [ "$CONFIG_QDIO" != "n" ]; then
-    bool '   Performance statistics in /proc' CONFIG_QDIO_PERF_STATS
-  fi
+if [ "$CONFIG_QDIO" != "n" ]; then
+  bool '   Performance statistics in /proc' CONFIG_QDIO_PERF_STATS
+fi
 
+comment 'Misc'
+bool 'Preemptible Kernel' CONFIG_PREEMPT
 bool 'Builtin IPL record support' CONFIG_IPL
 if [ "$CONFIG_IPL" = "y" ]; then
   choice 'IPL method generated into head.S' \
@@ -68,7 +66,6 @@
 mainmenu_option next_comment
 comment 'Kernel hacking'
 
-#bool 'Debug kmalloc/kfree' CONFIG_DEBUG_MALLOC
 #if [ "$CONFIG_CTC" = "y" ]; then
 #  bool 'Remote GDB kernel debugging' CONFIG_REMOTE_DEBUG
 #fi
diff -Nru a/arch/s390/defconfig b/arch/s390/defconfig
--- a/arch/s390/defconfig	Mon Oct  7 11:25:37 2002
+++ b/arch/s390/defconfig	Mon Oct  7 11:25:37 2002
@@ -31,19 +31,26 @@
 CONFIG_KMOD=y
 
 #
+# Base setup
+#
+
+#
 # Processor type and features
 #
 CONFIG_SMP=y
 CONFIG_MATHEMU=y
 
 #
-# Base setup
+# I/O subsystem configuration
 #
-CONFIG_FAST_IRQ=y
 CONFIG_MACHCHK_WARNING=y
-CONFIG_CHSC=y
 CONFIG_QDIO=m
 # CONFIG_QDIO_PERF_STATS is not set
+
+#
+# Misc
+#
+# CONFIG_PREEMPT is not set
 CONFIG_IPL=y
 # CONFIG_IPL_TAPE is not set
 CONFIG_IPL_VM=y
@@ -84,9 +91,6 @@
 #
 # CONFIG_SCSI_7000FASST is not set
 # CONFIG_SCSI_ACARD is not set
-# CONFIG_SCSI_AHA152X is not set
-# CONFIG_SCSI_AHA1542 is not set
-# CONFIG_SCSI_AHA1740 is not set
 # CONFIG_SCSI_AIC7XXX is not set
 # CONFIG_SCSI_AIC7XXX_OLD is not set
 # CONFIG_SCSI_DPT_I2O is not set
@@ -114,7 +118,6 @@
 # CONFIG_SCSI_PCI2220I is not set
 # CONFIG_SCSI_PSI240I is not set
 # CONFIG_SCSI_QLOGIC_FAS is not set
-# CONFIG_SCSI_SIM710 is not set
 # CONFIG_SCSI_SYM53C416 is not set
 # CONFIG_SCSI_T128 is not set
 # CONFIG_SCSI_U14_34F is not set
@@ -153,7 +156,7 @@
 CONFIG_MD_RAID1=m
 CONFIG_MD_RAID5=m
 # CONFIG_MD_MULTIPATH is not set
-CONFIG_BLK_DEV_LVM=m
+# CONFIG_BLK_DEV_LVM is not set
 
 #
 # Character device drivers
@@ -226,25 +229,22 @@
 # CONFIG_INET_ECN is not set
 # CONFIG_SYN_COOKIES is not set
 CONFIG_IPV6=m
-# CONFIG_KHTTPD is not set
-# CONFIG_ATM is not set
-# CONFIG_VLAN_8021Q is not set
 
 #
-#  
+#    SCTP Configuration (EXPERIMENTAL)
 #
+CONFIG_IPV6_SCTP__=m
+# CONFIG_IP_SCTP is not set
+# CONFIG_ATM is not set
+# CONFIG_VLAN_8021Q is not set
+# CONFIG_LLC is not set
 # CONFIG_IPX is not set
 # CONFIG_ATALK is not set
-
-#
-# Appletalk devices
-#
 # CONFIG_DEV_APPLETALK is not set
 # CONFIG_DECNET is not set
 # CONFIG_BRIDGE is not set
 # CONFIG_X25 is not set
 # CONFIG_LAPB is not set
-# CONFIG_LLC is not set
 # CONFIG_NET_DIVERT is not set
 # CONFIG_ECONET is not set
 # CONFIG_WAN_ROUTER is not set
@@ -299,7 +299,7 @@
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 CONFIG_DEVFS_FS=y
-CONFIG_DEVFS_MOUNT=y
+# CONFIG_DEVFS_MOUNT is not set
 # CONFIG_DEVFS_DEBUG is not set
 # CONFIG_DEVPTS_FS is not set
 # CONFIG_QNX4FS_FS is not set
@@ -311,6 +311,9 @@
 # CONFIG_UDF_RW is not set
 # CONFIG_UFS_FS is not set
 # CONFIG_UFS_FS_WRITE is not set
+# CONFIG_XFS_FS is not set
+# CONFIG_XFS_RT is not set
+# CONFIG_XFS_QUOTA is not set
 
 #
 # Network File Systems
@@ -361,6 +364,11 @@
 # Kernel hacking
 #
 CONFIG_MAGIC_SYSRQ=y
+
+#
+# Security options
+#
+CONFIG_SECURITY_CAPABILITIES=y
 
 #
 # Library routines
diff -Nru a/arch/s390/kernel/Makefile b/arch/s390/kernel/Makefile
--- a/arch/s390/kernel/Makefile	Mon Oct  7 11:25:39 2002
+++ b/arch/s390/kernel/Makefile	Mon Oct  7 11:25:39 2002
@@ -5,10 +5,10 @@
 EXTRA_TARGETS	:= head.o init_task.o
 EXTRA_AFLAGS	:= -traditional
 
-export-objs	:= debug.o ebcdic.o irq.o s390_ext.o smp.o s390_ksyms.o
-obj-y	:= entry.o bitmap.o traps.o time.o process.o irq.o \
+export-objs	:= debug.o ebcdic.o s390_ext.o smp.o s390_ksyms.o
+obj-y	:= entry.o bitmap.o traps.o time.o process.o \
             setup.o sys_s390.o ptrace.o signal.o cpcmd.o ebcdic.o \
-            semaphore.o s390fpu.o reipl.o s390_ext.o debug.o
+            semaphore.o reipl.o s390_ext.o debug.o
 
 obj-$(CONFIG_MODULES)		+= s390_ksyms.o
 obj-$(CONFIG_SMP)		+= smp.o
diff -Nru a/arch/s390/kernel/debug.c b/arch/s390/kernel/debug.c
--- a/arch/s390/kernel/debug.c	Mon Oct  7 11:25:26 2002
+++ b/arch/s390/kernel/debug.c	Mon Oct  7 11:25:26 2002
@@ -21,6 +21,7 @@
 #include <asm/semaphore.h>
 
 #include <linux/module.h>
+#include <linux/init.h>
 
 #include <asm/debug.h>
 
@@ -146,7 +147,7 @@
 static debug_info_t *debug_area_last = NULL;
 DECLARE_MUTEX(debug_lock);
 
-static int initialized = 0;
+static int initialized;
 
 static struct file_operations debug_file_ops = {
 	read:    debug_output,
@@ -591,7 +592,7 @@
 
 	MOD_INC_USE_COUNT;
 	if (!initialized)
-		debug_init();
+		BUG();
 	down(&debug_lock);
 
         /* create new debug_info */
@@ -828,18 +829,16 @@
  * - is called exactly once to initialize the debug feature
  */
 
-int debug_init(void)
+static int __init debug_init(void)
 {
 	int rc = 0;
 
 	down(&debug_lock);
-	if (!initialized) {
 #ifdef CONFIG_PROC_FS
-		debug_proc_root_entry = proc_mkdir(DEBUG_DIR_ROOT, NULL);
+	debug_proc_root_entry = proc_mkdir(DEBUG_DIR_ROOT, NULL);
 #endif /* CONFIG_PROC_FS */
-		printk(KERN_INFO "debug: Initialization complete\n");
-		initialized = 1;
-	}
+	printk(KERN_INFO "debug: Initialization complete\n");
+	initialized = 1;
 	up(&debug_lock);
 
 	return rc;
@@ -1173,27 +1172,9 @@
 }
 
 /*
- * init_module:
- */
-
-#ifdef MODULE
-int init_module(void)
-{
-	int rc = 0;
-#ifdef DEBUG
-	printk("debug_module_init: \n");
-#endif
-	rc = debug_init();
-	if (rc) 
-		printk(KERN_INFO "debug: an error occurred with debug_init\n");
-	return rc;
-}
-
-/*
- * cleanup_module:
+ * clean up module
  */
-
-void cleanup_module(void)
+void __exit debug_exit(void)
 {
 #ifdef DEBUG
 	printk("debug_cleanup_module: \n");
@@ -1204,7 +1185,12 @@
 	return;
 }
 
-#endif			/* MODULE */
+/*
+ * module definitions
+ */
+core_initcall(debug_init);
+module_exit(debug_exit);
+MODULE_LICENSE("GPL");
 
 EXPORT_SYMBOL(debug_register);
 EXPORT_SYMBOL(debug_unregister); 
diff -Nru a/arch/s390/kernel/entry.S b/arch/s390/kernel/entry.S
--- a/arch/s390/kernel/entry.S	Mon Oct  7 11:25:26 2002
+++ b/arch/s390/kernel/entry.S	Mon Oct  7 11:25:26 2002
@@ -15,7 +15,6 @@
 #include <asm/cache.h>
 #include <asm/lowcore.h>
 #include <asm/errno.h>
-#include <asm/smp.h>
 #include <asm/ptrace.h>
 #include <asm/thread_info.h>
 
@@ -49,7 +48,7 @@
 SP_TRAP      =  (SP_ORIG_R2+GPR_SIZE)
 SP_SIZE      =  (SP_TRAP+4)
 
-_TIF_WORK_MASK = (_TIF_NOTIFY_RESUME | _TIF_SIGPENDING | _TIF_NEED_RESCHED)
+_TIF_WORK_MASK = (_TIF_SIGPENDING | _TIF_NEED_RESCHED)
 
 /*
  * Base Address of this Module --- saved in __LC_ENTRY_BASE
@@ -199,33 +198,44 @@
         RESTORE_ALL 1
 
 #
-# One of the work bits _TIF_NOTIFY_RESUME, _TIF_SIGPENDING or
-# _TIF_NEED_RESCHED is on. Find out which one.
+# recheck if there is more work to do
+#
+sysc_work_loop:
+	stnsm   24(%r15),0xfc     # disable I/O and ext. interrupts
+        GET_THREAD_INFO           # load pointer to task_struct to R9
+	tm	__TI_flags+3(%r9),_TIF_WORK_MASK
+	bz	BASED(sysc_leave)      # there is no work to do
+#
+# One of the work bits is on. Find out which one.
+# Checked are: _TIF_SIGPENDING and _TIF_NEED_RESCHED
 #
 sysc_work:
-        tm      SP_PSW+1(%r15),0x01 # returning to user ?
-        bno     BASED(sysc_leave) # no-> skip resched & signal
 	tm	__TI_flags+3(%r9),_TIF_NEED_RESCHED
 	bo	BASED(sysc_reschedule)
-	# add a test for TIF_NOTIFY_RESUME here when it is used.
-	# _TIF_SIGPENDING is the only flag left
-#
-# call do_signal before return
-#
-sysc_signal_return:     
-        la      %r2,SP_PTREGS(%r15) # load pt_regs
-        sr      %r3,%r3           # clear *oldset
-        l       %r1,BASED(.Ldo_signal)
-	la      %r14,BASED(sysc_return)
-        br      %r1               # return point is sysc_return
+	tm	__TI_flags+3(%r9),_TIF_SIGPENDING
+	bo	BASED(sysc_sigpending)
+	b	BASED(sysc_leave)
 
 #
-# call schedule with sysc_return as return-address
-#
+# _TIF_NEED_RESCHED is set, call schedule
+#	
 sysc_reschedule:        
+        stosm   24(%r15),0x03          # reenable interrupts
         l       %r1,BASED(.Lschedule)
-	la      %r14,BASED(sysc_return)
-        br      %r1               # call scheduler, return to sysc_return
+	la      %r14,BASED(sysc_work_loop)
+	br      %r1		       # call scheduler
+
+#
+# _TIF_SIGPENDING is set, call do_signal
+#
+sysc_sigpending:     
+        stosm   24(%r15),0x03          # reenable interrupts
+        la      %r2,SP_PTREGS(%r15)    # load pt_regs
+        sr      %r3,%r3                # clear *oldset
+        l       %r1,BASED(.Ldo_signal)
+	basr	%r14,%r1               # call do_signal
+        stnsm   24(%r15),0xfc          # disable I/O and ext. interrupts
+	b	BASED(sysc_leave)      # out of here, do NOT recheck
 
 #
 # call trace before and after sys_call
@@ -258,9 +268,7 @@
         basr    %r13,0
         l       %r13,.Lentry_base-.(%r13)  # setup base pointer to &entry_base
         GET_THREAD_INFO           # load pointer to task_struct to R9
-        sr      %r0,%r0           # child returns 0
-        st      %r0,SP_R2(%r15)   # store return value (change R2 on stack)
-#ifdef CONFIG_SMP
+#if CONFIG_SMP || CONFIG_PREEMPT
         l       %r1,BASED(.Lschedtail)
 	la      %r14,BASED(sysc_return)
         br      %r1               # call schedule_tail, return to sysc_return
@@ -581,7 +589,15 @@
 	.long  sys_futex
 	.long  sys_sched_setaffinity
 	.long  sys_sched_getaffinity	 /* 240 */
-	.rept  255-240
+	.long  sys_security
+	.long  sys_ni_syscall		 /* reserved for TUX */
+	.long  sys_io_setup
+	.long  sys_io_destroy
+	.long  sys_io_getevents		 /* 245 */
+	.long  sys_io_submit
+	.long  sys_io_cancel
+	.long  sys_exit_group
+	.rept  255-248
 	.long  sys_ni_syscall
 	.endr
 
@@ -608,13 +624,15 @@
         tm      __LC_PGM_INT_CODE+1,0x80 # check whether we got a per exception
         bnz     BASED(pgm_per)           # got per exception -> special case
 	SAVE_ALL __LC_PGM_OLD_PSW,1
+	la	%r8,0x7f
+        l       %r3,__LC_PGM_ILC         # load program interruption code
         l       %r7,BASED(.Ljump_table)
-        lh      %r8,__LC_PGM_INT_CODE
+	nr	%r8,%r3
         sll     %r8,2
 	GET_THREAD_INFO
+        stosm   24(%r15),0x03            # reenable interrupts
         l       %r7,0(%r8,%r7)		 # load address of handler routine
         la      %r2,SP_PTREGS(%r15)	 # address of register-save area
-        l       %r3,__LC_PGM_ILC         # load program interruption code
 	la      %r14,BASED(sysc_return)
 	br      %r7			 # branch to interrupt-handler
 
@@ -639,6 +657,7 @@
 	GET_THREAD_INFO
 	la	%r4,0x7f
 	l	%r3,__LC_PGM_ILC	 # load program interruption code
+        stosm   24(%r15),0x03            # reenable interrupts
         nr      %r4,%r3                  # clear per-event-bit and ilc
         be      BASED(pgm_per_only)      # only per or per+check ?
         l       %r1,BASED(.Ljump_table)
@@ -658,9 +677,9 @@
 pgm_svcper:
 	SAVE_ALL __LC_SVC_OLD_PSW,1
         GET_THREAD_INFO           # load pointer to task_struct to R9
+        stosm   24(%r15),0x03     # reenable interrupts
 	lh	%r8,0x8a	  # get svc number from lowcore
         sll     %r8,2
-        stosm   24(%r15),0x03     # reenable interrupts
         l       %r8,sys_call_table-entry_base(%r8,%r13) # get system call addr.
 	tm	__TI_flags+3(%r9),_TIF_SYSCALL_TRACE
         bo      BASED(pgm_tracesys)
@@ -724,59 +743,81 @@
         basr    %r14,%r1          # branch to standard irq handler
 
 io_return:
-#	
-# check, if bottom-half has to be done
-#
-        l       %r1,__TI_cpu(%r9)
-        sll     %r1,L1_CACHE_SHIFT
-        al      %r1,BASED(.Lirq_stat)  # get address of irq_stat
-        icm     %r0,15,0(%r1)          # test irq_stat[#cpu].__softirq_pending
-        bnz     BASED(io_handle_bottom_half)
-io_return_bh:
+        tm      SP_PSW+1(%r15),0x01    # returning to user ?
+#ifdef CONFIG_PREEMPT
+	bno     BASED(io_preempt)      # no -> check for preemptive scheduling
+#else
+        bno     BASED(io_leave)        # no-> skip resched & signal
+#endif
 	tm	__TI_flags+3(%r9),_TIF_WORK_MASK
 	bnz	BASED(io_work)         # there is work to do (signals etc.)
 io_leave:
-        stnsm   24(%r15),0xfc          # disable I/O and ext. interrupts
         RESTORE_ALL 0
 
+#ifdef CONFIG_PREEMPT
+io_preempt:
+	icm	%r0,15,__TI_precount(%r9)
+	bnz     BASED(io_leave)
+io_resume_loop:
+	tm	__TI_flags+3(%r9),_TIF_NEED_RESCHED
+	bno	BASED(io_leave)
+	mvc     __TI_precount(4,%r9),.Lc_pactive
+	# hmpf, we are on the async. stack but to call schedule
+	# we have to move the interrupt frame to the process stack
+	l	%r1,SP_R15(%r15)
+	s	%r1,BASED(.Lc_spsize)
+	n	%r1,BASED(.Lc0xfffffff8)
+	mvc	SP_PTREGS(SP_SIZE-SP_PTREGS,%r1),SP_PTREGS(%r15)
+        xc      0(4,%r1),0(%r1)        # clear back chain
+	lr	%r15,%r1
+        stosm   24(%r15),0x03          # reenable interrupts
+        l       %r1,BASED(.Lschedule)
+	basr	%r14,%r1	       # call schedule
+        stnsm   24(%r15),0xfc          # disable I/O and ext. interrupts
+        GET_THREAD_INFO                # load pointer to task_struct to R9
+	xc      __TI_precount(4,%r9),__TI_precount(%r9)
+	b	BASED(io_resume_loop)
+#endif
+
 #
-# call do_softirq
+# recheck if there is more work to do
 #
-io_handle_bottom_half:
-        l       %r1,BASED(.Ldo_softirq)
-        la      %r14,BASED(io_return_bh)
-        br      %r1               # call do_softirq
-
+io_work_loop:
+        stnsm   24(%r15),0xfc          # disable I/O and ext. interrupts
+        GET_THREAD_INFO                # load pointer to task_struct to R9
+	tm	__TI_flags+3(%r9),_TIF_WORK_MASK
+	bz	BASED(io_leave)        # there is no work to do
 #
-# One of the work bits _TIF_NOTIFY_RESUME, _TIF_SIGPENDING or
-# _TIF_NEED_RESCHED is on. Find out which one.
+# One of the work bits is on. Find out which one.
+# Checked are: _TIF_SIGPENDING and _TIF_NEED_RESCHED
 #
 io_work:
-        tm      SP_PSW+1(%r15),0x01    # returning to user ?
-        bno     BASED(io_leave)        # no-> skip resched & signal
-        stosm   24(%r15),0x03          # reenable interrupts
 	tm	__TI_flags+3(%r9),_TIF_NEED_RESCHED
 	bo	BASED(io_reschedule)
-	# add a test for TIF_NOTIFY_RESUME here when it is used.
-	# _TIF_SIGPENDING is the only flag left
+	tm	__TI_flags+3(%r9),_TIF_SIGPENDING
+	bo	BASED(io_sigpending)
+	b	BASED(io_leave)
 
 #
-# call do_signal before return
-#
-io_signal_return:     
-        la      %r2,SP_PTREGS(%r15) # load pt_regs
-        sr      %r3,%r3           # clear *oldset
-        l       %r1,BASED(.Ldo_signal)
-	la      %r14,BASED(io_leave)
-        br      %r1               # return point is io_leave
+# _TIF_NEED_RESCHED is set, call schedule
+#	
+io_reschedule:        
+        stosm   24(%r15),0x03          # reenable interrupts
+        l       %r1,BASED(.Lschedule)
+	la      %r14,BASED(io_work_loop)
+	br      %r1		       # call scheduler
 
 #
-# call schedule with io_return as return-address
+# _TIF_SIGPENDING is set, call do_signal
 #
-io_reschedule:        
-        l       %r1,BASED(.Lschedule)
-	la      %r14,BASED(io_return)
-        br      %r1               # call scheduler, return to io_return
+io_sigpending:     
+        stosm   24(%r15),0x03          # reenable interrupts
+        la      %r2,SP_PTREGS(%r15)    # load pt_regs
+        sr      %r3,%r3                # clear *oldset
+        l       %r1,BASED(.Ldo_signal)
+	basr    %r14,%r1	       # call do_signal
+        stnsm   24(%r15),0xfc          # disable I/O and ext. interrupts
+	b	BASED(io_leave)        # out of here, do NOT recheck
 
 /*
  * External interrupt handler routine
@@ -857,19 +898,12 @@
                .align 4
 .Lc0xfffffff8: .long  -8           # to align stack pointer to 8
 .Lc0xffffe000: .long  -8192        # to round stack pointer to &task_struct
-.Lc8191:       .long  8191
 .Lc_spsize:    .long  SP_SIZE
 .Lc_overhead:  .long  STACK_FRAME_OVERHEAD
 .Lc_ac:        .long  0,0,1
 .Lc_ENOSYS:    .long  -ENOSYS
-.Lc4:          .long  4
-.Lc20:         .long  20
-.Lc0x1202:     .long  0x1202
-.Lc0x1004:     .long  0x1004
-.Lc0x2401:     .long  0x2401
-.Lc0x4000:     .long  0x4000
+.Lc_pactive:   .long  PREEMPT_ACTIVE
 .Lc0xff:       .long  0xff
-.Lc128:        .long  128
 .Lc256:        .long  256
 
 /*
@@ -882,7 +916,6 @@
 .Lentry_base:  .long  entry_base
 .Lext_hash:    .long  ext_int_hash
 .Lhandle_per:  .long  handle_per_exception
-.Lirq_stat:    .long  irq_stat
 .Ljump_table:  .long  pgm_check_table
 .Lschedule:    .long  schedule
 .Lclone:       .long  sys_clone
@@ -896,7 +929,7 @@
 .Lsigaltstack: .long  sys_sigaltstack
 .Ltrace:       .long  syscall_trace
 .Lvfork:       .long  sys_vfork
-#ifdef CONFIG_SMP
+#if CONFIG_SMP || CONFIG_PREEMPT
 .Lschedtail:   .long  schedule_tail
 #endif
 
diff -Nru a/arch/s390/kernel/head.S b/arch/s390/kernel/head.S
--- a/arch/s390/kernel/head.S	Mon Oct  7 11:25:28 2002
+++ b/arch/s390/kernel/head.S	Mon Oct  7 11:25:28 2002
@@ -653,5 +653,5 @@
 .Lstart:    .long  start_kernel
 .Lbss_bgn:  .long  __bss_start
 .Lbss_end:  .long  _end
-.Laregs:    .long  0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0
+.Laregs:    .long  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
 
diff -Nru a/arch/s390/kernel/init_task.c b/arch/s390/kernel/init_task.c
--- a/arch/s390/kernel/init_task.c	Mon Oct  7 11:25:36 2002
+++ b/arch/s390/kernel/init_task.c	Mon Oct  7 11:25:36 2002
@@ -15,7 +15,7 @@
 
 static struct fs_struct init_fs = INIT_FS;
 static struct files_struct init_files = INIT_FILES;
-static struct signal_struct init_signals = INIT_SIGNALS;
+static struct signal_struct init_signals = INIT_SIGNALS(init_signals);
 struct mm_struct init_mm = INIT_MM(init_mm);
 
 /*
diff -Nru a/arch/s390/kernel/irq.c b/arch/s390/kernel/irq.c
--- a/arch/s390/kernel/irq.c	Mon Oct  7 11:25:35 2002
+++ /dev/null	Wed Dec 31 16:00:00 1969
@@ -1,360 +0,0 @@
-/*
- *  arch/s390/kernel/irq.c
- *
- *  S390 version
- *    Copyright (C) 1999,2000 IBM Deutschland Entwicklung GmbH, IBM Corporation
- *    Author(s): Ingo Adlung (adlung@de.ibm.com)
- *
- *  Derived from "arch/i386/kernel/irq.c"
- *    Copyright (C) 1992, 1999 Linus Torvalds, Ingo Molnar
- *
- *  S/390 I/O interrupt processing and I/O request processing is
- *   implemented in arch/s390/kernel/s390io.c
- */
-#include <linux/module.h>
-#include <linux/config.h>
-#include <linux/ptrace.h>
-#include <linux/errno.h>
-#include <linux/kernel_stat.h>
-#include <linux/signal.h>
-#include <linux/sched.h>
-#include <linux/ioport.h>
-#include <linux/interrupt.h>
-#include <linux/timex.h>
-#include <linux/slab.h>
-#include <linux/string.h>
-#include <linux/random.h>
-#include <linux/smp.h>
-#include <linux/threads.h>
-#include <linux/smp_lock.h>
-#include <linux/init.h>
-#include <linux/seq_file.h>
-
-#include <asm/system.h>
-#include <asm/io.h>
-#include <asm/irq.h>
-#include <asm/bitops.h>
-#include <asm/smp.h>
-#include <asm/pgtable.h>
-#include <asm/delay.h>
-#include <asm/lowcore.h>
-
-void          s390_init_IRQ   ( void );
-void          s390_free_irq   ( unsigned int irq, void *dev_id);
-int           s390_request_irq( unsigned int irq,
-                     void           (*handler)(int, void *, struct pt_regs *),
-                     unsigned long  irqflags,
-                     const char    *devname,
-                     void          *dev_id);
-
-#if 0
-/*
- * The following vectors are part of the Linux architecture, there
- * is no hardware IRQ pin equivalent for them, they are triggered
- * through the ICC by us (IPIs), via smp_message_pass():
- */
-BUILD_SMP_INTERRUPT(reschedule_interrupt)
-BUILD_SMP_INTERRUPT(invalidate_interrupt)
-BUILD_SMP_INTERRUPT(stop_cpu_interrupt)
-BUILD_SMP_INTERRUPT(mtrr_interrupt)
-BUILD_SMP_INTERRUPT(spurious_interrupt)
-#endif
-
-int show_interrupts(struct seq_file *p, void *v)
-{
-	int i, j;
-
-	seq_puts(p, "           ");
-
-	for (j=0; j<smp_num_cpus; j++)
-		seq_printf(p, "CPU%d       ",j);
-
-	seq_putc(p, '\n');
-
-	for (i = 0 ; i < NR_IRQS ; i++) {
-		if (ioinfo[i] == INVALID_STORAGE_AREA)
-			continue;
-
-		seq_printf(p, "%3d: ",i);
-		seq_printf(p, "  %s", ioinfo[i]->irq_desc.name);
-
-		seq_putc(p, '\n');
-	
-	} /* endfor */
-
-	return 0;
-}
-
-/*
- * Global interrupt locks for SMP. Allow interrupts to come in on any
- * CPU, yet make cli/sti act globally to protect critical regions..
- */
-#ifdef CONFIG_SMP
-atomic_t global_irq_holder = ATOMIC_INIT(NO_PROC_ID);
-atomic_t global_irq_lock = ATOMIC_INIT(0);
-atomic_t global_irq_count = ATOMIC_INIT(0);
-atomic_t global_bh_count;
-
-/*
- * "global_cli()" is a special case, in that it can hold the
- * interrupts disabled for a longish time, and also because
- * we may be doing TLB invalidates when holding the global
- * IRQ lock for historical reasons. Thus we may need to check
- * SMP invalidate events specially by hand here (but not in
- * any normal spinlocks)
- *
- * Thankfully we don't need this as we can deliver flush tlbs with
- * interrupts disabled DJB :-)
- */
-#define check_smp_invalidate(cpu)
-
-extern void show_stack(unsigned long* esp);
-
-static void show(char * str)
-{
-	int cpu = smp_processor_id();
-
-	printk("\n%s, CPU %d:\n", str, cpu);
-	printk("irq:  %d [%d]\n",
-	       atomic_read(&global_irq_count),local_irq_count(smp_processor_id()));
-	printk("bh:   %d [%d]\n",
-	       atomic_read(&global_bh_count),local_bh_count(smp_processor_id()));
-	show_stack(NULL);
-}
-
-#define MAXCOUNT 100000000
-
-static inline void wait_on_bh(void)
-{
-	int count = MAXCOUNT;
-	do {
-		if (!--count) {
-			show("wait_on_bh");
-			count = ~0;
-		}
-		/* nothing .. wait for the other bh's to go away */
-	} while (atomic_read(&global_bh_count) != 0);
-}
-
-static inline void wait_on_irq(int cpu)
-{
-	int count = MAXCOUNT;
-
-	for (;;) {
-
-		/*
-		 * Wait until all interrupts are gone. Wait
-		 * for bottom half handlers unless we're
-		 * already executing in one..
-		 */
-		if (!atomic_read(&global_irq_count)) {
-			if (local_bh_count(cpu)||
-			    !atomic_read(&global_bh_count))
-				break;
-		}
-
-		/* Duh, we have to loop. Release the lock to avoid deadlocks */
-		atomic_set(&global_irq_lock, 0);
-
-		for (;;) {
-			if (!--count) {
-				show("wait_on_irq");
-				count = ~0;
-			}
-			local_irq_enable();
-			SYNC_OTHER_CORES(cpu);
-			local_irq_disable();
-			check_smp_invalidate(cpu);
-			if (atomic_read(&global_irq_count))
-				continue;
-			if (atomic_read(&global_irq_lock))
-				continue;
-			if (!local_bh_count(cpu)
-			    && atomic_read(&global_bh_count))
-				continue;
-			if (!atomic_compare_and_swap(0, 1, &global_irq_lock))
-				 break;
-		}
-	}
-}
-
-/*
- * This is called when we want to synchronize with
- * bottom half handlers. We need to wait until
- * no other CPU is executing any bottom half handler.
- *
- * Don't wait if we're already running in an interrupt
- * context or are inside a bh handler.
- */
-void synchronize_bh(void)
-{
-	if (atomic_read(&global_bh_count) && !in_interrupt())
-		wait_on_bh();
-}
-
-/*
- * This is called when we want to synchronize with
- * interrupts. We may for example tell a device to
- * stop sending interrupts: but to make sure there
- * are no interrupts that are executing on another
- * CPU we need to call this function.
- */
-void synchronize_irq(void)
-{
-	if (atomic_read(&global_irq_count)) {
-		/* Stupid approach */
-		cli();
-		sti();
-	}
-}
-
-static inline void get_irqlock(int cpu)
-{
-	if (atomic_compare_and_swap(0, 1, &global_irq_lock) != 0) {
-		/* do we already hold the lock? */
-		if ( cpu == atomic_read(&global_irq_holder))
-			return;
-		/* Uhhuh.. Somebody else got it. Wait.. */
-		do {
-			check_smp_invalidate(cpu);
-		} while (atomic_compare_and_swap(0, 1, &global_irq_lock) != 0);
-	}
-	/*
-	 * We also to make sure that nobody else is running
-	 * in an interrupt context.
-	 */
-	wait_on_irq(cpu);
-
-	/*
-	 * Ok, finally..
-	 */
-	atomic_set(&global_irq_holder,cpu);
-}
-
-#define EFLAGS_I_SHIFT 25
-
-/*
- * A global "cli()" while in an interrupt context
- * turns into just a local cli(). Interrupts
- * should use spinlocks for the (very unlikely)
- * case that they ever want to protect against
- * each other.
- *
- * If we already have local interrupts disabled,
- * this will not turn a local disable into a
- * global one (problems with spinlocks: this makes
- * save_flags+cli+sti usable inside a spinlock).
- */
-void __global_cli(void)
-{
-	unsigned long flags;
-
-	local_save_flags(flags);
-	if (flags & (1 << EFLAGS_I_SHIFT)) {
-		int cpu = smp_processor_id();
-		local_irq_disable();
-		if (!in_irq())
-			get_irqlock(cpu);
-	}
-}
-
-void __global_sti(void)
-{
-
-	if (!in_irq())
-		release_irqlock(smp_processor_id());
-	local_irq_enable();
-}
-
-/*
- * SMP flags value to restore to:
- * 0 - global cli
- * 1 - global sti
- * 2 - local cli
- * 3 - local sti
- */
-unsigned long __global_save_flags(void)
-{
-	int retval;
-	int local_enabled;
-	unsigned long flags;
-
-	local_save_flags(flags);
-	local_enabled = (flags >> EFLAGS_I_SHIFT) & 1;
-	/* default to local */
-	retval = 2 + local_enabled;
-
-	/* check for global flags if we're not in an interrupt */
-	if (!in_irq())
-	{
-		if (local_enabled)
-			retval = 1;
-		if (atomic_read(&global_irq_holder)== smp_processor_id())
-			retval = 0;
-	}
-	return retval;
-}
-
-void __global_restore_flags(unsigned long flags)
-{
-	switch (flags) {
-	case 0:
-		__global_cli();
-		break;
-	case 1:
-		__global_sti();
-		break;
-	case 2:
-		local_irq_disable();
-		break;
-	case 3:
-		local_irq_enable();
-		break;
-	default:
-		printk("global_restore_flags: %08lx (%08lx)\n",
-		       flags, (&flags)[-1]);
-	}
-}
-
-#endif
-
-
-void __init init_IRQ(void)
-{
-        s390_init_IRQ();
-}
-
-
-void free_irq(unsigned int irq, void *dev_id)
-{
-   s390_free_irq( irq, dev_id);
-}
-
-
-int request_irq( unsigned int   irq,
-                 void           (*handler)(int, void *, struct pt_regs *),
-                 unsigned long  irqflags,
-                 const char    *devname,
-                 void          *dev_id)
-{
-   return( s390_request_irq( irq, handler, irqflags, devname, dev_id ) );
-
-}
-
-void init_irq_proc(void)
-{
-        /* For now, nothing... */
-}
-
-#ifdef CONFIG_SMP
-EXPORT_SYMBOL(__global_cli);
-EXPORT_SYMBOL(__global_sti);
-EXPORT_SYMBOL(__global_save_flags);
-EXPORT_SYMBOL(__global_restore_flags);
-EXPORT_SYMBOL(global_irq_holder);
-EXPORT_SYMBOL(global_irq_lock);
-EXPORT_SYMBOL(global_irq_count);
-EXPORT_SYMBOL(global_bh_count);
-#endif
-
-EXPORT_SYMBOL(global_bh_lock);
diff -Nru a/arch/s390/kernel/process.c b/arch/s390/kernel/process.c
--- a/arch/s390/kernel/process.c	Mon Oct  7 11:25:36 2002
+++ b/arch/s390/kernel/process.c	Mon Oct  7 11:25:36 2002
@@ -15,9 +15,6 @@
  * This file handles the architecture-dependent parts of process handling..
  */
 
-#define __KERNEL_SYSCALLS__
-#include <stdarg.h>
-
 #include <linux/config.h>
 #include <linux/errno.h>
 #include <linux/sched.h>
@@ -78,9 +75,10 @@
 
 	/* 
 	 * Wait for external, I/O or machine check interrupt and
-	 * switch of machine check bit after the wait has ended.
+	 * switch off machine check bit after the wait has ended.
 	 */
-	wait_psw.mask = _WAIT_PSW_MASK;
+	wait_psw.mask = PSW_KERNEL_BITS | PSW_MASK_MCHECK | PSW_MASK_WAIT |
+		PSW_MASK_IO | PSW_MASK_EXT;
 	asm volatile (
 		"    basr %0,0\n"
 		"0:  la   %0,1f-0b(%0)\n"
@@ -117,35 +115,39 @@
 
 	show_registers(regs);
 	/* Show stack backtrace if pt_regs is from kernel mode */
-	if (!(regs->psw.mask & PSW_PROBLEM_STATE))
+	if (!(regs->psw.mask & PSW_MASK_PSTATE))
 		show_trace((unsigned long *) regs->gprs[15]);
 }
 
+extern void kernel_thread_starter(void);
+__asm__(".align 4\n"
+	"kernel_thread_starter:\n"
+	"    l     15,0(8)\n"
+	"    sr    15,7\n"
+	"    stosm 24(15),3\n"
+	"    lr    2,10\n"
+	"    basr  14,9\n"
+	"    sr    2,2\n"
+	"    br    11\n");
+
 int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags)
 {
-        int clone_arg = flags | CLONE_VM;
-        int retval;
+	struct task_struct *p;
+	struct pt_regs regs;
+
+	memset(&regs, 0, sizeof(regs));
+	regs.psw.mask = PSW_KERNEL_BITS;
+	regs.psw.addr = (__u32) kernel_thread_starter | PSW_ADDR_AMODE31;
+	regs.gprs[7] = STACK_FRAME_OVERHEAD;
+	regs.gprs[8] = __LC_KERNEL_STACK;
+	regs.gprs[9] = (unsigned long) fn;
+	regs.gprs[10] = (unsigned long) arg;
+	regs.gprs[11] = (unsigned long) do_exit;
+	regs.orig_gpr2 = -1;
 
-        __asm__ __volatile__(
-                "     sr    2,2\n"
-                "     lr    3,%1\n"
-                "     l     4,%6\n"     /* load kernel stack ptr of parent */
-                "     svc   %b2\n"                     /* Linux system call*/
-                "     cl    4,%6\n"    /* compare ksp's: child or parent ? */
-                "     je    0f\n"                          /* parent - jump*/
-                "     l     15,%6\n"            /* fix kernel stack pointer*/
-                "     ahi   15,%7\n"
-                "     xc    0(96,15),0(15)\n"           /* clear save area */
-                "     lr    2,%4\n"                        /* load argument*/
-                "     lr    14,%5\n"                      /* get fn-pointer*/
-                "     basr  14,14\n"                             /* call fn*/
-                "     svc   %b3\n"                     /* Linux system call*/
-                "0:   lr    %0,2"
-                : "=a" (retval)
-                : "d" (clone_arg), "i" (__NR_clone), "i" (__NR_exit),
-                  "d" (arg), "d" (fn), "i" (__LC_KERNEL_STACK) , "i" (-STACK_FRAME_OVERHEAD)
-                : "2", "3", "4" );
-        return retval;
+	/* Ok, create the new process.. */
+	p = do_fork(flags | CLONE_VM, 0, &regs, 0, NULL);
+	return IS_ERR(p) ? PTR_ERR(p) : p->pid;
 }
 
 /*
@@ -186,20 +188,28 @@
         frame = ((struct stack_frame *)
 		 (THREAD_SIZE + (unsigned long) p->thread_info)) - 1;
         p->thread.ksp = (unsigned long) frame;
-        memcpy(&frame->childregs,regs,sizeof(struct pt_regs));
+        frame->childregs = *regs;
+	frame->childregs.gprs[2] = 0;	/* child returns 0 on fork. */
         frame->childregs.gprs[15] = new_stackp;
         frame->back_chain = frame->eos = 0;
 
-        /* new return point is ret_from_sys_call */
-        frame->gprs[8] = ((unsigned long) &ret_from_fork) | 0x80000000;
+        /* new return point is ret_from_fork */
+        frame->gprs[8] = (unsigned long) ret_from_fork;
 	/* start disabled because of schedule_tick and rq->lock being held */
 	frame->childregs.psw.mask &= ~0x03000000;
 
         /* fake return stack for resume(), don't go back to schedule */
         frame->gprs[9]  = (unsigned long) frame;
-        /* save fprs, if used in last task */
-	save_fp_regs(&p->thread.fp_regs);
+        /*
+	 * save fprs to current->thread.fp_regs to merge them with
+	 * the emulated registers and then copy the result to the child.
+	 */
+	save_fp_regs(&current->thread.fp_regs);
+	memcpy(&p->thread.fp_regs, &current->thread.fp_regs,
+	       sizeof(s390_fp_regs));
         p->thread.user_seg = __pa((unsigned long) p->mm->pgd) | _SEGMENT_TABLE;
+	/* start process with ar4 pointing to the correct address space */
+	p->thread.ar4 = get_fs().ar4;
         /* Don't copy debug registers */
         memset(&p->thread.per_info,0,sizeof(p->thread.per_info));
         return 0;
@@ -208,7 +218,7 @@
 asmlinkage int sys_fork(struct pt_regs regs)
 {
 	struct task_struct *p;
-        p = do_fork(SIGCHLD, regs.gprs[15], &regs, 0);
+        p = do_fork(SIGCHLD, regs.gprs[15], &regs, 0, NULL);
 	return IS_ERR(p) ? PTR_ERR(p) : p->pid;
 }
 
@@ -217,12 +227,14 @@
         unsigned long clone_flags;
         unsigned long newsp;
 	struct task_struct *p;
+	int *user_tid;
 
         clone_flags = regs.gprs[3];
         newsp = regs.orig_gpr2;
+	user_tid = (int *) regs.gprs[4];
         if (!newsp)
                 newsp = regs.gprs[15];
-        p = do_fork(clone_flags & ~CLONE_IDLETASK, newsp, &regs, 0);
+        p = do_fork(clone_flags & ~CLONE_IDLETASK, newsp, &regs, 0, user_tid);
 	return IS_ERR(p) ? PTR_ERR(p) : p->pid;
 }
 
@@ -239,7 +251,8 @@
 asmlinkage int sys_vfork(struct pt_regs regs)
 {
 	struct task_struct *p;
-	p = do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs.gprs[15], &regs, 0);
+	p = do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD,
+		    regs.gprs[15], &regs, 0, NULL);
 	return IS_ERR(p) ? PTR_ERR(p) : p->pid;
 }
 
@@ -255,20 +268,13 @@
         error = PTR_ERR(filename);
         if (IS_ERR(filename))
                 goto out;
-        error = do_execve(filename, (char **) regs.gprs[3], (char **) regs.gprs[4], &regs);
-	if (error == 0)
-	{
+        error = do_execve(filename, (char **) regs.gprs[3],
+			  (char **) regs.gprs[4], &regs);
+	if (error == 0) {
 		current->ptrace &= ~PT_DTRACE;
-		current->thread.fp_regs.fpc=0;
-		if(MACHINE_HAS_IEEE)
-		{
-			__asm__ __volatile__
-			("sr  0,0\n\t"
-			 "sfpc 0,0\n\t"
-				:
-			        :
-                                :"0");
-		}
+		current->thread.fp_regs.fpc = 0;
+		if (MACHINE_HAS_IEEE)
+			asm volatile("sfpc %0,%0" : : "d" (0));
 	}
         putname(filename);
 out:
@@ -281,7 +287,12 @@
  */
 int dump_fpu (struct pt_regs * regs, s390_fp_regs *fpregs)
 {
-	save_fp_regs(fpregs);
+        /*
+	 * save fprs to current->thread.fp_regs to merge them with
+	 * the emulated registers and then copy the result to the dump.
+	 */
+	save_fp_regs(&current->thread.fp_regs);
+	memcpy(fpregs, &current->thread.fp_regs, sizeof(s390_fp_regs));
 	return 1;
 }
 
@@ -295,15 +306,15 @@
 	dump->magic = CMAGIC;
 	dump->start_code = 0;
 	dump->start_stack = regs->gprs[15] & ~(PAGE_SIZE - 1);
-	dump->u_tsize = ((unsigned long) current->mm->end_code) >> PAGE_SHIFT;
-	dump->u_dsize = ((unsigned long) (current->mm->brk + (PAGE_SIZE-1))) >> PAGE_SHIFT;
+	dump->u_tsize = current->mm->end_code >> PAGE_SHIFT;
+	dump->u_dsize = (current->mm->brk + PAGE_SIZE - 1) >> PAGE_SHIFT;
 	dump->u_dsize -= dump->u_tsize;
 	dump->u_ssize = 0;
 	if (dump->start_stack < TASK_SIZE)
-		dump->u_ssize = ((unsigned long) (TASK_SIZE - dump->start_stack)) >> PAGE_SHIFT;
-	memcpy(&dump->regs.gprs[0],regs,sizeof(s390_regs));
+		dump->u_ssize = (TASK_SIZE - dump->start_stack) >> PAGE_SHIFT;
+	memcpy(&dump->regs, regs, sizeof(s390_regs));
 	dump_fpu (regs, &dump->regs.fp_regs);
-	memcpy(&dump->regs.per_info,&current->thread.per_info,sizeof(per_struct));
+	dump->regs.per_info = current->thread.per_info;
 }
 
 /*
diff -Nru a/arch/s390/kernel/ptrace.c b/arch/s390/kernel/ptrace.c
--- a/arch/s390/kernel/ptrace.c	Mon Oct  7 11:25:38 2002
+++ b/arch/s390/kernel/ptrace.c	Mon Oct  7 11:25:38 2002
@@ -4,6 +4,7 @@
  *  S390 version
  *    Copyright (C) 1999,2000 IBM Deutschland Entwicklung GmbH, IBM Corporation
  *    Author(s): Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com),
+ *               Martin Schwidefsky (schwidefsky@de.ibm.com)
  *
  *  Based on PowerPC version 
  *    Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org)
@@ -21,7 +22,6 @@
  * this archive for more details.
  */
 
-#include <stddef.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
 #include <linux/mm.h>
@@ -39,266 +39,216 @@
 #include <asm/uaccess.h>
 
 
-void FixPerRegisters(struct task_struct *task)
+static void FixPerRegisters(struct task_struct *task)
 {
-	struct pt_regs *regs = __KSTK_PTREGS(task);
-	per_struct *per_info=
-			(per_struct *)&task->thread.per_info;
+	struct pt_regs *regs;
+	per_struct *per_info;
 
-	per_info->control_regs.bits.em_instruction_fetch=
-	per_info->single_step|per_info->instruction_fetch;
+	regs = __KSTK_PTREGS(task);
+	per_info = (per_struct *) &task->thread.per_info;
+	per_info->control_regs.bits.em_instruction_fetch =
+		per_info->single_step | per_info->instruction_fetch;
 	
-	if(per_info->single_step)
-	{
-		per_info->control_regs.bits.starting_addr=0;
-		per_info->control_regs.bits.ending_addr=0x7fffffffUL;
-	}
-	else
-	{
-		per_info->control_regs.bits.starting_addr=
-		per_info->starting_addr;
-		per_info->control_regs.bits.ending_addr=
-		per_info->ending_addr;
-	}
-	/* if any of the control reg tracing bits are on 
-	   we switch on per in the psw */
-	if(per_info->control_regs.words.cr[0]&PER_EM_MASK)
-		regs->psw.mask |=PSW_PER_MASK;
+	if (per_info->single_step) {
+		per_info->control_regs.bits.starting_addr = 0;
+		per_info->control_regs.bits.ending_addr = 0x7fffffffUL;
+	} else {
+		per_info->control_regs.bits.starting_addr =
+			per_info->starting_addr;
+		per_info->control_regs.bits.ending_addr =
+			per_info->ending_addr;
+	}
+	/*
+	 * if any of the control reg tracing bits are on 
+	 * we switch on per in the psw
+	 */
+	if (per_info->control_regs.words.cr[0] & PER_EM_MASK)
+		regs->psw.mask |= PSW_MASK_PER;
 	else
-		regs->psw.mask &= ~PSW_PER_MASK;
+		regs->psw.mask &= ~PSW_MASK_PER;
+
 	if (per_info->control_regs.bits.em_storage_alteration)
-	{
-		per_info->control_regs.bits.storage_alt_space_ctl=1;
-		//((pgd_t *)__pa(task->mm->pgd))->pgd |= USER_STD_MASK;
-	}
+		per_info->control_regs.bits.storage_alt_space_ctl = 1;
 	else
-	{
-		per_info->control_regs.bits.storage_alt_space_ctl=0;
-		//((pgd_t *)__pa(task->mm->pgd))->pgd &= ~USER_STD_MASK;
-	}
+		per_info->control_regs.bits.storage_alt_space_ctl = 0;
 }
 
 void set_single_step(struct task_struct *task)
 {
-	per_struct *per_info=
-			(per_struct *)&task->thread.per_info;	
-	
-	per_info->single_step=1;  /* Single step */
+	task->thread.per_info.single_step = 1;
 	FixPerRegisters(task);
 }
 
 void clear_single_step(struct task_struct *task)
 {
-	per_struct *per_info=
-			(per_struct *)&task->thread.per_info;
-
-	per_info->single_step=0;
+	task->thread.per_info.single_step = 0;
 	FixPerRegisters(task);
 }
 
-int ptrace_usercopy(addr_t realuseraddr,addr_t copyaddr,int len,int tofromuser,int writeuser,u32 mask)
+/*
+ * Called by kernel/ptrace.c when detaching..
+ *
+ * Make sure single step bits etc are not set.
+ */
+void ptrace_disable(struct task_struct *child)
 {
-	u32  tempuser;
-	int  retval=0;
-	
-	if(writeuser&&realuseraddr==(addr_t)NULL)
-		return(0);
-	if(mask!=0xffffffff)
-	{
-		tempuser=*((u32 *)realuseraddr);
-		if(!writeuser)
-		{
-			tempuser&=mask;
-			realuseraddr=(addr_t)&tempuser;
-		}
-	}
-	if(tofromuser)
-	{
-		if(writeuser)
-		{
-			retval=copy_from_user((void *)realuseraddr,(void *)copyaddr,len) ? -EFAULT : 0;
-		}
-		else
-		{
-			if(realuseraddr==(addr_t)NULL)
-				retval=(clear_user((void *)copyaddr,len) ? -EIO:0);
-			else
-				retval=(copy_to_user((void *)copyaddr,(void *)realuseraddr,len) ? -EIO:0);
-		}      
-	}
-	else
-	{
-		if(writeuser)
-			memcpy((void *)realuseraddr,(void *)copyaddr,len);
-		else
-			memcpy((void *)copyaddr,(void *)realuseraddr,len);
-	}
-	if(mask!=0xffffffff&&writeuser)
-			(*((u32 *)realuseraddr))=(((*((u32 *)realuseraddr))&mask)|(tempuser&~mask));
-	return(retval);
+	/* make sure the single step bit is not set. */
+	clear_single_step(child);
 }
 
-int copy_user(struct task_struct *task,saddr_t useraddr,addr_t copyaddr,int len,int tofromuser,int writingtouser)
+/*
+ * Read the word at offset addr from the user area of a process. The
+ * trouble here is that the information is littered over different
+ * locations. The process registers are found on the kernel stack,
+ * the floating point stuff and the trace settings are stored in
+ * the task structure. In addition the different structures in
+ * struct user contain pad bytes that should be read as zeroes.
+ * Lovely...
+ */
+static int peek_user(struct task_struct *child, addr_t addr, addr_t data)
 {
-	int copylen=0,copymax;
-	addr_t  realuseraddr;
-	saddr_t enduseraddr=useraddr+len;
-	
-	u32 mask;
+	struct user *dummy = NULL;
+	addr_t offset;
+	__u32 tmp;
+
+	if ((addr & 3) || addr > sizeof(struct user) - 3)
+		return -EIO;
+
+	if (addr <= (addr_t) &dummy->regs.orig_gpr2) {
+		/*
+		 * psw, gprs, acrs and orig_gpr2 are stored on the stack
+		 */
+		tmp = *(__u32 *)((addr_t) __KSTK_PTREGS(child) + addr);
+
+	} else if (addr >= (addr_t) &dummy->regs.fp_regs &&
+		   addr < (addr_t) (&dummy->regs.fp_regs + 1)) {
+		/* 
+		 * floating point regs. are stored in the thread structure
+		 */
+		offset = addr - (addr_t) &dummy->regs.fp_regs;
+		tmp = *(__u32 *)((addr_t) &child->thread.fp_regs + offset);
+
+	} else if (addr >= (addr_t) &dummy->regs.per_info &&
+		   addr < (addr_t) (&dummy->regs.per_info + 1)) {
+		/*
+		 * per_info is found in the thread structure
+		 */
+		offset = addr - (addr_t) &dummy->regs.per_info;
+		tmp = *(__u32 *)((addr_t) &child->thread.per_info + offset);
 
-	if (useraddr < 0 || enduseraddr > sizeof(struct user)||
-	   (useraddr < PT_ENDREGS && (useraddr&3))||
-	   (enduseraddr < PT_ENDREGS && (enduseraddr&3)))
-		return (-EIO);
-	while(len>0)
-	{
-		mask=0xffffffff;
-		if(useraddr<PT_FPC)
-		{
-			realuseraddr=((addr_t) __KSTK_PTREGS(task)) + useraddr;
-			if(useraddr<PT_PSWMASK)
-			{
-				copymax=PT_PSWMASK;
-			}
-			else if(useraddr<(PT_PSWMASK+4))
-			{
-				copymax=(PT_PSWMASK+4);
-				if(writingtouser)
-					mask=PSW_MASK_DEBUGCHANGE;
-			}
-			else if(useraddr<(PT_PSWADDR+4))
-			{
-				copymax=PT_PSWADDR+4;
-				mask=PSW_ADDR_DEBUGCHANGE;
-			}
-			else
-				copymax=PT_FPC;
-			
-		}
-		else if(useraddr<(PT_FPR15_LO+4))
-		{
-			copymax=(PT_FPR15_LO+4);
-			realuseraddr=(addr_t)&(((u8 *)&task->thread.fp_regs)[useraddr-PT_FPC]);
-		}
-		else if(useraddr<sizeof(struct user_regs_struct))
-		{
-			copymax=sizeof(struct user_regs_struct);
-			realuseraddr=(addr_t)&(((u8 *)&task->thread.per_info)[useraddr-PT_CR_9]);
-		}
-		else 
-		{
-			copymax=sizeof(struct user);
-			realuseraddr=(addr_t)NULL;
-		}
-		copylen=copymax-useraddr;
-		copylen=(copylen>len ? len:copylen);
-		if(ptrace_usercopy(realuseraddr,copyaddr,copylen,tofromuser,writingtouser,mask))
-			return (-EIO);
-		copyaddr+=copylen;
-		len-=copylen;
-		useraddr+=copylen;
-	}
-	FixPerRegisters(task);
-	return(0);
+	} else
+		tmp = 0;
+
+	return put_user(tmp, (__u32 *) data);
 }
 
 /*
- * Called by kernel/ptrace.c when detaching..
- *
- * Make sure single step bits etc are not set.
+ * Write a word to the user area of a process at location addr. This
+ * operation does have an additional problem compared to peek_user.
+ * Stores to the program status word and on the floating point
+ * control register needs to get checked for validity.
  */
-void ptrace_disable(struct task_struct *child)
+static int poke_user(struct task_struct *child, addr_t addr, addr_t data)
 {
-	/* make sure the single step bit is not set. */
-	clear_single_step(child);
+	struct user *dummy = NULL;
+	addr_t offset;
+
+	if ((addr & 3) || addr > sizeof(struct user) - 3)
+		return -EIO;
+
+	if (addr <= (addr_t) &dummy->regs.orig_gpr2) {
+		/*
+		 * psw, gprs, acrs and orig_gpr2 are stored on the stack
+		 */
+		if (addr == (addr_t) &dummy->regs.psw.mask &&
+		    (data & ~PSW_MASK_CC) != PSW_USER_BITS)
+			/* Invalid psw mask. */
+			return -EINVAL;
+		if (addr == (addr_t) &dummy->regs.psw.addr)
+			/* I'd like to reject addresses without the
+			   high order bit but older gdb's rely on it */
+			data |= PSW_ADDR_AMODE31;
+		*(__u32 *)((addr_t) __KSTK_PTREGS(child) + addr) = data;
+
+	} else if (addr >= (addr_t) &dummy->regs.fp_regs &&
+		   addr < (addr_t) (&dummy->regs.fp_regs + 1)) {
+		/*
+		 * floating point regs. are stored in the thread structure
+		 */
+		if (addr == (addr_t) &dummy->regs.fp_regs.fpc &&
+		    (data & ~FPC_VALID_MASK) != 0)
+			return -EINVAL;
+		offset = addr - (addr_t) &dummy->regs.fp_regs;
+		*(__u32 *)((addr_t) &child->thread.fp_regs + offset) = data;
+
+	} else if (addr >= (addr_t) &dummy->regs.per_info &&
+		   addr < (addr_t) (&dummy->regs.per_info + 1)) {
+		/*
+		 * per_info is found in the thread structure 
+		 */
+		offset = addr - (addr_t) &dummy->regs.per_info;
+		*(__u32 *)((addr_t) &child->thread.per_info + offset) = data;
+
+	}
+
+	FixPerRegisters(child);
+	return 0;
 }
 
-asmlinkage int sys_ptrace(long request, long pid, long addr, long data)
+static int
+do_ptrace(struct task_struct *child, long request, long addr, long data)
 {
-	struct task_struct *child;
-	int ret = -EPERM;
 	unsigned long tmp;
-	int copied;
-	ptrace_area   parea; 
+	ptrace_area parea; 
+	int copied, ret;
 
-	lock_kernel();
-	if (request == PTRACE_TRACEME) 
-	{
-		/* are we already being traced? */
-		if (current->ptrace & PT_PTRACED)
-			goto out;
-		/* set the ptrace bit in the process flags. */
-		current->ptrace |= PT_PTRACED;
-		ret = 0;
-		goto out;
-	}
-	ret = -ESRCH;
-	read_lock(&tasklist_lock);
-	child = find_task_by_pid(pid);
-	if (child)
-		get_task_struct(child);
-	read_unlock(&tasklist_lock);
-	if (!child)
-		goto out;
-	ret = -EPERM;
-	if (pid == 1)		/* you may not mess with init */
-		goto out_tsk;
-	if (request == PTRACE_ATTACH) 
-	{
-		ret = ptrace_attach(child);
-		goto out_tsk;
-	}
-	ret = -ESRCH;
-	// printk("child=%lX child->flags=%lX",child,child->flags);
-	/* I added child!=current line so we can get the */
-	/* ieee_instruction_pointer from the user structure DJB */
-	if(child!=current)
-	{
-		if (!(child->ptrace & PT_PTRACED))
-			goto out_tsk;
-		if (child->state != TASK_STOPPED) 
-		{
-			if (request != PTRACE_KILL)
-				goto out_tsk;
-		}
-		if (child->parent != current)
-			goto out_tsk;
-	}
-	switch (request) 
-	{
-		/* If I and D space are separate, these will need to be fixed. */
-	case PTRACE_PEEKTEXT: /* read word at location addr. */ 
-	case PTRACE_PEEKDATA: 
-		copied = access_process_vm(child,ADDR_BITS_REMOVE(addr), &tmp, sizeof(tmp), 0);
-		ret = -EIO;
+	if (request == PTRACE_ATTACH)
+		return ptrace_attach(child);
+
+	/*
+	 * I added child != current line so we can get the
+	 * ieee_instruction_pointer from the user structure DJB
+	 */
+	if (child != current) {
+		ret = ptrace_check_attach(child, request == PTRACE_KILL);
+		if (ret < 0)
+			return ret;
+	}
+
+	/* Remove high order bit from address. */
+	addr &= PSW_ADDR_INSN;
+
+	switch (request) {
+	case PTRACE_PEEKTEXT:
+	case PTRACE_PEEKDATA:
+		/* read word at location addr. */
+		copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0);
 		if (copied != sizeof(tmp))
-			break;
-		ret = put_user(tmp,(unsigned long *) data);
-		break;
+			return -EIO;
+		return put_user(tmp, (unsigned long *) data);
 
-		/* read the word at location addr in the USER area. */
 	case PTRACE_PEEKUSR:
-		ret=copy_user(child,addr,data,sizeof(unsigned long),1,0);
-		break;
+		/* read the word at location addr in the USER area. */
+		return peek_user(child, addr, data);
 
-		/* If I and D space are separate, this will have to be fixed. */
-	case PTRACE_POKETEXT: /* write the word at location addr. */
+	case PTRACE_POKETEXT:
 	case PTRACE_POKEDATA:
-		ret = 0;
-		if (access_process_vm(child,ADDR_BITS_REMOVE(addr), &data, sizeof(data), 1) == sizeof(data))
-			break;
-		ret = -EIO;
-		break;
-
-	case PTRACE_POKEUSR: /* write the word at location addr in the USER area */
-		ret=copy_user(child,addr,(addr_t)&data,sizeof(unsigned long),0,1);
-		break;
-
-	case PTRACE_SYSCALL: 	/* continue and stop at next (return from) syscall */
-	case PTRACE_CONT: 	 /* restart after signal. */
-		ret = -EIO;
+		/* write the word at location addr. */
+		copied = access_process_vm(child, addr, &data, sizeof(data),1);
+		if (copied != sizeof(data))
+			return -EIO;
+		return 0;
+
+	case PTRACE_POKEUSR:
+		/* write the word at location addr in the USER area */
+		return poke_user(child, addr, data);
+
+	case PTRACE_SYSCALL:
+		/* continue and stop at next (return from) syscall */
+	case PTRACE_CONT:
+		/* restart after signal. */
 		if ((unsigned long) data >= _NSIG)
-			break;
+			return -EIO;
 		if (request == PTRACE_SYSCALL)
 			set_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
 		else
@@ -307,60 +257,104 @@
 		/* make sure the single step bit is not set. */
 		clear_single_step(child);
 		wake_up_process(child);
-		ret = 0;
-		break;
+		return 0;
 
-/*
- * make the child exit.  Best I can do is send it a sigkill. 
- * perhaps it should be put in the status that it wants to 
- * exit.
- */
 	case PTRACE_KILL:
-		ret = 0;
+		/*
+		 * make the child exit.  Best I can do is send it a sigkill. 
+		 * perhaps it should be put in the status that it wants to 
+		 * exit.
+		 */
 		if (child->state == TASK_ZOMBIE) /* already dead */
-			break;
+			return 0;
 		child->exit_code = SIGKILL;
+		/* make sure the single step bit is not set. */
 		clear_single_step(child);
 		wake_up_process(child);
-		/* make sure the single step bit is not set. */
-		break;
+		return 0;
 
-	case PTRACE_SINGLESTEP:  /* set the trap flag. */
-		ret = -EIO;
+	case PTRACE_SINGLESTEP:
+		/* set the trap flag. */
 		if ((unsigned long) data >= _NSIG)
-			break;
+			return -EIO;
 		clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
 		child->exit_code = data;
 		set_single_step(child);
 		/* give it a chance to run. */
 		wake_up_process(child);
-		ret = 0;
-		break;
+		return 0;
+
+	case PTRACE_DETACH:
+		/* detach a process that was attached. */
+		return ptrace_detach(child, data);
 
-	case PTRACE_DETACH:  /* detach a process that was attached. */
-		ret = ptrace_detach(child, data);
-		break;
 	case PTRACE_PEEKUSR_AREA:
 	case PTRACE_POKEUSR_AREA:
-		if((ret=copy_from_user(&parea,(void *)addr,sizeof(parea)))==0)  
-		   ret=copy_user(child,parea.kernel_addr,parea.process_addr,
-				 parea.len,1,(request==PTRACE_POKEUSR_AREA));
-		break;
-	case PTRACE_SETOPTIONS: {
+		if (!copy_from_user(&parea, (void *) addr, sizeof(parea)))
+			return -EFAULT;
+		addr = parea.kernel_addr;
+		data = parea.process_addr;
+		copied = 0;
+		while (copied < parea.len) {
+			if (request == PTRACE_PEEKUSR_AREA)
+				ret = peek_user(child, addr, data);
+			else
+				ret = poke_user(child, addr, data);
+			if (ret)
+				return ret;
+			addr += sizeof(unsigned long);
+			data += sizeof(unsigned long);
+			copied += sizeof(unsigned long);
+		}
+		return 0;
+
+	case PTRACE_SETOPTIONS:
 		if (data & PTRACE_O_TRACESYSGOOD)
 			child->ptrace |= PT_TRACESYSGOOD;
 		else
 			child->ptrace &= ~PT_TRACESYSGOOD;
-		ret = 0;
-		break;
+		return 0;
 	}
-	default:
-		ret = -EIO;
-		break;
+	return -EIO;
+}
+
+asmlinkage int sys_ptrace(long request, long pid, long addr, long data)
+{
+	struct task_struct *child;
+	int ret;
+
+	lock_kernel();
+
+	if (request == PTRACE_TRACEME) {
+		/* are we already being traced? */
+		ret = -EPERM;
+		if (current->ptrace & PT_PTRACED)
+			goto out;
+		ret = security_ops->ptrace(current->parent, current);
+		if (ret)
+			goto out;
+		/* set the ptrace bit in the process flags. */
+		current->ptrace |= PT_PTRACED;
+		goto out;
 	}
- out_tsk:
+
+	ret = -EPERM;
+	if (pid == 1)		/* you may not mess with init */
+		goto out;
+
+	ret = -ESRCH;
+	read_lock(&tasklist_lock);
+	child = find_task_by_pid(pid);
+	if (child)
+		get_task_struct(child);
+	read_unlock(&tasklist_lock);
+	if (!child)
+		goto out;
+
+	ret = do_ptrace(child, request, addr, data);
+
 	put_task_struct(child);
- out:
+out:
 	unlock_kernel();
 	return ret;
 }
@@ -371,8 +365,8 @@
 		return;
 	if (!(current->ptrace & PT_PTRACED))
 		return;
-	current->exit_code = SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD)
-					? 0x80 : 0);
+	current->exit_code =
+		SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) ? 0x80 : 0);
 	current->state = TASK_STOPPED;
 	notify_parent(current, SIGCHLD);
 	schedule();
diff -Nru a/arch/s390/kernel/s390fpu.c b/arch/s390/kernel/s390fpu.c
--- a/arch/s390/kernel/s390fpu.c	Mon Oct  7 11:25:25 2002
+++ /dev/null	Wed Dec 31 16:00:00 1969
@@ -1,138 +0,0 @@
-/*
- *  arch/s390/kernel/s390fpu.c
- *
- *  S390 version
- *    Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation
- *    Author(s): Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com)
- *
- *  s390fpu.h functions for saving & restoring the fpu state.
- *
- *  I couldn't inline these as linux/sched.h included half the world
- *  & was required to at the task structure.
- *  & the functions were too complex to make macros from.
- *  ( & as usual I didn't feel like debugging inline code ).
- */
-
-#include <linux/config.h>
-#include <linux/sched.h>
-
-int save_fp_regs1(s390_fp_regs *fpregs)
-{
-	int has_ieee=MACHINE_HAS_IEEE;
-/*
-  I don't think we can use STE here as this would load
-  fp registers 0 & 2 into memory locations 0 & 1 etc. 
- */	
-	asm volatile ("STD   0,8(%0)\n\t"
-		      "STD   2,24(%0)\n\t"
-		      "STD   4,40(%0)\n\t"
-		      "STD   6,56(%0)"
-                      : 
-		      : "a" (fpregs)
-		      : "memory"
-		);
-	if(has_ieee)
-	{
-		asm volatile ("STFPC 0(%0)\n\t"
-			      "STD   1,16(%0)\n\t"
-			      "STD   3,32(%0)\n\t"
-			      "STD   5,48(%0)\n\t"
-			      "STD   7,64(%0)\n\t"
-			      "STD   8,72(%0)\n\t"
-			      "STD   9,80(%0)\n\t"
-			      "STD   10,88(%0)\n\t"
-			      "STD   11,96(%0)\n\t"
-			      "STD   12,104(%0)\n\t"
-			      "STD   13,112(%0)\n\t"
-			      "STD   14,120(%0)\n\t"
-			      "STD   15,128(%0)\n\t"
-			      : 
-			      : "a" (fpregs)
-			      : "memory"
-			);
-	}
-	return(has_ieee);
-}
-
-
-void save_fp_regs(s390_fp_regs *fpregs)
-{
-#if CONFIG_MATHEMU
-	s390_fp_regs *currentfprs;
-
-	if(!save_fp_regs1(fpregs))
-	{
-		currentfprs=&current->thread.fp_regs;
-		fpregs->fpc=currentfprs->fpc;
-		fpregs->fprs[1].d=currentfprs->fprs[1].d;
-		fpregs->fprs[3].d=currentfprs->fprs[3].d;
-		fpregs->fprs[5].d=currentfprs->fprs[5].d;
-		fpregs->fprs[7].d=currentfprs->fprs[7].d;
-		memcpy(&fpregs->fprs[8].d,&currentfprs->fprs[8].d,sizeof(freg_t)*8);
-	}
-#else
-	save_fp_regs1(fpregs);
-#endif
-}
-
-
-int restore_fp_regs1(s390_fp_regs *fpregs)
-{
-	int has_ieee=MACHINE_HAS_IEEE;
-
-	/* If we don't mask with the FPC_VALID_MASK here
-	 * we've got a very quick shutdown -h now command
-         * via a kernel specification exception.
-	 */
-	fpregs->fpc&=FPC_VALID_MASK;
-	asm volatile ("LD   0,8(%0)\n\t"
-		      "LD   2,24(%0)\n\t"
-		      "LD   4,40(%0)\n\t"
-		      "LD   6,56(%0)"
-                      : 
-		      : "a" (fpregs)
-		      : "memory"
-		);
-	if(has_ieee)
-	{
-		asm volatile ("LFPC 0(%0)\n\t"
-			      "LD   1,16(%0)\n\t"
-			      "LD   3,32(%0)\n\t"
-			      "LD   5,48(%0)\n\t"
-			      "LD   7,64(%0)\n\t"
-			      "LD   8,72(%0)\n\t"
-			      "LD   9,80(%0)\n\t"
-			      "LD   10,88(%0)\n\t"
-			      "LD   11,96(%0)\n\t"
-			      "LD   12,104(%0)\n\t"
-			      "LD   13,112(%0)\n\t"
-			      "LD   14,120(%0)\n\t"
-			      "LD   15,128(%0)\n\t"
-			      : 
-			      : "a" (fpregs)
-			      : "memory"
-			);
-	}
-	return(has_ieee);
-}
-
-void restore_fp_regs(s390_fp_regs *fpregs)
-{
-#if CONFIG_MATHEMU
-	s390_fp_regs *currentfprs;
-
-	if(!restore_fp_regs1(fpregs))
-	{
-		currentfprs=&current->thread.fp_regs;
-		currentfprs->fpc=fpregs->fpc;
-		currentfprs->fprs[1].d=fpregs->fprs[1].d;
-		currentfprs->fprs[3].d=fpregs->fprs[3].d;
-		currentfprs->fprs[5].d=fpregs->fprs[5].d;
-		currentfprs->fprs[7].d=fpregs->fprs[7].d;
-		memcpy(&currentfprs->fprs[8].d,&fpregs->fprs[8].d,sizeof(freg_t)*8);
-	}
-#else
-	restore_fp_regs1(fpregs);
-#endif
-}
-
diff -Nru a/arch/s390/kernel/setup.c b/arch/s390/kernel/setup.c
--- a/arch/s390/kernel/setup.c	Mon Oct  7 11:25:25 2002
+++ b/arch/s390/kernel/setup.c	Mon Oct  7 11:25:25 2002
@@ -52,7 +52,6 @@
 struct { unsigned long addr, size, type; } memory_chunk[16] = { { 0 } };
 #define CHUNK_READ_WRITE 0
 #define CHUNK_READ_ONLY 1
-__u16 boot_cpu_addr;
 int cpus_initialized = 0;
 unsigned long cpu_initialized = 0;
 volatile int __cpu_logical_map[NR_CPUS]; /* logical cpu to cpu address */
@@ -79,7 +78,7 @@
 /*
  * cpu_init() initializes state that is per-CPU.
  */
-void __init cpu_init (void)
+void __devinit cpu_init (void)
 {
         int nr = smp_processor_id();
         int addr = hard_smp_processor_id();
@@ -305,7 +304,7 @@
 	unsigned long start_pfn, end_pfn;
         static unsigned int smptrap=0;
         unsigned long delay = 0;
-	struct _lowcore *lowcore;
+	struct _lowcore *lc;
 	int i;
 
         if (smptrap)
@@ -452,30 +451,28 @@
         /*
          * Setup lowcore for boot cpu
          */
-	lowcore = (struct _lowcore *)
-		__alloc_bootmem(PAGE_SIZE, PAGE_SIZE, 0);
-	memset(lowcore, 0, PAGE_SIZE);
-	lowcore->restart_psw.mask = _RESTART_PSW_MASK;
-	lowcore->restart_psw.addr = _ADDR_31 + (addr_t) &restart_int_handler;
-	lowcore->external_new_psw.mask = _EXT_PSW_MASK;
-	lowcore->external_new_psw.addr = _ADDR_31 + (addr_t) &ext_int_handler;
-	lowcore->svc_new_psw.mask = _SVC_PSW_MASK;
-	lowcore->svc_new_psw.addr = _ADDR_31 + (addr_t) &system_call;
-	lowcore->program_new_psw.mask = _PGM_PSW_MASK;
-	lowcore->program_new_psw.addr = _ADDR_31 + (addr_t) &pgm_check_handler;
-        lowcore->mcck_new_psw.mask = _MCCK_PSW_MASK;
-	lowcore->mcck_new_psw.addr = _ADDR_31 + (addr_t) &mcck_int_handler;
-	lowcore->io_new_psw.mask = _IO_PSW_MASK;
-	lowcore->io_new_psw.addr = _ADDR_31 + (addr_t) &io_int_handler;
-	lowcore->ipl_device = S390_lowcore.ipl_device;
-	lowcore->kernel_stack = ((__u32) &init_thread_union) + 8192;
-	lowcore->async_stack = (__u32)
+	lc = (struct _lowcore *) __alloc_bootmem(PAGE_SIZE, PAGE_SIZE, 0);
+	memset(lc, 0, PAGE_SIZE);
+	lc->restart_psw.mask = PSW_BASE_BITS;
+	lc->restart_psw.addr = PSW_ADDR_AMODE31 + (__u32) restart_int_handler;
+	lc->external_new_psw.mask = PSW_KERNEL_BITS;
+	lc->external_new_psw.addr = PSW_ADDR_AMODE31 + (__u32) ext_int_handler;
+	lc->svc_new_psw.mask = PSW_KERNEL_BITS;
+	lc->svc_new_psw.addr = PSW_ADDR_AMODE31 + (__u32) system_call;
+	lc->program_new_psw.mask = PSW_KERNEL_BITS;
+	lc->program_new_psw.addr = PSW_ADDR_AMODE31 + (__u32)pgm_check_handler;
+        lc->mcck_new_psw.mask = PSW_KERNEL_BITS;
+	lc->mcck_new_psw.addr = PSW_ADDR_AMODE31 + (__u32) mcck_int_handler;
+	lc->io_new_psw.mask = PSW_KERNEL_BITS;
+	lc->io_new_psw.addr = PSW_ADDR_AMODE31 + (__u32) io_int_handler;
+	lc->ipl_device = S390_lowcore.ipl_device;
+	lc->kernel_stack = ((__u32) &init_thread_union) + 8192;
+	lc->async_stack = (__u32)
 		__alloc_bootmem(2*PAGE_SIZE, 2*PAGE_SIZE, 0) + 8192;
-	lowcore->jiffy_timer = -1LL;
-	set_prefix((__u32) lowcore);
+	lc->jiffy_timer = -1LL;
+	set_prefix((__u32) lc);
         cpu_init();
-        boot_cpu_addr = S390_lowcore.cpu_data.cpu_addr;
-        __cpu_logical_map[0] = boot_cpu_addr;
+        __cpu_logical_map[0] = S390_lowcore.cpu_data.cpu_addr;
 
 	/*
 	 * Create kernel page tables and switch to virtual addressing.
@@ -524,11 +521,14 @@
 		seq_printf(m, "vendor_id       : IBM/S390\n"
 			       "# processors    : %i\n"
 			       "bogomips per cpu: %lu.%02lu\n",
-			       smp_num_cpus, loops_per_jiffy/(500000/HZ),
+			       num_online_cpus(), loops_per_jiffy/(500000/HZ),
 			       (loops_per_jiffy/(5000/HZ))%100);
 	}
 	if (cpu_online_map & (1 << n)) {
-		cpuinfo = &safe_get_cpu_lowcore(n)->cpu_data;
+		if (smp_processor_id() == n)
+			cpuinfo = &S390_lowcore.cpu_data;
+		else
+			cpuinfo = &lowcore_ptr[n]->cpu_data;
 		seq_printf(m, "processor %li: "
 			       "version = %02X,  "
 			       "identification = %06X,  "
diff -Nru a/arch/s390/kernel/signal.c b/arch/s390/kernel/signal.c
--- a/arch/s390/kernel/signal.c	Mon Oct  7 11:25:27 2002
+++ b/arch/s390/kernel/signal.c	Mon Oct  7 11:25:27 2002
@@ -49,22 +49,23 @@
 	struct ucontext uc;
 } rt_sigframe;
 
-asmlinkage int FASTCALL(do_signal(struct pt_regs *regs, sigset_t *oldset));
+int do_signal(struct pt_regs *regs, sigset_t *oldset);
 
 /*
  * Atomically swap in the new signal mask, and wait for a signal.
  */
 asmlinkage int
-sys_sigsuspend(struct pt_regs * regs,int history0, int history1, old_sigset_t mask)
+sys_sigsuspend(struct pt_regs * regs, int history0, int history1,
+	       old_sigset_t mask)
 {
 	sigset_t saveset;
 
 	mask &= _BLOCKABLE;
-	spin_lock_irq(&current->sigmask_lock);
+	spin_lock_irq(&current->sig->siglock);
 	saveset = current->blocked;
 	siginitset(&current->blocked, mask);
 	recalc_sigpending();
-	spin_unlock_irq(&current->sigmask_lock);
+	spin_unlock_irq(&current->sig->siglock);
 	regs->gprs[2] = -EINTR;
 
 	while (1) {
@@ -88,11 +89,11 @@
 		return -EFAULT;
 	sigdelsetmask(&newset, ~_BLOCKABLE);
 
-	spin_lock_irq(&current->sigmask_lock);
+	spin_lock_irq(&current->sig->siglock);
 	saveset = current->blocked;
 	current->blocked = newset;
 	recalc_sigpending();
-	spin_unlock_irq(&current->sigmask_lock);
+	spin_unlock_irq(&current->sig->siglock);
 	regs->gprs[2] = -EINTR;
 
 	while (1) {
@@ -147,37 +148,39 @@
 static int save_sigregs(struct pt_regs *regs,_sigregs *sregs)
 {
 	int err;
-	s390_fp_regs fpregs;
   
-	err = __copy_to_user(&sregs->regs,regs,sizeof(_s390_regs_common));
-	if(!err)
-	{
-		save_fp_regs(&fpregs);
-		err=__copy_to_user(&sregs->fpregs,&fpregs,sizeof(fpregs));
-	}
-	return(err);
-	
+	err = __copy_to_user(&sregs->regs, regs, sizeof(_s390_regs_common));
+	if (err != 0)
+		return err;
+	/* 
+	 * We have to store the fp registers to current->thread.fp_regs
+	 * to merge them with the emulated registers.
+	 */
+	save_fp_regs(&current->thread.fp_regs);
+	return __copy_to_user(&sregs->fpregs, &current->thread.fp_regs, 
+			      sizeof(s390_fp_regs));
 }
 
 /* Returns positive number on error */
 static int restore_sigregs(struct pt_regs *regs,_sigregs *sregs)
 {
 	int err;
-	s390_fp_regs fpregs;
-	psw_t saved_psw=regs->psw;
-	err=__copy_from_user(regs,&sregs->regs,sizeof(_s390_regs_common));
-	if(!err)
-	{
-		regs->trap = -1;		/* disable syscall checks */
-		regs->psw.mask=(saved_psw.mask&~PSW_MASK_DEBUGCHANGE)|
-		(regs->psw.mask&PSW_MASK_DEBUGCHANGE);
-		regs->psw.addr=(saved_psw.addr&~PSW_ADDR_DEBUGCHANGE)|
-		(regs->psw.addr&PSW_ADDR_DEBUGCHANGE);
-		err=__copy_from_user(&fpregs,&sregs->fpregs,sizeof(fpregs));
-		if(!err)
-			restore_fp_regs(&fpregs);
-	}
-	return(err);
+
+	err = __copy_from_user(regs, &sregs->regs, sizeof(_s390_regs_common));
+	regs->psw.mask = PSW_USER_BITS | (regs->psw.mask & PSW_MASK_CC);
+	regs->psw.addr |= PSW_ADDR_AMODE31;
+	if (err)
+		return err;
+
+	err = __copy_from_user(&current->thread.fp_regs, &sregs->fpregs,
+			       sizeof(s390_fp_regs));
+	current->thread.fp_regs.fpc &= FPC_VALID_MASK;
+	if (err)
+		return err;
+
+	restore_fp_regs(&current->thread.fp_regs);
+	regs->trap = -1;	/* disable syscall checks */
+	return 0;
 }
 
 asmlinkage long sys_sigreturn(struct pt_regs *regs)
@@ -191,10 +194,10 @@
 		goto badframe;
 
 	sigdelsetmask(&set, ~_BLOCKABLE);
-	spin_lock_irq(&current->sigmask_lock);
+	spin_lock_irq(&current->sig->siglock);
 	current->blocked = set;
 	recalc_sigpending();
-	spin_unlock_irq(&current->sigmask_lock);
+	spin_unlock_irq(&current->sig->siglock);
 
 	if (restore_sigregs(regs, &frame->sregs))
 		goto badframe;
@@ -217,10 +220,10 @@
 		goto badframe;
 
 	sigdelsetmask(&set, ~_BLOCKABLE);
-	spin_lock_irq(&current->sigmask_lock);
+	spin_lock_irq(&current->sig->siglock);
 	current->blocked = set;
 	recalc_sigpending();
-	spin_unlock_irq(&current->sigmask_lock);
+	spin_unlock_irq(&current->sig->siglock);
 
 	if (restore_sigregs(regs, &frame->uc.uc_mcontext))
 		goto badframe;
@@ -295,9 +298,9 @@
 	/* Set up to return from userspace.  If provided, use a stub
 	   already in userspace.  */
 	if (ka->sa.sa_flags & SA_RESTORER) {
-                regs->gprs[14] = FIX_PSW(ka->sa.sa_restorer);
+                regs->gprs[14] = (__u32) ka->sa.sa_restorer | PSW_ADDR_AMODE31;
 	} else {
-                regs->gprs[14] = FIX_PSW(frame->retcode);
+                regs->gprs[14] = (__u32) frame->retcode | PSW_ADDR_AMODE31;
 		if (__put_user(S390_SYSCALL_OPCODE | __NR_sigreturn, 
 	                       (u16 *)(frame->retcode)))
 			goto give_sigsegv;
@@ -308,12 +311,12 @@
 		goto give_sigsegv;
 
 	/* Set up registers for signal handler */
-	regs->gprs[15] = (addr_t)frame;
-	regs->psw.addr = FIX_PSW(ka->sa.sa_handler);
-	regs->psw.mask = _USER_PSW_MASK;
+	regs->gprs[15] = (__u32) frame;
+	regs->psw.addr = (__u32) ka->sa.sa_handler | PSW_ADDR_AMODE31;
+	regs->psw.mask = PSW_USER_BITS;
 
 	regs->gprs[2] = map_signal(sig);
-	regs->gprs[3] = (addr_t)&frame->sc;
+	regs->gprs[3] = (__u32) &frame->sc;
 
 	/* We forgot to include these in the sigcontext.
 	   To avoid breaking binary compatibility, they are passed as args. */
@@ -353,9 +356,9 @@
 	/* Set up to return from userspace.  If provided, use a stub
 	   already in userspace.  */
 	if (ka->sa.sa_flags & SA_RESTORER) {
-                regs->gprs[14] = FIX_PSW(ka->sa.sa_restorer);
+                regs->gprs[14] = (__u32) ka->sa.sa_restorer | PSW_ADDR_AMODE31;
 	} else {
-                regs->gprs[14] = FIX_PSW(frame->retcode);
+                regs->gprs[14] = (__u32) frame->retcode | PSW_ADDR_AMODE31;
 		err |= __put_user(S390_SYSCALL_OPCODE | __NR_rt_sigreturn, 
 	                          (u16 *)(frame->retcode));
 	}
@@ -365,13 +368,13 @@
 		goto give_sigsegv;
 
 	/* Set up registers for signal handler */
-	regs->gprs[15] = (addr_t)frame;
-	regs->psw.addr = FIX_PSW(ka->sa.sa_handler);
-	regs->psw.mask = _USER_PSW_MASK;
+	regs->gprs[15] = (__u32) frame;
+	regs->psw.addr = (__u32) ka->sa.sa_handler | PSW_ADDR_AMODE31;
+	regs->psw.mask = PSW_USER_BITS;
 
 	regs->gprs[2] = map_signal(sig);
-	regs->gprs[3] = (addr_t)&frame->info;
-	regs->gprs[4] = (addr_t)&frame->uc;
+	regs->gprs[3] = (__u32) &frame->info;
+	regs->gprs[4] = (__u32) &frame->uc;
 	return;
 
 give_sigsegv:
@@ -420,11 +423,11 @@
 		ka->sa.sa_handler = SIG_DFL;
 
 	if (!(ka->sa.sa_flags & SA_NODEFER)) {
-		spin_lock_irq(&current->sigmask_lock);
+		spin_lock_irq(&current->sig->siglock);
 		sigorsets(&current->blocked,&current->blocked,&ka->sa.sa_mask);
 		sigaddset(&current->blocked,sig);
 		recalc_sigpending();
-		spin_unlock_irq(&current->sigmask_lock);
+		spin_unlock_irq(&current->sig->siglock);
 	}
 }
 
diff -Nru a/arch/s390/kernel/smp.c b/arch/s390/kernel/smp.c
--- a/arch/s390/kernel/smp.c	Mon Oct  7 11:25:25 2002
+++ b/arch/s390/kernel/smp.c	Mon Oct  7 11:25:25 2002
@@ -41,51 +41,21 @@
 /* prototypes */
 extern int cpu_idle(void * unused);
 
-extern __u16 boot_cpu_addr;
 extern volatile int __cpu_logical_map[];
 
 /*
  * An array with a pointer the lowcore of every CPU.
  */
-static int       max_cpus = NR_CPUS;	  /* Setup configured maximum number of CPUs to activate	*/
-int              smp_num_cpus;
+
 struct _lowcore *lowcore_ptr[NR_CPUS];
 cycles_t         cacheflush_time=0;
 int              smp_threads_ready=0;      /* Set when the idlers are all forked. */
-static atomic_t  smp_commenced = ATOMIC_INIT(0);
 
-volatile unsigned long phys_cpu_present_map;
 volatile unsigned long cpu_online_map;
+volatile unsigned long cpu_possible_map;
 unsigned long    cache_decay_ticks = 0;
 
 /*
- *      Setup routine for controlling SMP activation
- *
- *      Command-line option of "nosmp" or "maxcpus=0" will disable SMP
- *      activation entirely (the MPS table probe still happens, though).
- *
- *      Command-line option of "maxcpus=<NUM>", where <NUM> is an integer
- *      greater than 0, limits the maximum number of CPUs activated in
- *      SMP mode to <NUM>.
- */
-
-static int __init nosmp(char *str)
-{
-	max_cpus = 0;
-	return 1;
-}
-
-__setup("nosmp", nosmp);
-
-static int __init maxcpus(char *str)
-{
-	get_option(&str, &max_cpus);
-	return 1;
-}
-
-__setup("maxcpus=", maxcpus);
-
-/*
  * Reboot, halt and power_off routines for SMP.
  */
 extern char vmhalt_cmd[];
@@ -148,9 +118,10 @@
  */
 {
 	struct call_data_struct data;
-	int cpus = smp_num_cpus-1;
+	int cpus = num_online_cpus()-1;
 
-	if (!cpus || !atomic_read(&smp_commenced))
+	/* FIXME: get cpu lock -hc */
+	if (cpus <= 0)
 		return 0;
 
 	data.func = func;
@@ -183,8 +154,8 @@
         int i, rc;
 
         /* stop all processors */
-        for (i =  0; i < smp_num_cpus; i++) {
-                if (smp_processor_id() == i)
+        for (i =  0; i < NR_CPUS; i++) {
+                if (!cpu_online(i) || smp_processor_id() == i)
 			continue;
 		do {
 			rc = signal_processor_ps(&dummy, 0, i, sigp_stop);
@@ -199,10 +170,10 @@
         int i, rc;
 
         /* store status of all processors in their lowcores (real 0) */
-        for (i =  0; i < smp_num_cpus; i++) {
-                if (smp_processor_id() == i)
+        for (i =  0; i < NR_CPUS; i++) {
+                if (!cpu_online(i) || smp_processor_id() == i)
 			continue;
-		low_core_addr = (unsigned long)get_cpu_lowcore(i);
+		low_core_addr = (unsigned long) lowcore_ptr[i];
 		do {
 			rc = signal_processor_ps(&dummy, low_core_addr, i,
 						 sigp_store_status_at_address);
@@ -217,7 +188,7 @@
 void smp_send_stop(void)
 {
         /* write magic number to zero page (absolute 0) */
-        get_cpu_lowcore(smp_processor_id())->panic_magic = __PANIC_MAGIC;
+	lowcore_ptr[smp_processor_id()]->panic_magic = __PANIC_MAGIC;
 
 	/* stop other processors. */
 	do_send_stop();
@@ -325,7 +296,7 @@
  */
 static sigp_ccode smp_ext_bitcall(int cpu, ec_bit_sig sig)
 {
-        struct _lowcore *lowcore = get_cpu_lowcore(cpu);
+        struct _lowcore *lowcore = lowcore_ptr[cpu];
         sigp_ccode ccode;
 
         /*
@@ -345,10 +316,10 @@
         struct _lowcore *lowcore;
         int i;
 
-        for (i = 0; i < smp_num_cpus; i++) {
-                if (smp_processor_id() == i)
+        for (i = 0; i < NR_CPUS; i++) {
+                if (!cpu_online(i) || smp_processor_id() == i)
                         continue;
-                lowcore = get_cpu_lowcore(i);
+                lowcore = lowcore_ptr[i];
                 /*
                  * Set signaling bit in lowcore of target cpu and kick it
                  */
@@ -425,13 +396,11 @@
 void smp_ctl_set_bit(int cr, int bit) {
         ec_creg_mask_parms parms;
 
-        if (atomic_read(&smp_commenced) != 0) {
-                parms.start_ctl = cr;
-                parms.end_ctl = cr;
-                parms.orvals[cr] = 1 << bit;
-                parms.andvals[cr] = 0xFFFFFFFF;
-                smp_call_function(smp_ctl_bit_callback, &parms, 0, 1);
-        }
+	parms.start_ctl = cr;
+	parms.end_ctl = cr;
+	parms.orvals[cr] = 1 << bit;
+	parms.andvals[cr] = 0xFFFFFFFF;
+	smp_call_function(smp_ctl_bit_callback, &parms, 0, 1);
         __ctl_set_bit(cr, bit);
 }
 
@@ -441,13 +410,11 @@
 void smp_ctl_clear_bit(int cr, int bit) {
         ec_creg_mask_parms parms;
 
-        if (atomic_read(&smp_commenced) != 0) {
-                parms.start_ctl = cr;
-                parms.end_ctl = cr;
-                parms.orvals[cr] = 0x00000000;
-                parms.andvals[cr] = ~(1 << bit);
-                smp_call_function(smp_ctl_bit_callback, &parms, 0, 1);
-        }
+	parms.start_ctl = cr;
+	parms.end_ctl = cr;
+	parms.orvals[cr] = 0x00000000;
+	parms.andvals[cr] = ~(1 << bit);
+	smp_call_function(smp_ctl_bit_callback, &parms, 0, 1);
         __ctl_clear_bit(cr, bit);
 }
 
@@ -455,30 +422,31 @@
  * Lets check how many CPUs we have.
  */
 
-void smp_count_cpus(void)
+void __init smp_check_cpus(unsigned int max_cpus)
 {
-        int curr_cpu;
+        int curr_cpu, num_cpus;
+	__u16 boot_cpu_addr;
 
+	boot_cpu_addr = S390_lowcore.cpu_data.cpu_addr;
         current_thread_info()->cpu = 0;
-        smp_num_cpus = 1;
-	phys_cpu_present_map = 1;
+        num_cpus = 1;
+	cpu_possible_map = 1;
 	cpu_online_map = 1;
         for (curr_cpu = 0;
-             curr_cpu <= 65535 && smp_num_cpus < max_cpus; curr_cpu++) {
+             curr_cpu <= 65535 && num_cpus < max_cpus; curr_cpu++) {
                 if ((__u16) curr_cpu == boot_cpu_addr)
                         continue;
-                __cpu_logical_map[smp_num_cpus] = (__u16) curr_cpu;
-                if (signal_processor(smp_num_cpus, sigp_sense) ==
+                __cpu_logical_map[num_cpus] = (__u16) curr_cpu;
+                if (signal_processor(num_cpus, sigp_sense) ==
                     sigp_not_operational)
                         continue;
-		set_bit(smp_num_cpus, &phys_cpu_present_map);
-                smp_num_cpus++;
+		set_bit(num_cpus, &cpu_possible_map);
+                num_cpus++;
         }
-        printk("Detected %d CPU's\n",(int) smp_num_cpus);
+        printk("Detected %d CPU's\n",(int) num_cpus);
         printk("Boot cpu address %2X\n", boot_cpu_addr);
 }
 
-
 /*
  *      Activate a secondary processor.
  */
@@ -486,46 +454,62 @@
 extern int pfault_init(void);
 extern int pfault_token(void);
 
-int __init start_secondary(void *cpuvoid)
+int __devinit start_secondary(void *cpuvoid)
 {
         /* Setup the cpu */
         cpu_init();
-	/* Mark this cpu as online */
-	set_bit(smp_processor_id(), &cpu_online_map);
-        /* Print info about this processor */
-        print_cpu_info(&safe_get_cpu_lowcore(smp_processor_id())->cpu_data);
-        /* Wait for completion of smp startup */
-        while (!atomic_read(&smp_commenced))
-                /* nothing */ ;
         /* init per CPU timer */
         init_cpu_timer();
 #ifdef CONFIG_PFAULT
 	/* Enable pfault pseudo page faults on this cpu. */
 	pfault_init();
 #endif
+	/* Mark this cpu as online */
+	set_bit(smp_processor_id(), &cpu_online_map);
+	/* Switch on interrupts */
+	local_irq_enable();
+        /* Print info about this processor */
+        print_cpu_info(&S390_lowcore.cpu_data);
         /* cpu_idle will call schedule for us */
         return cpu_idle(NULL);
 }
 
-static struct task_struct *__init fork_by_hand(void)
+static struct task_struct *__devinit fork_by_hand(void)
 {
        struct pt_regs regs;
        /* don't care about the psw and regs settings since we'll never
           reschedule the forked task. */
        memset(&regs,0,sizeof(struct pt_regs));
-       return do_fork(CLONE_VM|CLONE_IDLETASK, 0, &regs, 0);
+       return do_fork(CLONE_VM|CLONE_IDLETASK, 0, &regs, 0, NULL);
 }
 
-static void __init do_boot_cpu(int cpu)
+int __cpu_up(unsigned int cpu)
 {
         struct task_struct *idle;
         struct _lowcore    *cpu_lowcore;
+        sigp_ccode          ccode;
+
+	/*
+	 *  Set prefix page for new cpu
+	 */
+
+	ccode = signal_processor_p((u32)(lowcore_ptr[cpu]),
+				   cpu, sigp_set_prefix);
+	if (ccode){
+		printk("sigp_set_prefix failed for cpu %d "
+		       "with condition code %d\n",
+		       (int) cpu, (int) ccode);
+		return -EIO;
+	}
+
 
         /* We can't use kernel_thread since we must _avoid_ to reschedule
            the child. */
         idle = fork_by_hand();
-	if (IS_ERR(idle))
-                panic("failed fork for CPU %d", cpu);
+	if (IS_ERR(idle)){
+                printk("failed fork for CPU %d", cpu);
+		return -EIO;
+	}
 
         /*
          * We remove it from the pidhash and the runqueue
@@ -535,9 +519,9 @@
 
         unhash_process(idle);
 
-        cpu_lowcore = get_cpu_lowcore(cpu);
+        cpu_lowcore = lowcore_ptr[cpu];
 	cpu_lowcore->save_area[15] = idle->thread.ksp;
-	cpu_lowcore->kernel_stack = (__u32) idle->thread_info + 8192;
+	cpu_lowcore->kernel_stack = (__u32) idle->thread_info + (2*PAGE_SIZE);
         __asm__ __volatile__("la    1,%0\n\t"
 			     "stctl 0,15,0(1)\n\t"
 			     "la    1,%1\n\t"
@@ -548,48 +532,34 @@
 
         eieio();
         signal_processor(cpu,sigp_restart);
-}
-
-/*
- *      Architecture specific routine called by the kernel just before init is
- *      fired off. This allows the BP to have everything in order [we hope].
- *      At the end of this all the APs will hit the system scheduling and off
- *      we go. Each AP will load the system gdt's and jump through the kernel
- *      init into idle(). At this point the scheduler will one day take over
- *      and give them jobs to do. smp_callin is a standard routine
- *      we use to track CPUs as they power up.
- */
 
-void __init smp_commence(void)
-{
-        /*
-         *      Lets the callins below out of their loop.
-         */
-        atomic_set(&smp_commenced,1);
+	while (!cpu_online(cpu));
+	return 0;
 }
 
 /*
- *	Cycle through the processors sending sigp_restart to boot each.
+ *	Cycle through the processors and setup structures.
  */
 
-void __init smp_boot_cpus(void)
+void __init smp_prepare_cpus(unsigned int max_cpus)
 {
 	unsigned long async_stack;
-        sigp_ccode   ccode;
         int i;
 
         /* request the 0x1202 external interrupt */
         if (register_external_interrupt(0x1202, do_ext_call_interrupt) != 0)
                 panic("Couldn't request external interrupt 0x1202");
-        smp_count_cpus();
+        smp_check_cpus(max_cpus);
         memset(lowcore_ptr,0,sizeof(lowcore_ptr));  
-        
+
         /*
-         *      Initialize the logical to physical CPU number mapping
+         *  Initialize prefix pages and stacks for all possible cpus
          */
-        print_cpu_info(&safe_get_cpu_lowcore(0)->cpu_data);
+	print_cpu_info(&S390_lowcore.cpu_data);
 
-        for(i = 0; i < smp_num_cpus; i++) {
+        for(i = 0; i < NR_CPUS; i++) {
+		if (!cpu_possible(i))
+			continue;
 		lowcore_ptr[i] = (struct _lowcore *)
 			__get_free_page(GFP_KERNEL|GFP_DMA);
 		async_stack = __get_free_pages(GFP_KERNEL,1);
@@ -598,26 +568,12 @@
 
                 memcpy(lowcore_ptr[i], &S390_lowcore, sizeof(struct _lowcore));
 		lowcore_ptr[i]->async_stack = async_stack + (2 * PAGE_SIZE);
-                /*
-                 * Most of the parameters are set up when the cpu is
-                 * started up.
-                 */
-		if (smp_processor_id() == i) {
-			set_prefix((u32) lowcore_ptr[i]);
-			continue;
-		}
-		ccode = signal_processor_p((u32)(lowcore_ptr[i]),
-					   i, sigp_set_prefix);
-		if (ccode)
-			panic("sigp_set_prefix failed for cpu %d "
-			      "with condition code %d\n",
-			      (int) i, (int) ccode);
-		do_boot_cpu(i);
 	}
-	/*
-	 * Now wait until all of the cpus are online.
-	 */
-	while (phys_cpu_present_map != cpu_online_map);
+	set_prefix((u32) lowcore_ptr[smp_processor_id()]);
+}
+
+void smp_cpus_done(unsigned int max_cpus)
+{
 }
 
 /*
@@ -634,5 +590,4 @@
 EXPORT_SYMBOL(lowcore_ptr);
 EXPORT_SYMBOL(smp_ctl_set_bit);
 EXPORT_SYMBOL(smp_ctl_clear_bit);
-EXPORT_SYMBOL(smp_num_cpus);
 EXPORT_SYMBOL(smp_call_function);
diff -Nru a/arch/s390/kernel/time.c b/arch/s390/kernel/time.c
--- a/arch/s390/kernel/time.c	Mon Oct  7 11:25:25 2002
+++ b/arch/s390/kernel/time.c	Mon Oct  7 11:25:25 2002
@@ -27,6 +27,7 @@
 #include <asm/uaccess.h>
 #include <asm/delay.h>
 #include <asm/s390_ext.h>
+#include <asm/div64.h>
 
 #include <linux/timex.h>
 #include <linux/config.h>
@@ -37,55 +38,39 @@
 #define USECS_PER_JIFFY     ((unsigned long) 1000000/HZ)
 #define CLK_TICKS_PER_JIFFY ((unsigned long) USECS_PER_JIFFY << 12)
 
+/*
+ * Create a small time difference between the timer interrupts
+ * on the different cpus to avoid lock contention.
+ */
+#define CPU_DEVIATION       (smp_processor_id() << 12)
+
 #define TICK_SIZE tick
 
 u64 jiffies_64;
 
 static ext_int_info_t ext_int_info_timer;
+static uint64_t xtime_cc;
 static uint64_t init_timer_cc;
 
 extern rwlock_t xtime_lock;
 extern unsigned long wall_jiffies;
 
-void tod_to_timeval(__u64 todval, struct timeval *xtime)
+void tod_to_timeval(__u64 todval, struct timespec *xtime)
 {
-        const int high_bit = 0x80000000L;
-        const int c_f4240 = 0xf4240L;
-        const int c_7a120 = 0x7a120;
-	/* We have to divide the 64 bit value todval by 4096
-	 * (because the 2^12 bit is the one that changes every 
-         * microsecond) and then split it into seconds and
-         * microseconds. A value of max (2^52-1) divided by
-         * the value 0xF4240 can yield a max result of approx
-         * (2^32.068). Thats to big to fit into a signed int
-	 *   ... hacking time!
-         */
-	asm volatile ("L     2,%1\n\t"
-		      "LR    3,2\n\t"
-		      "SRL   2,12\n\t"
-		      "SLL   3,20\n\t"
-		      "L     4,%O1+4(%R1)\n\t"
-		      "SRL   4,12\n\t"
-		      "OR    3,4\n\t"  /* now R2/R3 contain (todval >> 12) */
-		      "SR    4,4\n\t"
-		      "CL    2,%2\n\t"
-		      "JL    .+12\n\t"
-		      "S     2,%2\n\t"
-		      "L     4,%3\n\t"
-                      "D     2,%4\n\t"
-		      "OR    3,4\n\t"
-		      "ST    2,%O0+4(%R0)\n\t"
-		      "ST    3,%0"
-		      : "=m" (*xtime) : "m" (todval),
-		        "m" (c_7a120), "m" (high_bit), "m" (c_f4240)
-		      : "cc", "memory", "2", "3", "4" );
+	unsigned long long sec;
+
+	sec = todval >> 12;
+	do_div(sec, 1000000);
+	xtime->tv_sec = sec;
+	todval -= (sec * 1000000) << 12;
+	xtime->tv_nsec = ((todval * 1000) >> 12);
 }
 
 static inline unsigned long do_gettimeoffset(void) 
 {
 	__u64 now;
 
-	asm ("STCK 0(%0)" : : "a" (&now) : "memory", "cc");
+	asm volatile ("STCK 0(%0)" : : "a" (&now) : "memory", "cc");
         now = (now - init_timer_cc) >> 12;
 	/* We require the offset from the latest update of xtime */
 	now -= (__u64) wall_jiffies*USECS_PER_JIFFY;
@@ -102,7 +87,7 @@
 
 	read_lock_irqsave(&xtime_lock, flags);
 	sec = xtime.tv_sec;
-	usec = xtime.tv_usec + do_gettimeoffset();
+	usec = xtime.tv_nsec / 1000 + do_gettimeoffset();
 	read_unlock_irqrestore(&xtime_lock, flags);
 
 	while (usec >= 1000000) {
@@ -118,7 +103,7 @@
 {
 
 	write_lock_irq(&xtime_lock);
-	/* This is revolting. We need to set the xtime.tv_usec
+	/* This is revolting. We need to set the xtime.tv_nsec
 	 * correctly. However, the value in this location is
 	 * is value at the last tick.
 	 * Discover what correction gettimeofday
@@ -131,7 +116,8 @@
 		tv->tv_sec--;
 	}
 
-	xtime = *tv;
+	xtime.tv_sec = tv->tv_sec;
+	xtime.tv_nsec = tv->tv_usec * 1000;
 	time_adjust = 0;		/* stop active adjtime() */
 	time_status |= STA_UNSYNC;
 	time_maxerror = NTP_PHASE_LIMIT;
@@ -139,58 +125,90 @@
 	write_unlock_irq(&xtime_lock);
 }
 
+static inline __u32 div64_32(__u64 dividend, __u32 divisor)
+{
+	register_pair rp;
+
+	rp.pair = dividend;
+	asm ("dr %0,%1" : "+d" (rp) : "d" (divisor));
+	return rp.subreg.odd;
+}
+
 /*
  * timer_interrupt() needs to keep up the real-time clock,
  * as well as call the "do_timer()" routine every clocktick
  */
-
-#ifdef CONFIG_SMP
-extern __u16 boot_cpu_addr;
-#endif
-
 static void do_comparator_interrupt(struct pt_regs *regs, __u16 error_code)
 {
 	int cpu = smp_processor_id();
+	__u64 tmp;
+	__u32 ticks;
 
-	irq_enter(cpu, 0);
-
-	/*
-	 * set clock comparator for next tick
-	 */
-        S390_lowcore.jiffy_timer += CLK_TICKS_PER_JIFFY;
-        asm volatile ("SCKC %0" : : "m" (S390_lowcore.jiffy_timer));
+	/* Calculate how many ticks have passed. */
+	asm volatile ("STCK 0(%0)" : : "a" (&tmp) : "memory", "cc");
+	tmp = tmp - S390_lowcore.jiffy_timer;
+	if (tmp >= 2*CLK_TICKS_PER_JIFFY) {  /* more than one tick ? */
+		ticks = div64_32(tmp >> 1, CLK_TICKS_PER_JIFFY >> 1);
+		S390_lowcore.jiffy_timer +=
+			CLK_TICKS_PER_JIFFY * (__u64) ticks;
+	} else {
+		ticks = 1;
+		S390_lowcore.jiffy_timer += CLK_TICKS_PER_JIFFY;
+	}
 
-#ifdef CONFIG_SMP
-	if (S390_lowcore.cpu_data.cpu_addr == boot_cpu_addr)
-		write_lock(&xtime_lock);
+	/* set clock comparator for next tick */
+	tmp = S390_lowcore.jiffy_timer + CLK_TICKS_PER_JIFFY + CPU_DEVIATION;
+        asm volatile ("SCKC %0" : : "m" (tmp));
 
-	update_process_times(user_mode(regs));
+	irq_enter();
 
-	if (S390_lowcore.cpu_data.cpu_addr == boot_cpu_addr) {
-		do_timer(regs);
-		write_unlock(&xtime_lock);
+#ifdef CONFIG_SMP
+	/*
+	 * Do not rely on the boot cpu to do the calls to do_timer.
+	 * Spread it over all cpus instead.
+	 */
+	write_lock(&xtime_lock);
+	if (S390_lowcore.jiffy_timer > xtime_cc) {
+		__u32 xticks;
+
+		tmp = S390_lowcore.jiffy_timer - xtime_cc;
+		if (tmp >= 2*CLK_TICKS_PER_JIFFY) {
+			xticks = div64_32(tmp >> 1, CLK_TICKS_PER_JIFFY >> 1);
+			xtime_cc += (__u64) xticks * CLK_TICKS_PER_JIFFY;
+		} else {
+			xticks = 1;
+			xtime_cc += CLK_TICKS_PER_JIFFY;
+		}
+		while (xticks--)
+			do_timer(regs);
 	}
+	write_unlock(&xtime_lock);
+	while (ticks--)
+		update_process_times(user_mode(regs));
 #else
-	do_timer(regs);
+	while (ticks--)
+		do_timer(regs);
 #endif
 
-	irq_exit(cpu, 0);
+	irq_exit();
 }
 
 /*
- * Start the clock comparator on the current CPU
+ * Start the clock comparator on the current CPU.
  */
 void init_cpu_timer(void)
 {
 	unsigned long cr0;
+	__u64 timer;
 
         /* allow clock comparator timer interrupt */
         asm volatile ("STCTL 0,0,%0" : "=m" (cr0) : : "memory");
         cr0 |= 0x800;
         asm volatile ("LCTL 0,0,%0" : : "m" (cr0) : "memory");
-	S390_lowcore.jiffy_timer = (__u64) jiffies * CLK_TICKS_PER_JIFFY;
-	S390_lowcore.jiffy_timer += init_timer_cc + CLK_TICKS_PER_JIFFY;
-	asm volatile ("SCKC %0" : : "m" (S390_lowcore.jiffy_timer));
+	timer = init_timer_cc + jiffies_64 * CLK_TICKS_PER_JIFFY;
+	S390_lowcore.jiffy_timer = timer;
+	timer += CLK_TICKS_PER_JIFFY + CPU_DEVIATION;
+	asm volatile ("SCKC %0" : : "m" (timer));
 }
 
 /*
@@ -199,7 +217,7 @@
  */
 void __init time_init(void)
 {
-        __u64 set_time_cc;
+	__u64 set_time_cc;
 	int cc;
 
         /* kick the TOD clock */
@@ -222,8 +240,9 @@
         }
 
 	/* set xtime */
-        set_time_cc = init_timer_cc - 0x8126d60e46000000LL +
-                      (0x3c26700LL*1000000*4096);
+	xtime_cc = init_timer_cc;
+	set_time_cc = init_timer_cc - 0x8126d60e46000000LL +
+		(0x3c26700LL*1000000*4096);
         tod_to_timeval(set_time_cc, &xtime);
 
         /* request the 0x1004 external interrupt */
diff -Nru a/arch/s390/kernel/traps.c b/arch/s390/kernel/traps.c
--- a/arch/s390/kernel/traps.c	Mon Oct  7 11:25:30 2002
+++ b/arch/s390/kernel/traps.c	Mon Oct  7 11:25:30 2002
@@ -116,22 +116,22 @@
 		stack = (unsigned long*)&stack;
 
 	printk("Call Trace: ");
-	low_addr = ((unsigned long) stack) & PSW_ADDR_MASK;
+	low_addr = ((unsigned long) stack) & PSW_ADDR_INSN;
 	high_addr = (low_addr & (-THREAD_SIZE)) + THREAD_SIZE;
 	/* Skip the first frame (biased stack) */
-	backchain = *((unsigned long *) low_addr) & PSW_ADDR_MASK;
+	backchain = *((unsigned long *) low_addr) & PSW_ADDR_INSN;
 	/* Print up to 8 lines */
 	for (i = 0; i < 8; i++) {
 		if (backchain < low_addr || backchain >= high_addr)
 			break;
-		ret_addr = *((unsigned long *) (backchain+56)) & PSW_ADDR_MASK;
+		ret_addr = *((unsigned long *) (backchain+56)) & PSW_ADDR_INSN;
 		if (!kernel_text_address(ret_addr))
 			break;
 		if (i && ((i % 6) == 0))
 			printk("\n   ");
 		printk("[<%08lx>] ", ret_addr);
 		low_addr = backchain;
-		backchain = *((unsigned long *) backchain) & PSW_ADDR_MASK;
+		backchain = *((unsigned long *) backchain) & PSW_ADDR_INSN;
 	}
 	printk("\n");
 }
@@ -170,13 +170,21 @@
 	show_trace(sp);
 }
 
+/*
+ * The architecture-independent dump_stack generator
+ */
+void dump_stack(void)
+{
+	show_stack(0);
+}
+
 void show_registers(struct pt_regs *regs)
 {
 	mm_segment_t old_fs;
 	char *mode;
 	int i;
 
-	mode = (regs->psw.mask & PSW_PROBLEM_STATE) ? "User" : "Krnl";
+	mode = (regs->psw.mask & PSW_MASK_PSTATE) ? "User" : "Krnl";
 	printk("%s PSW : %08lx %08lx\n",
 	       mode, (unsigned long) regs->psw.mask,
 	       (unsigned long) regs->psw.addr);
@@ -202,7 +210,7 @@
 	 * time of the fault.
 	 */
 	old_fs = get_fs();
-	if (regs->psw.mask & PSW_PROBLEM_STATE)
+	if (regs->psw.mask & PSW_MASK_PSTATE)
 		set_fs(USER_DS);
 	else
 		set_fs(KERNEL_DS);
@@ -279,10 +287,10 @@
 	 * We got all needed information from the lowcore and can
 	 * now safely switch on interrupts.
 	 */
-        if (regs->psw.mask & PSW_PROBLEM_STATE)
+        if (regs->psw.mask & PSW_MASK_PSTATE)
 		local_irq_enable();
 
-        if (regs->psw.mask & PSW_PROBLEM_STATE) {
+        if (regs->psw.mask & PSW_MASK_PSTATE) {
                 struct task_struct *tsk = current;
 
                 tsk->thread.trap_no = interruption_code & 0xffff;
@@ -314,12 +322,12 @@
 
 static inline void *get_check_address(struct pt_regs *regs)
 {
-	return (void *) ADDR_BITS_REMOVE(regs->psw.addr-S390_lowcore.pgm_ilc);
+	return (void *)((regs->psw.addr-S390_lowcore.pgm_ilc) & PSW_ADDR_INSN);
 }
 
 int do_debugger_trap(struct pt_regs *regs,int signal)
 {
-	if(regs->psw.mask&PSW_PROBLEM_STATE)
+	if(regs->psw.mask&PSW_MASK_PSTATE)
 	{
 		if(current->ptrace & PT_PTRACED)
 			force_sig(signal,current);
@@ -415,10 +423,10 @@
 	 * We got all needed information from the lowcore and can
 	 * now safely switch on interrupts.
 	 */
-	if (regs->psw.mask & PSW_PROBLEM_STATE)
+	if (regs->psw.mask & PSW_MASK_PSTATE)
 		local_irq_enable();
 
-	if (regs->psw.mask & PSW_PROBLEM_STATE)
+	if (regs->psw.mask & PSW_MASK_PSTATE)
 		get_user(*((__u16 *) opcode), location);
 	else
 		*((__u16 *)opcode)=*((__u16 *)location);
@@ -428,7 +436,7 @@
 			signal = SIGILL;
 	}
 #ifdef CONFIG_MATHEMU
-        else if (regs->psw.mask & PSW_PROBLEM_STATE)
+        else if (regs->psw.mask & PSW_MASK_PSTATE)
 	{
 		if (opcode[0] == 0xb3) {
 			get_user(*((__u16 *) (opcode+2)), location+1);
@@ -476,10 +484,10 @@
 	 * We got all needed information from the lowcore and can
 	 * now safely switch on interrupts.
 	 */
-	if (regs->psw.mask & PSW_PROBLEM_STATE)
+	if (regs->psw.mask & PSW_MASK_PSTATE)
 		local_irq_enable();
 		
-        if (regs->psw.mask & PSW_PROBLEM_STATE) {
+        if (regs->psw.mask & PSW_MASK_PSTATE) {
 		get_user(*((__u16 *) opcode), location);
 		switch (opcode[0]) {
 		case 0x28: /* LDR Rx,Ry   */
@@ -539,7 +547,7 @@
 	 * We got all needed information from the lowcore and can
 	 * now safely switch on interrupts.
 	 */
-	if (regs->psw.mask & PSW_PROBLEM_STATE)
+	if (regs->psw.mask & PSW_MASK_PSTATE)
 		local_irq_enable();
 
 	if (MACHINE_HAS_IEEE)
@@ -547,7 +555,7 @@
 				  : "=m" (current->thread.fp_regs.fpc));
 
 #ifdef CONFIG_MATHEMU
-        else if (regs->psw.mask & PSW_PROBLEM_STATE) {
+        else if (regs->psw.mask & PSW_MASK_PSTATE) {
         	__u8 opcode[6];
 		get_user(*((__u16 *) opcode), location);
 		switch (opcode[0]) {
@@ -671,21 +679,19 @@
 
 void handle_per_exception(struct pt_regs *regs)
 {
-	if(regs->psw.mask&PSW_PROBLEM_STATE)
-	{
+	if (regs->psw.mask & PSW_MASK_PSTATE) {
 		per_struct *per_info=&current->thread.per_info;
 		per_info->lowcore.words.perc_atmid=S390_lowcore.per_perc_atmid;
 		per_info->lowcore.words.address=S390_lowcore.per_address;
 		per_info->lowcore.words.access_id=S390_lowcore.per_access_id;
 	}
-	if(do_debugger_trap(regs,SIGTRAP))
-	{
+	if (do_debugger_trap(regs,SIGTRAP)) {
 		/* I've seen this possibly a task structure being reused ? */
 		printk("Spurious per exception detected\n");
 		printk("switching off per tracing for this task.\n");
 		show_regs(regs);
 		/* Hopefully switching off per tracing will help us survive */
-		regs->psw.mask &= ~PSW_PER_MASK;
+		regs->psw.mask &= ~PSW_MASK_PER;
 	}
 }
 
diff -Nru a/arch/s390/lib/Makefile b/arch/s390/lib/Makefile
--- a/arch/s390/lib/Makefile	Mon Oct  7 11:25:25 2002
+++ b/arch/s390/lib/Makefile	Mon Oct  7 11:25:25 2002
@@ -6,8 +6,7 @@
 
 EXTRA_AFLAGS := -traditional
 
-obj-y = checksum.o delay.o memset.o misaligned.o strcmp.o strncpy.o uaccess.o
-export-objs += misaligned.o
+obj-y = delay.o memset.o strcmp.o strncpy.o uaccess.o
 
 include $(TOPDIR)/Rules.make
 
diff -Nru a/arch/s390/lib/checksum.c b/arch/s390/lib/checksum.c
--- a/arch/s390/lib/checksum.c	Mon Oct  7 11:25:25 2002
+++ /dev/null	Wed Dec 31 16:00:00 1969
@@ -1,57 +0,0 @@
-/*
- *  arch/s390/lib/checksum.c
- *    S390 fast network checksum routines
- *
- *  S390 version
- *    Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation
- *    Author(s): Ulrich Hild        (first version),
- *               Martin Schwidefsky (schwidefsky@de.ibm.com),
- *               Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com),
- *
- * This file contains network checksum routines
- */
- 
-#include <linux/string.h>
-#include <linux/types.h>
-#include <asm/uaccess.h>
-#include <asm/byteorder.h>
-#include <asm/checksum.h>
-
-/*
- * computes a partial checksum, e.g. for TCP/UDP fragments
- */
-unsigned int
-csum_partial (const unsigned char *buff, int len, unsigned int sum)
-{
-	register_pair rp;
-	  /*
-	   * Experiments with ethernet and slip connections show that buff
-	   * is aligned on either a 2-byte or 4-byte boundary.
-	   */
-	rp.subreg.even = (unsigned long) buff;
-	rp.subreg.odd = (unsigned long) len;
-        __asm__ __volatile__ (
-                "0:  cksm %0,%1\n"    /* do checksum on longs */
-                "    jo   0b\n"
-                : "+&d" (sum), "+&a" (rp) : : "cc" );
-        return sum;
-}
-
-/*
- *	Fold a partial checksum without adding pseudo headers
- */
-unsigned short csum_fold(unsigned int sum)
-{
-	register_pair rp;
-
-	__asm__ __volatile__ (
-		"    slr  %N1,%N1\n" /* %0 = H L */
-		"    lr   %1,%0\n"   /* %0 = H L, %1 = H L 0 0 */
-		"    srdl %1,16\n"   /* %0 = H L, %1 = 0 H L 0 */
-		"    alr  %1,%N1\n"  /* %0 = H L, %1 = L H L 0 */
-		"    alr  %0,%1\n"   /* %0 = H+L+C L+H */
-		"    srl  %0,16\n"   /* %0 = H+L+C */
-		: "+&d" (sum), "=d" (rp) : : "cc" );
-	return ((unsigned short) ~sum);
-}
-
diff -Nru a/arch/s390/lib/misaligned.c b/arch/s390/lib/misaligned.c
--- a/arch/s390/lib/misaligned.c	Mon Oct  7 11:25:35 2002
+++ /dev/null	Wed Dec 31 16:00:00 1969
@@ -1,29 +0,0 @@
-/*
- *  arch/s390/lib/misaligned.c
- *    S390 misalignment panic stubs
- *
- *  S390 version
- *    Copyright (C) 2001 IBM Deutschland Entwicklung GmbH, IBM Corporation
- *    Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com).
- *
- * xchg wants to panic if the pointer is not aligned. To avoid multiplying
- * the panic message over and over again, the panic is done in the helper
- * functions __misaligned_u32 and __misaligned_u16.
- */
-
-#include <linux/module.h> 
-#include <linux/kernel.h>
-
-void __misaligned_u16(void)
-{
-	panic("misaligned (__u16 *) in __xchg\n");
-}
-
-void __misaligned_u32(void)
-{
-	panic("misaligned (__u32 *) in __xchg\n");
-}
-
-EXPORT_SYMBOL(__misaligned_u16);
-EXPORT_SYMBOL(__misaligned_u32);
-
diff -Nru a/arch/s390/mm/extable.c b/arch/s390/mm/extable.c
--- a/arch/s390/mm/extable.c	Mon Oct  7 11:25:26 2002
+++ b/arch/s390/mm/extable.c	Mon Oct  7 11:25:26 2002
@@ -48,7 +48,7 @@
         addr &= 0x7fffffff;  /* remove amode bit from address */
 	/* There is only the kernel to search.  */
 	ret = search_one_table(__start___ex_table, __stop___ex_table-1, addr);
-	if (ret) ret = FIX_PSW(ret);
+	if (ret) ret = ret | PSW_ADDR_AMODE31;
 	return ret;
 #else
 	unsigned long flags;
@@ -63,7 +63,7 @@
 		ret = search_one_table(mp->ex_table_start,
 				       mp->ex_table_end - 1, addr);
 		if (ret) {
-			ret = FIX_PSW(ret);
+			ret = ret | PSW_ADDR_AMODE31;
 			break;
 		}
 	}
diff -Nru a/arch/s390/mm/fault.c b/arch/s390/mm/fault.c
--- a/arch/s390/mm/fault.c	Mon Oct  7 11:25:36 2002
+++ b/arch/s390/mm/fault.c	Mon Oct  7 11:25:36 2002
@@ -46,7 +46,6 @@
  */
 void bust_spinlocks(int yes)
 {
-	spin_lock_init(&timerlist_lock);
 	if (yes) {
 		oops_in_progress = 1;
 	} else {
@@ -167,7 +166,7 @@
 
 		/* Low-address protection hit in kernel mode means 
 		   NULL pointer write access in kernel mode.  */
- 		if (!(regs->psw.mask & PSW_PROBLEM_STATE)) {
+ 		if (!(regs->psw.mask & PSW_MASK_PSTATE)) {
 			address = 0;
 			user_address = 0;
 			goto no_context;
@@ -234,16 +233,18 @@
 	 * the fault.
 	 */
 	switch (handle_mm_fault(mm, vma, address, error_code == 4)) {
-	case 1:
+	case VM_FAULT_MINOR:
 		tsk->min_flt++;
 		break;
-	case 2:
+	case VM_FAULT_MAJOR:
 		tsk->maj_flt++;
 		break;
-	case 0:
+	case VM_FAULT_SIGBUS:
 		goto do_sigbus;
-	default:
+	case VM_FAULT_OOM:
 		goto out_of_memory;
+	default:
+		BUG();
 	}
 
         up_read(&mm->mmap_sem);
@@ -257,7 +258,7 @@
         up_read(&mm->mmap_sem);
 
         /* User mode accesses just cause a SIGSEGV */
-        if (regs->psw.mask & PSW_PROBLEM_STATE) {
+        if (regs->psw.mask & PSW_MASK_PSTATE) {
                 tsk->thread.prot_addr = address;
                 tsk->thread.trap_no = error_code;
 		force_sigsegv(regs, error_code, si_code, address);
@@ -297,7 +298,7 @@
 		goto survive;
 	}
 	printk("VM: killing process %s\n", tsk->comm);
-	if (regs->psw.mask & PSW_PROBLEM_STATE)
+	if (regs->psw.mask & PSW_MASK_PSTATE)
 		do_exit(SIGKILL);
 	goto no_context;
 
@@ -313,7 +314,7 @@
 	force_sig(SIGBUS, tsk);
 
 	/* Kernel mode? Handle exceptions or die */
-	if (!(regs->psw.mask & PSW_PROBLEM_STATE))
+	if (!(regs->psw.mask & PSW_MASK_PSTATE))
 		goto no_context;
 }
 
@@ -392,7 +393,7 @@
                 spin_unlock(&pseudo_wait_spinlock);
         } else {
                 /* Pseudo page faults in kernel mode is a bad idea */
-                if (!(regs->psw.mask & PSW_PROBLEM_STATE)) {
+                if (!(regs->psw.mask & PSW_MASK_PSTATE)) {
                         /*
 			 * VM presents pseudo page faults if the interrupted
 			 * state was not disabled for interrupts. So we can
@@ -527,7 +528,7 @@
 	 * We got all needed information from the lowcore and can
 	 * now safely switch on interrupts.
 	 */
-	if (regs->psw.mask & PSW_PROBLEM_STATE)
+	if (regs->psw.mask & PSW_MASK_PSTATE)
 		local_irq_enable();
 
 	if (subcode & 0x0080) {
diff -Nru a/arch/s390/vmlinux-shared.lds b/arch/s390/vmlinux-shared.lds
--- a/arch/s390/vmlinux-shared.lds	Mon Oct  7 11:25:39 2002
+++ /dev/null	Wed Dec 31 16:00:00 1969
@@ -1,93 +0,0 @@
-/* ld script to make s390 Linux kernel
- * Written by Martin Schwidefsky (schwidefsky@de.ibm.com)
- */
-OUTPUT_FORMAT("elf32-s390", "elf32-s390", "elf32-s390")
-OUTPUT_ARCH(s390)
-ENTRY(_start)
-jiffies = jiffies_64 + 4;
-SECTIONS
-{
-  . = 0x00000000;
-  _text = .;			/* Text and read-only data */
-  .text : {
-	*(.text)
-	*(.fixup)
-	*(.gnu.warning)
-	} = 0x0700
-
-  _etext = .;			/* End of text section */
-
-  .rodata : { *(.rodata) }
-  .kstrtab : { *(.kstrtab) }
-
-  . = ALIGN(16);		/* Exception table */
-  __start___ex_table = .;
-  __ex_table : { *(__ex_table) }
-  __stop___ex_table = .;
-
-  __start___ksymtab = .;	/* Kernel symbol table */
-  __ksymtab : { *(__ksymtab) }
-  __stop___ksymtab = .;
-
-  . = ALIGN(1048576);		/* VM shared segments are 1MB aligned */
-
-  _eshared = .;			/* End of shareable data */
-
-  .data : {			/* Data */
-	*(.data)
-	CONSTRUCTORS
-	}
-
-  _edata = .;			/* End of data section */
-
-  . = ALIGN(8192);		/* init_task */
-  .data.init_task : { *(.data.init_task) }
-
-  . = ALIGN(4096);		/* Init code and data */
-  __init_begin = .;
-  .text.init : { *(.text.init) }
-  .data.init : { *(.data.init) }
-  . = ALIGN(256);
-  __setup_start = .;
-  .setup.init : { *(.setup.init) }
-  __setup_end = .;
-  __initcall_start = .;
-  .initcall.init : {
-	*(.initcall1.init) 
-	*(.initcall2.init) 
-	*(.initcall3.init) 
-	*(.initcall4.init) 
-	*(.initcall5.init) 
-	*(.initcall6.init) 
-	*(.initcall7.init)
-  }
-  __initcall_end = .;
-  . = ALIGN(256);
-  __per_cpu_start = .;
-  .date.percpu  : { *(.data.percpu) }
-  __per_cpu_end = .;
-  . = ALIGN(4096);
-  __init_end = .;
-
-  . = ALIGN(32);
-  .data.cacheline_aligned : { *(.data.cacheline_aligned) }
-
-  . = ALIGN(4096);
-  .data.page_aligned : { *(.data.idt) }
-
-
-  __bss_start = .;		/* BSS */
-  .bss : {
-	*(.bss)
-	}
-  _end = . ;
-
-  /* Stabs debugging sections.  */
-  .stab 0 : { *(.stab) }
-  .stabstr 0 : { *(.stabstr) }
-  .stab.excl 0 : { *(.stab.excl) }
-  .stab.exclstr 0 : { *(.stab.exclstr) }
-  .stab.index 0 : { *(.stab.index) }
-  .stab.indexstr 0 : { *(.stab.indexstr) }
-  .comment 0 : { *(.comment) }
-}
diff -Nru a/arch/s390/vmlinux.lds b/arch/s390/vmlinux.lds
--- a/arch/s390/vmlinux.lds	Mon Oct  7 11:25:29 2002
+++ /dev/null	Wed Dec 31 16:00:00 1969
@@ -1,88 +0,0 @@
-/* ld script to make s390 Linux kernel
- * Written by Martin Schwidefsky (schwidefsky@de.ibm.com)
- */
-OUTPUT_FORMAT("elf32-s390", "elf32-s390", "elf32-s390")
-OUTPUT_ARCH(s390)
-ENTRY(_start)
-jiffies = jiffies_64 + 4;
-SECTIONS
-{
-  . = 0x00000000;
-  _text = .;			/* Text and read-only data */
-  .text : {
-	*(.text)
-	*(.fixup)
-	*(.gnu.warning)
-	} = 0x0700
-
-  _etext = .;			/* End of text section */
-
-  .rodata : { *(.rodata) *(.rodata.*) }
-  .kstrtab : { *(.kstrtab) }
-
-  . = ALIGN(16);		/* Exception table */
-  __start___ex_table = .;
-  __ex_table : { *(__ex_table) }
-  __stop___ex_table = .;
-
-  __start___ksymtab = .;	/* Kernel symbol table */
-  __ksymtab : { *(__ksymtab) }
-  __stop___ksymtab = .;
-
-  .data : {			/* Data */
-	*(.data)
-	CONSTRUCTORS
-	}
-
-  _edata = .;			/* End of data section */
-
-  . = ALIGN(8192);		/* init_task */
-  .data.init_task : { *(.data.init_task) }
-
-  . = ALIGN(4096);		/* Init code and data */
-  __init_begin = .;
-  .text.init : { *(.text.init) }
-  .data.init : { *(.data.init) }
-  . = ALIGN(256);
-  __setup_start = .;
-  .setup.init : { *(.setup.init) }
-  __setup_end = .;
-  __initcall_start = .;
-  .initcall.init : {
-	*(.initcall1.init) 
-	*(.initcall2.init) 
-	*(.initcall3.init) 
-	*(.initcall4.init) 
-	*(.initcall5.init) 
-	*(.initcall6.init) 
-	*(.initcall7.init)
-  }
-  __initcall_end = .;
-  . = ALIGN(256);
-  __per_cpu_start = .;
-  .date.percpu  : { *(.data.percpu) }
-  __per_cpu_end = .;
-  . = ALIGN(4096);
-  __init_end = .;
-
-  . = ALIGN(32);
-  .data.cacheline_aligned : { *(.data.cacheline_aligned) }
-
-  . = ALIGN(4096);
-  .data.page_aligned : { *(.data.idt) }
-
-  __bss_start = .;		/* BSS */
-  .bss : {
-	*(.bss)
-	}
-  _end = . ;
-
-  /* Stabs debugging sections.  */
-  .stab 0 : { *(.stab) }
-  .stabstr 0 : { *(.stabstr) }
-  .stab.excl 0 : { *(.stab.excl) }
-  .stab.exclstr 0 : { *(.stab.exclstr) }
-  .stab.index 0 : { *(.stab.index) }
-  .stab.indexstr 0 : { *(.stab.indexstr) }
-  .comment 0 : { *(.comment) }
-}
diff -Nru a/arch/s390/vmlinux.lds.S b/arch/s390/vmlinux.lds.S
--- a/arch/s390/vmlinux.lds.S	Mon Oct  7 11:25:31 2002
+++ b/arch/s390/vmlinux.lds.S	Mon Oct  7 11:25:31 2002
@@ -1,7 +1,94 @@
-#include <linux/config.h>
+/* ld script to make s390 Linux kernel
+ * Written by Martin Schwidefsky (schwidefsky@de.ibm.com)
+ */
+OUTPUT_FORMAT("elf32-s390", "elf32-s390", "elf32-s390")
+OUTPUT_ARCH(s390)
+ENTRY(_start)
+jiffies = jiffies_64 + 4;
+SECTIONS
+{
+  . = 0x00000000;
+  _text = .;			/* Text and read-only data */
+  .text : {
+	*(.text)
+	*(.fixup)
+	*(.gnu.warning)
+	} = 0x0700
+
+  _etext = .;			/* End of text section */
+
+  .rodata : { *(.rodata) *(.rodata.*) }
+  .kstrtab : { *(.kstrtab) }
+
+  . = ALIGN(16);		/* Exception table */
+  __start___ex_table = .;
+  __ex_table : { *(__ex_table) }
+  __stop___ex_table = .;
+
+  __start___ksymtab = .;	/* Kernel symbol table */
+  __ksymtab : { *(__ksymtab) }
+  __stop___ksymtab = .;
 
 #ifdef CONFIG_SHARED_KERNEL
-#include "vmlinux-shared.lds"
-#else
-#include "vmlinux.lds"
+  . = ALIGN(1048576);		/* VM shared segments are 1MB aligned */
+
+  _eshared = .;			/* End of shareable data */
 #endif
+
+  .data : {			/* Data */
+	*(.data)
+	CONSTRUCTORS
+	}
+
+  _edata = .;			/* End of data section */
+
+  . = ALIGN(8192);		/* init_task */
+  .data.init_task : { *(.data.init_task) }
+
+  . = ALIGN(4096);		/* Init code and data */
+  __init_begin = .;
+  .text.init : { *(.text.init) }
+  .data.init : { *(.data.init) }
+  . = ALIGN(256);
+  __setup_start = .;
+  .setup.init : { *(.setup.init) }
+  __setup_end = .;
+  __initcall_start = .;
+  .initcall.init : {
+	*(.initcall1.init) 
+	*(.initcall2.init) 
+	*(.initcall3.init) 
+	*(.initcall4.init) 
+	*(.initcall5.init) 
+	*(.initcall6.init) 
+	*(.initcall7.init)
+  }
+  __initcall_end = .;
+  . = ALIGN(256);
+  __per_cpu_start = .;
+  .date.percpu  : { *(.data.percpu) }
+  __per_cpu_end = .;
+  . = ALIGN(4096);
+  __init_end = .;
+
+  . = ALIGN(32);
+  .data.cacheline_aligned : { *(.data.cacheline_aligned) }
+
+  . = ALIGN(4096);
+  .data.page_aligned : { *(.data.idt) }
+
+  __bss_start = .;		/* BSS */
+  .bss : {
+	*(.bss)
+	}
+  _end = . ;
+
+  /* Stabs debugging sections.  */
+  .stab 0 : { *(.stab) }
+  .stabstr 0 : { *(.stabstr) }
+  .stab.excl 0 : { *(.stab.excl) }
+  .stab.exclstr 0 : { *(.stab.exclstr) }
+  .stab.index 0 : { *(.stab.index) }
+  .stab.indexstr 0 : { *(.stab.indexstr) }
+  .comment 0 : { *(.comment) }
+}
diff -Nru a/arch/s390x/Config.help b/arch/s390x/Config.help
--- a/arch/s390x/Config.help	Mon Oct  7 11:25:26 2002
+++ b/arch/s390x/Config.help	Mon Oct  7 11:25:26 2002
@@ -159,24 +159,10 @@
   Select "vm_reader" if you are running under VM/ESA and want
   to IPL the image from the emulated card reader.
 
-CONFIG_FAST_IRQ
-  Select this option in order to get the interrupts processed faster
-  on your S/390 or zSeries machine.  If selected, after an interrupt
-  is processed, the channel subsystem will be asked for other pending
-  interrupts which will also be processed before leaving the interrupt
-  context.  This speeds up the I/O a lot. Say "Y".
-
 CONFIG_MACHCHK_WARNING
   Select this option if you want the machine check handler on IBM S/390 or
   zSeries to process warning machine checks (e.g. on power failures). 
   If unsure, say "Y".
-
-CONFIG_CHSC
-  Select this option if you want the s390 common I/O layer to use information
-  obtained by channel subsystem calls. This will enable Linux to process link
-  failures and resource accessibility events. Moreover, if you have procfs
-  enabled, you'll be able to toggle chpids logically offline and online. Even
-  if you don't understand what this means, you should say "Y".
 
 CONFIG_S390_SUPPORT
   Select this option if you want to enable your system kernel to
diff -Nru a/arch/s390x/Makefile b/arch/s390x/Makefile
--- a/arch/s390x/Makefile	Mon Oct  7 11:25:35 2002
+++ b/arch/s390x/Makefile	Mon Oct  7 11:25:35 2002
@@ -26,7 +26,7 @@
 
 core-y		+= arch/s390x/mm/ arch/s390x/kernel/
 drivers-y	+= drivers/s390/
-libs-y		+= arch/s390/lib/
+libs-y		+= arch/s390x/lib/
 
 all: image listing
 
diff -Nru a/arch/s390x/config.in b/arch/s390x/config.in
--- a/arch/s390x/config.in	Mon Oct  7 11:25:25 2002
+++ b/arch/s390x/config.in	Mon Oct  7 11:25:25 2002
@@ -17,31 +17,30 @@
 source init/Config.in
 
 mainmenu_option next_comment
+comment 'Base setup'
 comment 'Processor type and features'
 bool 'Symmetric multi-processing support' CONFIG_SMP
 bool 'Kernel support for 31 bit emulation' CONFIG_S390_SUPPORT
 if [ "$CONFIG_S390_SUPPORT" = "y" ]; then
   tristate 'Kernel support for 31 bit ELF binaries' CONFIG_BINFMT_ELF32 
 fi
-endmenu
 
-mainmenu_option next_comment
-comment 'Base setup'
-bool 'Fast IRQ handling' CONFIG_FAST_IRQ
+comment 'I/O subsystem configuration'
 bool 'Process warning machine checks' CONFIG_MACHCHK_WARNING
-bool 'Use chscs for Common I/O' CONFIG_CHSC
-  
 tristate 'QDIO support' CONFIG_QDIO
-  if [ "$CONFIG_QDIO" != "n" ]; then
-    bool '   Performance statistics in /proc' CONFIG_QDIO_PERF_STATS
-  fi
+if [ "$CONFIG_QDIO" != "n" ]; then
+  bool '   Performance statistics in /proc' CONFIG_QDIO_PERF_STATS
+fi
 
+comment 'Misc'
+bool 'Preemptible Kernel' CONFIG_PREEMPT
 bool 'Builtin IPL record support' CONFIG_IPL
 if [ "$CONFIG_IPL" = "y" ]; then
   choice 'IPL method generated into head.S' \
           "tape                   CONFIG_IPL_TAPE \
            vm_reader              CONFIG_IPL_VM" tape
 fi
+
 define_bool CONFIG_KCORE_ELF y
 tristate 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF
 tristate 'Kernel support for MISC binaries' CONFIG_BINFMT_MISC
@@ -71,7 +70,6 @@
 mainmenu_option next_comment
 comment 'Kernel hacking'
 
-#bool 'Debug kmalloc/kfree' CONFIG_DEBUG_MALLOC
 #if [ "$CONFIG_CTC" = "y" ]; then
 #  bool 'Remote GDB kernel debugging' CONFIG_REMOTE_DEBUG
 #fi
diff -Nru a/arch/s390x/defconfig b/arch/s390x/defconfig
--- a/arch/s390x/defconfig	Mon Oct  7 11:25:25 2002
+++ b/arch/s390x/defconfig	Mon Oct  7 11:25:25 2002
@@ -31,6 +31,10 @@
 CONFIG_KMOD=y
 
 #
+# Base setup
+#
+
+#
 # Processor type and features
 #
 CONFIG_SMP=y
@@ -38,13 +42,16 @@
 CONFIG_BINFMT_ELF32=y
 
 #
-# Base setup
+# I/O subsystem configuration
 #
-CONFIG_FAST_IRQ=y
 CONFIG_MACHCHK_WARNING=y
-CONFIG_CHSC=y
-CONFIG_QDIO=m
+CONFIG_QDIO=y
 # CONFIG_QDIO_PERF_STATS is not set
+
+#
+# Misc
+#
+# CONFIG_PREEMPT is not set
 CONFIG_IPL=y
 # CONFIG_IPL_TAPE is not set
 CONFIG_IPL_VM=y
@@ -85,9 +92,6 @@
 #
 # CONFIG_SCSI_7000FASST is not set
 # CONFIG_SCSI_ACARD is not set
-# CONFIG_SCSI_AHA152X is not set
-# CONFIG_SCSI_AHA1542 is not set
-# CONFIG_SCSI_AHA1740 is not set
 # CONFIG_SCSI_AIC7XXX is not set
 # CONFIG_SCSI_AIC7XXX_OLD is not set
 # CONFIG_SCSI_DPT_I2O is not set
@@ -115,7 +119,6 @@
 # CONFIG_SCSI_PCI2220I is not set
 # CONFIG_SCSI_PSI240I is not set
 # CONFIG_SCSI_QLOGIC_FAS is not set
-# CONFIG_SCSI_SIM710 is not set
 # CONFIG_SCSI_SYM53C416 is not set
 # CONFIG_SCSI_T128 is not set
 # CONFIG_SCSI_U14_34F is not set
@@ -153,7 +156,7 @@
 CONFIG_MD_RAID1=m
 CONFIG_MD_RAID5=m
 # CONFIG_MD_MULTIPATH is not set
-CONFIG_BLK_DEV_LVM=m
+# CONFIG_BLK_DEV_LVM is not set
 
 #
 # Character device drivers
@@ -225,26 +228,23 @@
 # CONFIG_ARPD is not set
 # CONFIG_INET_ECN is not set
 # CONFIG_SYN_COOKIES is not set
-CONFIG_IPV6=m
-# CONFIG_KHTTPD is not set
-# CONFIG_ATM is not set
-# CONFIG_VLAN_8021Q is not set
+# CONFIG_IPV6 is not set
 
 #
-#  
+#    SCTP Configuration (EXPERIMENTAL)
 #
+CONFIG_IPV6_SCTP__=y
+# CONFIG_IP_SCTP is not set
+# CONFIG_ATM is not set
+# CONFIG_VLAN_8021Q is not set
+# CONFIG_LLC is not set
 # CONFIG_IPX is not set
 # CONFIG_ATALK is not set
-
-#
-# Appletalk devices
-#
 # CONFIG_DEV_APPLETALK is not set
 # CONFIG_DECNET is not set
 # CONFIG_BRIDGE is not set
 # CONFIG_X25 is not set
 # CONFIG_LAPB is not set
-# CONFIG_LLC is not set
 # CONFIG_NET_DIVERT is not set
 # CONFIG_ECONET is not set
 # CONFIG_WAN_ROUTER is not set
@@ -299,7 +299,7 @@
 # CONFIG_HPFS_FS is not set
 CONFIG_PROC_FS=y
 CONFIG_DEVFS_FS=y
-CONFIG_DEVFS_MOUNT=y
+# CONFIG_DEVFS_MOUNT is not set
 # CONFIG_DEVFS_DEBUG is not set
 # CONFIG_DEVPTS_FS is not set
 # CONFIG_QNX4FS_FS is not set
@@ -311,6 +311,9 @@
 # CONFIG_UDF_RW is not set
 # CONFIG_UFS_FS is not set
 # CONFIG_UFS_FS_WRITE is not set
+# CONFIG_XFS_FS is not set
+# CONFIG_XFS_RT is not set
+# CONFIG_XFS_QUOTA is not set
 
 #
 # Network File Systems
@@ -320,12 +323,12 @@
 CONFIG_NFS_FS=y
 # CONFIG_NFS_V3 is not set
 # CONFIG_ROOT_NFS is not set
-# CONFIG_NFSD is not set
+CONFIG_NFSD=y
 # CONFIG_NFSD_V3 is not set
 # CONFIG_NFSD_TCP is not set
 CONFIG_SUNRPC=y
 CONFIG_LOCKD=y
-# CONFIG_EXPORTFS is not set
+CONFIG_EXPORTFS=y
 # CONFIG_SMB_FS is not set
 # CONFIG_NCP_FS is not set
 # CONFIG_NCPFS_PACKET_SIGNING is not set
@@ -361,6 +364,11 @@
 # Kernel hacking
 #
 CONFIG_MAGIC_SYSRQ=y
+
+#
+# Security options
+#
+CONFIG_SECURITY_CAPABILITIES=y
 
 #
 # Library routines
diff -Nru a/arch/s390x/kernel/Makefile b/arch/s390x/kernel/Makefile
--- a/arch/s390x/kernel/Makefile	Mon Oct  7 11:25:32 2002
+++ b/arch/s390x/kernel/Makefile	Mon Oct  7 11:25:32 2002
@@ -5,12 +5,12 @@
 EXTRA_TARGETS	:= head.o init_task.o
 EXTRA_AFLAGS	:= -traditional
 
-export-objs	:= debug.o ebcdic.o irq.o s390_ext.o smp.o s390_ksyms.o \
+export-objs	:= debug.o ebcdic.o s390_ext.o smp.o s390_ksyms.o \
 		   exec32.o
 
-obj-y		:= entry.o bitmap.o traps.o time.o process.o irq.o \
+obj-y		:= entry.o bitmap.o traps.o time.o process.o \
 		   setup.o sys_s390.o ptrace.o signal.o cpcmd.o ebcdic.o \
-		   semaphore.o s390fpu.o reipl.o s390_ext.o debug.o
+		   semaphore.o reipl.o s390_ext.o debug.o
 
 obj-$(CONFIG_MODULES)		+= s390_ksyms.o
 obj-$(CONFIG_SMP)		+= smp.o
diff -Nru a/arch/s390x/kernel/debug.c b/arch/s390x/kernel/debug.c
--- a/arch/s390x/kernel/debug.c	Mon Oct  7 11:25:26 2002
+++ b/arch/s390x/kernel/debug.c	Mon Oct  7 11:25:26 2002
@@ -21,6 +21,7 @@
 #include <asm/semaphore.h>
 
 #include <linux/module.h>
+#include <linux/init.h>
 
 #include <asm/debug.h>
 
@@ -146,7 +147,7 @@
 static debug_info_t *debug_area_last = NULL;
 DECLARE_MUTEX(debug_lock);
 
-static int initialized = 0;
+static int initialized;
 
 static struct file_operations debug_file_ops = {
 	read:    debug_output,
@@ -591,7 +592,7 @@
 
 	MOD_INC_USE_COUNT;
 	if (!initialized)
-		debug_init();
+		BUG();
 	down(&debug_lock);
 
         /* create new debug_info */
@@ -828,18 +829,16 @@
  * - is called exactly once to initialize the debug feature
  */
 
-int debug_init(void)
+static int __init debug_init(void)
 {
 	int rc = 0;
 
 	down(&debug_lock);
-	if (!initialized) {
 #ifdef CONFIG_PROC_FS
-		debug_proc_root_entry = proc_mkdir(DEBUG_DIR_ROOT, NULL);
+	debug_proc_root_entry = proc_mkdir(DEBUG_DIR_ROOT, NULL);
 #endif /* CONFIG_PROC_FS */
-		printk(KERN_INFO "debug: Initialization complete\n");
-		initialized = 1;
-	}
+	printk(KERN_INFO "debug: Initialization complete\n");
+	initialized = 1;
 	up(&debug_lock);
 
 	return rc;
@@ -1173,27 +1172,9 @@
 }
 
 /*
- * init_module:
- */
-
-#ifdef MODULE
-int init_module(void)
-{
-	int rc = 0;
-#ifdef DEBUG
-	printk("debug_module_init: \n");
-#endif
-	rc = debug_init();
-	if (rc) 
-		printk(KERN_INFO "debug: an error occurred with debug_init\n");
-	return rc;
-}
-
-/*
- * cleanup_module:
+ * clean up module
  */
-
-void cleanup_module(void)
+void __exit debug_exit(void)
 {
 #ifdef DEBUG
 	printk("debug_cleanup_module: \n");
@@ -1204,7 +1185,12 @@
 	return;
 }
 
-#endif			/* MODULE */
+/*
+ * module definitions
+ */
+core_initcall(debug_init);
+module_exit(debug_exit);
+MODULE_LICENSE("GPL");
 
 EXPORT_SYMBOL(debug_register);
 EXPORT_SYMBOL(debug_unregister); 
diff -Nru a/arch/s390x/kernel/entry.S b/arch/s390x/kernel/entry.S
--- a/arch/s390x/kernel/entry.S	Mon Oct  7 11:25:28 2002
+++ b/arch/s390x/kernel/entry.S	Mon Oct  7 11:25:28 2002
@@ -15,7 +15,6 @@
 #include <asm/cache.h>
 #include <asm/lowcore.h>
 #include <asm/errno.h>
-#include <asm/smp.h>
 #include <asm/ptrace.h>
 #include <asm/thread_info.h>
 
@@ -49,7 +48,7 @@
 SP_TRAP      =  (SP_ORIG_R2+GPR_SIZE)
 SP_SIZE      =  (SP_TRAP+4)
 
-_TIF_WORK_MASK = (_TIF_NOTIFY_RESUME | _TIF_SIGPENDING | _TIF_NEED_RESCHED)
+_TIF_WORK_MASK = (_TIF_SIGPENDING | _TIF_NEED_RESCHED)
 
 /*
  * Register usage in interrupt handlers:
@@ -185,32 +184,42 @@
         RESTORE_ALL 1
 
 #
-# One of the work bits _TIF_NOTIFY_RESUME, _TIF_SIGPENDING or
-# _TIF_NEED_RESCHED is on. Find out which one.
+# recheck if there is more work to do
+#
+sysc_work_loop:
+	stnsm   48(%r15),0xfc     # disable I/O and ext. interrupts
+        GET_THREAD_INFO           # load pointer to task_struct to R9
+	tm	__TI_flags+7(%r9),_TIF_WORK_MASK
+	jz	sysc_leave        # there is no work to do
+#
+# One of the work bits is on. Find out which one.
+# Checked are: _TIF_SIGPENDING and _TIF_NEED_RESCHED
 #
 sysc_work:
-        tm      SP_PSW+1(%r15),0x01 # returning to user ?
-        jno     sysc_leave        # no-> skip resched & signal
 	tm	__TI_flags+7(%r9),_TIF_NEED_RESCHED
 	jo	sysc_reschedule
-	# add a test for TIF_NOTIFY_RESUME here when it is used.
-	# _TIF_SIGPENDING is the only flag left
+	tm	__TI_flags+7(%r9),_TIF_SIGPENDING
+	jo	sysc_sigpending
+	j	sysc_leave
 
 #
-# call do_signal before return
-#
-sysc_signal_return:     
-        la      %r2,SP_PTREGS(%r15) # load pt_regs
-        sgr     %r3,%r3             # clear *oldset
-	larl    %r14,sysc_return
-	jg      do_signal           # return point is sysc_return
+# _TIF_NEED_RESCHED is set, call schedule
+#	
+sysc_reschedule:        
+	stosm   48(%r15),0x03     # reenable interrupts
+	larl    %r14,sysc_work_loop
+        jg      schedule            # return point is sysc_return
 
 #
-# call schedule with sysc_return as return-address
+# _TIF_SIGPENDING is set, call do_signal
 #
-sysc_reschedule:        
-	larl    %r14,sysc_return
-        jg      schedule            # return point is sysc_return
+sysc_sigpending:     
+	stosm   48(%r15),0x03     # reenable interrupts
+        la      %r2,SP_PTREGS(%r15) # load pt_regs
+        sgr     %r3,%r3           # clear *oldset
+	brasl	%r14,do_signal    # call do_signal
+	stnsm   48(%r15),0xfc     # disable I/O and ext. interrupts
+	j	sysc_leave        # out of here, do NOT recheck
 
 #
 # call syscall_trace before and after system call
@@ -242,8 +251,7 @@
         .globl  ret_from_fork
 ret_from_fork:  
         GET_THREAD_INFO           # load pointer to task_struct to R9
-	xc      SP_R2(8,%r15),SP_R2(%r15) # child returns 0
-#ifdef CONFIG_SMP
+#if CONFIG_SMP || CONFIG_PREEMPT
 	larl    %r14,sysc_return
         jg      schedule_tail     # return to sysc_return
 #else
@@ -551,8 +559,8 @@
         .long  SYSCALL(sys_rt_sigtimedwait,sys32_rt_sigtimedwait_wrapper)
         .long  SYSCALL(sys_rt_sigqueueinfo,sys32_rt_sigqueueinfo_wrapper)
         .long  SYSCALL(sys_rt_sigsuspend_glue,sys32_rt_sigsuspend_glue)
-        .long  SYSCALL(sys_pread,sys32_pread_wrapper)           /* 180 */
-        .long  SYSCALL(sys_pwrite,sys32_pwrite_wrapper)
+        .long  SYSCALL(sys_pread64,sys32_pread64_wrapper)           /* 180 */
+        .long  SYSCALL(sys_pwrite64,sys32_pwrite64_wrapper)
         .long  SYSCALL(sys_ni_syscall,sys32_chown16_wrapper)    /* old chown16 syscall */
         .long  SYSCALL(sys_getcwd,sys32_getcwd_wrapper)
         .long  SYSCALL(sys_capget,sys32_capget_wrapper)
@@ -606,13 +614,21 @@
 	.long  SYSCALL(sys_flistxattr,sys32_flistxattr_wrapper)
 	.long  SYSCALL(sys_removexattr,sys32_removexattr_wrapper)
 	.long  SYSCALL(sys_lremovexattr,sys32_lremovexattr_wrapper)
-	.long  SYSCALL(sys_fremovexattr,sys32_fremovexattr_wrapper)
+	.long  SYSCALL(sys_fremovexattr,sys32_fremovexattr_wrapper) /* 235 */
 	.long  SYSCALL(sys_gettid,sys_gettid)
 	.long  SYSCALL(sys_tkill,sys_tkill)
 	.long  SYSCALL(sys_futex,sys32_futex_wrapper)
 	.long  SYSCALL(sys_sched_setaffinity,sys32_sched_setaffinity_wrapper)
-	.long  SYSCALL(sys_sched_getaffinity,sys32_sched_getaffinity_wrapper)
-        .rept  255-240
+	.long  SYSCALL(sys_sched_getaffinity,sys32_sched_getaffinity_wrapper) /* 240 */
+	.long  SYSCALL(sys_security,sys_ni_syscall)
+	.long  SYSCALL(sys_ni_syscall,sys_ni_syscall) /* reserved for TUX */
+	.long  SYSCALL(sys_io_setup,sys_ni_syscall)
+	.long  SYSCALL(sys_io_destroy,sys_ni_syscall)
+	.long  SYSCALL(sys_io_getevents,sys_ni_syscall)       /* 245 */
+	.long  SYSCALL(sys_io_submit,sys_ni_syscall)
+	.long  SYSCALL(sys_io_cancel,sys_ni_syscall)
+	.long  SYSCALL(sys_exit_group,sys32_exit_group_wrapper)
+        .rept  255-248
 	.long  SYSCALL(sys_ni_syscall,sys_ni_syscall)
 	.endr
 
@@ -638,13 +654,15 @@
         tm      __LC_PGM_INT_CODE+1,0x80 # check whether we got a per exception
         jnz     pgm_per                  # got per exception -> special case
 	SAVE_ALL __LC_PGM_OLD_PSW,1
-	llgh    %r8,__LC_PGM_INT_CODE
+	lghi	%r8,0x7f
+	lgf     %r3,__LC_PGM_ILC	 # load program interruption code
+	ngr	%r8,%r3
         sll     %r8,3
 	GET_THREAD_INFO
+	stosm   48(%r15),0x03            # reenable interrupts
         larl    %r1,pgm_check_table
         lg      %r1,0(%r8,%r1)		 # load address of handler routine
         la      %r2,SP_PTREGS(%r15)	 # address of register-save area
-	lgf     %r3,__LC_PGM_ILC	 # load program interruption code
 	larl	%r14,sysc_return
         br      %r1			 # branch to interrupt-handler
 
@@ -668,6 +686,7 @@
 	GET_THREAD_INFO
 	lghi    %r4,0x7f
 	lgf     %r3,__LC_PGM_ILC	 # load program interruption code
+	stosm   48(%r15),0x03            # reenable interrupts
         nr      %r4,%r3			 # clear per-event-bit and ilc
         je      pgm_per_only		 # only per of per+check ?
         sll     %r4,3
@@ -751,57 +770,79 @@
 	brasl   %r14,do_IRQ            # call standard irq handler
 
 io_return:
-#
-# check, if bottom-half has to be done
-#
-        lgf     %r1,__TI_cpu(%r9)
-	larl    %r2,irq_stat
-	sll     %r1,L1_CACHE_SHIFT
-	la      %r1,0(%r1,%r2)
-	icm     %r0,15,0(%r1)          # test irq_stat[#cpu].__softirq_pending
-        jnz     io_handle_bottom_half
-io_return_bh:	
+        tm      SP_PSW+1(%r15),0x01    # returning to user ?
+#ifdef CONFIG_PREEMPT
+	jno     io_preempt             # no -> check for preemptive scheduling
+#else
+        jno     io_leave               # no-> skip resched & signal
+#endif
 	tm	__TI_flags+7(%r9),_TIF_WORK_MASK
 	jnz	io_work                # there is work to do (signals etc.)
 io_leave:
-        stnsm   48(%r15),0xfc          # disable I/O and ext. interrupts
         RESTORE_ALL 0
 
+#ifdef CONFIG_PREEMPT
+io_preempt:
+	icm	%r0,15,__TI_precount(%r9)	
+	jnz     io_leave
+io_resume_loop:
+	tm	__TI_flags+7(%r9),_TIF_NEED_RESCHED
+	jno	io_leave
+	larl    %r1,.Lc_pactive
+	mvc     __TI_precount(4,%r9),0(%r1)
+	# hmpf, we are on the async. stack but to call schedule
+	# we have to move the interrupt frame to the process stack
+	lg	%r1,SP_R15(%r15)
+	aghi	%r1,-SP_SIZE
+	nill	%r1,0xfff8
+	mvc	SP_PTREGS(SP_SIZE-SP_PTREGS,%r1),SP_PTREGS(%r15)
+        xc      0(8,%r1),0(%r1)        # clear back chain
+	lgr	%r15,%r1
+        stosm   48(%r15),0x03          # reenable interrupts
+	brasl   %r14,schedule          # call schedule
+        stnsm   48(%r15),0xfc          # disable I/O and ext. interrupts
+        GET_THREAD_INFO                # load pointer to task_struct to R9
+	xc      __TI_precount(4,%r9),__TI_precount(%r9)
+	j	io_resume_loop
+#endif
+
 #
-# call do_softirq
+# recheck if there is more work to do
 #
-io_handle_bottom_half:        
-	larl    %r14,io_return_bh
-        jg      do_softirq             # return point is io_return_bh
-
+io_work_loop:
+        stnsm   48(%r15),0xfc          # disable I/O and ext. interrupts
+        GET_THREAD_INFO                # load pointer to task_struct to R9
+	tm	__TI_flags+7(%r9),_TIF_WORK_MASK
+	jz	io_leave               # there is no work to do
 #
-# One of the work bits _TIF_NOTIFY_RESUME, _TIF_SIGPENDING or
-# _TIF_NEED_RESCHED is on. Find out which one.
+# One of the work bits is on. Find out which one.
+# Checked are: _TIF_SIGPENDING and _TIF_NEED_RESCHED
 #
 io_work:
-        tm      SP_PSW+1(%r15),0x01    # returning to user ?
-        jno     io_leave               # no-> skip resched & signal
-        stosm   48(%r15),0x03          # reenable interrupts
 	tm	__TI_flags+7(%r9),_TIF_NEED_RESCHED
 	jo	io_reschedule
-	# add a test for TIF_NOTIFY_RESUME here when it is used.
-	# _TIF_SIGPENDING is the only flag left
+	tm	__TI_flags+7(%r9),_TIF_SIGPENDING
+	jo	io_sigpending
+	j	io_leave
 
 #
-# call do_signal before return
-#
-io_signal_return:     
-        la      %r2,SP_PTREGS(%r15) # load pt_regs
-        slgr    %r3,%r3             # clear *oldset
-	larl    %r14,io_leave
-        jg      do_signal           # return point is io_leave
+# _TIF_NEED_RESCHED is set, call schedule
+#	
+io_reschedule:        
+        stosm   48(%r15),0x03       # reenable interrupts
+	larl    %r14,io_work_loop
+        jg      schedule            # call scheduler
 
 #
-# call schedule with io_return as return-address
+# _TIF_SIGPENDING is set, call do_signal
 #
-io_reschedule:        
-	larl    %r14,io_return
-        jg      schedule            # call scheduler, return to io_return
+io_sigpending:     
+        stosm   48(%r15),0x03       # reenable interrupts
+        la      %r2,SP_PTREGS(%r15) # load pt_regs
+        slgr    %r3,%r3             # clear *oldset
+	brasl	%r14,do_signal      # call do_signal
+        stnsm   48(%r15),0xfc       # disable I/O and ext. interrupts
+	j	sysc_leave          # out of here, do NOT recheck
 
 /*
  * External interrupt handler routine
@@ -875,4 +916,5 @@
  */
                .align 4
 .Lc_ac:        .long  0,0,1
+.Lc_pactive:   .long  PREEMPT_ACTIVE
 .Lc256:        .quad  256
diff -Nru a/arch/s390x/kernel/head.S b/arch/s390x/kernel/head.S
--- a/arch/s390x/kernel/head.S	Mon Oct  7 11:25:26 2002
+++ b/arch/s390x/kernel/head.S	Mon Oct  7 11:25:26 2002
@@ -555,6 +555,17 @@
 	oi     7(%r12),16               # set MVPG flag
 0:
 
+#
+# find out if the diag 0x44 works in 64 bit mode
+#
+	la     %r1,0f-.LPG1(%r13)	# set program check address
+	stg    %r1,__LC_PGM_NEW_PSW+8
+	mvc    __LC_DIAG44_OPCODE(8),.Lnop-.LPG1(%r13)
+	diag   0,0,0x44			# test diag 0x44
+	oi     7(%r12),32		# set diag44 flag
+	mvc    __LC_DIAG44_OPCODE(8),.Ldiag44-.LPG1(%r13)
+0:	
+
         lpswe .Lentry-.LPG1(13)         # jump to _stext in primary-space,
                                         # virtual and never return ...
         .align 16
@@ -578,6 +589,8 @@
 .Lpcmsk:.quad  0x0000000180000000
 .L4malign:.quad 0xffffffffffc00000
 .Lscan2g:.quad 0x80000000 + 0x20000 - 8 # 2GB + 128K - 8
+.Lnop:	.long  0x07000700
+.Ldiag44:.long 0x83000044
 
 	.org PARMAREA-64
 .Lduct:	.long 0,0,0,0,0,0,0,0
@@ -645,5 +658,5 @@
 #
             .align 8
 .Ldw:       .quad  0x0002000180000000,0x0000000000000000
-.Laregs:    .long  0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0
+.Laregs:    .long  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
 
diff -Nru a/arch/s390x/kernel/init_task.c b/arch/s390x/kernel/init_task.c
--- a/arch/s390x/kernel/init_task.c	Mon Oct  7 11:25:31 2002
+++ b/arch/s390x/kernel/init_task.c	Mon Oct  7 11:25:31 2002
@@ -15,7 +15,7 @@
 
 static struct fs_struct init_fs = INIT_FS;
 static struct files_struct init_files = INIT_FILES;
-static struct signal_struct init_signals = INIT_SIGNALS;
+static struct signal_struct init_signals = INIT_SIGNALS(init_signals);
 struct mm_struct init_mm = INIT_MM(init_mm);
 
 /*
diff -Nru a/arch/s390x/kernel/irq.c b/arch/s390x/kernel/irq.c
--- a/arch/s390x/kernel/irq.c	Mon Oct  7 11:25:40 2002
+++ /dev/null	Wed Dec 31 16:00:00 1969
@@ -1,361 +0,0 @@
-/*
- *  arch/s390/kernel/irq.c
- *
- *  S390 version
- *    Copyright (C) 1999,2000 IBM Deutschland Entwicklung GmbH, IBM Corporation
- *    Author(s): Ingo Adlung (adlung@de.ibm.com)
- *
- *  Derived from "arch/i386/kernel/irq.c"
- *    Copyright (C) 1992, 1999 Linus Torvalds, Ingo Molnar
- *
- *  S/390 I/O interrupt processing and I/O request processing is
- *   implemented in arch/s390/kernel/s390io.c
- */
-#include <linux/module.h>
-#include <linux/config.h>
-#include <linux/ptrace.h>
-#include <linux/errno.h>
-#include <linux/kernel_stat.h>
-#include <linux/signal.h>
-#include <linux/sched.h>
-#include <linux/ioport.h>
-#include <linux/interrupt.h>
-#include <linux/timex.h>
-#include <linux/slab.h>
-#include <linux/string.h>
-#include <linux/random.h>
-#include <linux/smp.h>
-#include <linux/threads.h>
-#include <linux/smp_lock.h>
-#include <linux/init.h>
-#include <linux/seq_file.h>
-
-#include <asm/system.h>
-#include <asm/io.h>
-#include <asm/irq.h>
-#include <asm/bitops.h>
-#include <asm/smp.h>
-#include <asm/pgtable.h>
-#include <asm/delay.h>
-#include <asm/lowcore.h>
-
-void          s390_init_IRQ   ( void );
-void          s390_free_irq   ( unsigned int irq, void *dev_id);
-int           s390_request_irq( unsigned int irq,
-                     void           (*handler)(int, void *, struct pt_regs *),
-                     unsigned long  irqflags,
-                     const char    *devname,
-                     void          *dev_id);
-
-#if 0
-/*
- * The following vectors are part of the Linux architecture, there
- * is no hardware IRQ pin equivalent for them, they are triggered
- * through the ICC by us (IPIs), via smp_message_pass():
- */
-BUILD_SMP_INTERRUPT(reschedule_interrupt)
-BUILD_SMP_INTERRUPT(invalidate_interrupt)
-BUILD_SMP_INTERRUPT(stop_cpu_interrupt)
-BUILD_SMP_INTERRUPT(mtrr_interrupt)
-BUILD_SMP_INTERRUPT(spurious_interrupt)
-#endif
-
-int show_interrupts(struct seq_file *p, void *v)
-{
-	int i, j;
-
-	seq_puts(p, "           ");
-
-	for (j=0; j<smp_num_cpus; j++)
-		seq_printf(p, "CPU%d       ",j);
-
-	seq_putc(p, '\n');
-
-	for (i = 0 ; i < NR_IRQS ; i++)
-	{
-		if (ioinfo[i] == INVALID_STORAGE_AREA)
-			continue;
-
-		seq_printf(p, "%3d: ",i);
-		seq_printf(p, "  %s", ioinfo[i]->irq_desc.name);
-
-		seq_putc(p, '\n');
-	
-	} /* endfor */
-
-	return 0;
-}
-
-/*
- * Global interrupt locks for SMP. Allow interrupts to come in on any
- * CPU, yet make cli/sti act globally to protect critical regions..
- */
-#ifdef CONFIG_SMP
-atomic_t global_irq_holder = ATOMIC_INIT(NO_PROC_ID);
-atomic_t global_irq_lock = ATOMIC_INIT(0);
-atomic_t global_irq_count = ATOMIC_INIT(0);
-atomic_t global_bh_count;
-
-/*
- * "global_cli()" is a special case, in that it can hold the
- * interrupts disabled for a longish time, and also because
- * we may be doing TLB invalidates when holding the global
- * IRQ lock for historical reasons. Thus we may need to check
- * SMP invalidate events specially by hand here (but not in
- * any normal spinlocks)
- *
- * Thankfully we don't need this as we can deliver flush tlbs with
- * interrupts disabled DJB :-)
- */
-#define check_smp_invalidate(cpu)
-
-extern void show_stack(unsigned long* esp);
-
-static void show(char * str)
-{
-	int cpu = smp_processor_id();
-
-	printk("\n%s, CPU %d:\n", str, cpu);
-	printk("irq:  %d [%d]\n",
-	       atomic_read(&global_irq_count),local_irq_count(smp_processor_id()));
-	printk("bh:   %d [%d]\n",
-	       atomic_read(&global_bh_count),local_bh_count(smp_processor_id()));
-	show_stack(NULL);
-}
-
-#define MAXCOUNT 100000000
-
-static inline void wait_on_bh(void)
-{
-	int count = MAXCOUNT;
-	do {
-		if (!--count) {
-			show("wait_on_bh");
-			count = ~0;
-		}
-		/* nothing .. wait for the other bh's to go away */
-	} while (atomic_read(&global_bh_count) != 0);
-}
-
-static inline void wait_on_irq(int cpu)
-{
-	int count = MAXCOUNT;
-
-	for (;;) {
-
-		/*
-		 * Wait until all interrupts are gone. Wait
-		 * for bottom half handlers unless we're
-		 * already executing in one..
-		 */
-		if (!atomic_read(&global_irq_count)) {
-			if (local_bh_count(cpu)||
-			    !atomic_read(&global_bh_count))
-				break;
-		}
-
-		/* Duh, we have to loop. Release the lock to avoid deadlocks */
-                atomic_set(&global_irq_lock, 0);
-
-		for (;;) {
-			if (!--count) {
-				show("wait_on_irq");
-				count = ~0;
-			}
-			local_irq_enable();
-			SYNC_OTHER_CORES(cpu);
-			local_irq_disable();
-			check_smp_invalidate(cpu);
-			if (atomic_read(&global_irq_count))
-				continue;
-			if (atomic_read(&global_irq_lock))
-				continue;
-			if (!local_bh_count(cpu)
-			    && atomic_read(&global_bh_count))
-				continue;
-                        if (!atomic_compare_and_swap(0, 1, &global_irq_lock))
-				break;
-		}
-	}
-}
-
-/*
- * This is called when we want to synchronize with
- * bottom half handlers. We need to wait until
- * no other CPU is executing any bottom half handler.
- *
- * Don't wait if we're already running in an interrupt
- * context or are inside a bh handler.
- */
-void synchronize_bh(void)
-{
-	if (atomic_read(&global_bh_count) && !in_interrupt())
-		wait_on_bh();
-}
-
-/*
- * This is called when we want to synchronize with
- * interrupts. We may for example tell a device to
- * stop sending interrupts: but to make sure there
- * are no interrupts that are executing on another
- * CPU we need to call this function.
- */
-void synchronize_irq(void)
-{
-	if (atomic_read(&global_irq_count)) {
-		/* Stupid approach */
-		cli();
-		sti();
-	}
-}
-
-static inline void get_irqlock(int cpu)
-{
-	if (atomic_compare_and_swap(0,1,&global_irq_lock) != 0) {
-		/* do we already hold the lock? */
-		if ( cpu == atomic_read(&global_irq_holder))
-			return;
-		/* Uhhuh.. Somebody else got it. Wait.. */
-		do {
-			check_smp_invalidate(cpu);
-		} while (atomic_compare_and_swap(0,1,&global_irq_lock) != 0);
-	}
-	/*
-	 * We also to make sure that nobody else is running
-	 * in an interrupt context.
-	 */
-	wait_on_irq(cpu);
-
-	/*
-	 * Ok, finally..
-	 */
-	atomic_set(&global_irq_holder,cpu);
-}
-
-#define EFLAGS_I_SHIFT 57
-
-/*
- * A global "cli()" while in an interrupt context
- * turns into just a local cli(). Interrupts
- * should use spinlocks for the (very unlikely)
- * case that they ever want to protect against
- * each other.
- *
- * If we already have local interrupts disabled,
- * this will not turn a local disable into a
- * global one (problems with spinlocks: this makes
- * save_flags+cli+sti usable inside a spinlock).
- */
-void __global_cli(void)
-{
-	unsigned long flags;
-
-	local_save_flags(flags);
-	if (flags & (1UL << EFLAGS_I_SHIFT)) {
-		int cpu = smp_processor_id();
-		local_irq_disable();
-		if (!in_irq())
-			get_irqlock(cpu);
-	}
-}
-
-void __global_sti(void)
-{
-
-	if (!in_irq())
-		release_irqlock(smp_processor_id());
-	local_irq_enable();
-}
-
-/*
- * SMP flags value to restore to:
- * 0 - global cli
- * 1 - global sti
- * 2 - local cli
- * 3 - local sti
- */
-unsigned long __global_save_flags(void)
-{
-	int retval;
-	int local_enabled;
-	unsigned long flags;
-
-	local_save_flags(flags);
-	local_enabled = (flags >> EFLAGS_I_SHIFT) & 1;
-	/* default to local */
-	retval = 2 + local_enabled;
-
-	/* check for global flags if we're not in an interrupt */
-	if (!in_irq())
-	{
-		if (local_enabled)
-			retval = 1;
-		if (atomic_read(&global_irq_holder)== smp_processor_id())
-			retval = 0;
-	}
-	return retval;
-}
-
-void __global_restore_flags(unsigned long flags)
-{
-	switch (flags) {
-	case 0:
-		__global_cli();
-		break;
-	case 1:
-		__global_sti();
-		break;
-	case 2:
-		local_irq_disable();
-		break;
-	case 3:
-		local_irq_enable();
-		break;
-	default:
-		printk("global_restore_flags: %08lx (%08lx)\n",
-		       flags, (&flags)[-1]);
-	}
-}
-
-#endif
-
-
-void __init init_IRQ(void)
-{
-   s390_init_IRQ();
-}
-
-
-void free_irq(unsigned int irq, void *dev_id)
-{
-   s390_free_irq( irq, dev_id);
-}
-
-
-int request_irq( unsigned int   irq,
-                 void           (*handler)(int, void *, struct pt_regs *),
-                 unsigned long  irqflags,
-                 const char    *devname,
-                 void          *dev_id)
-{
-   return( s390_request_irq( irq, handler, irqflags, devname, dev_id ) );
-
-}
-
-void init_irq_proc(void)
-{
-        /* For now, nothing... */
-}
-
-#ifdef CONFIG_SMP
-EXPORT_SYMBOL(__global_cli);
-EXPORT_SYMBOL(__global_sti);
-EXPORT_SYMBOL(__global_save_flags);
-EXPORT_SYMBOL(__global_restore_flags);
-EXPORT_SYMBOL(global_irq_holder);
-EXPORT_SYMBOL(global_irq_lock);
-EXPORT_SYMBOL(global_irq_count);
-EXPORT_SYMBOL(global_bh_count);
-#endif
-
-EXPORT_SYMBOL(global_bh_lock);
diff -Nru a/arch/s390x/kernel/linux32.c b/arch/s390x/kernel/linux32.c
--- a/arch/s390x/kernel/linux32.c	Mon Oct  7 11:25:26 2002
+++ b/arch/s390x/kernel/linux32.c	Mon Oct  7 11:25:26 2002
@@ -514,16 +514,15 @@
 	if (!p)
 		return -ENOMEM;
 
+	err = -EINVAL;
 	if (second > MSGMAX || first < 0 || second < 0)
-		return -EINVAL;
+		goto out;
 
 	err = -EFAULT;
 	if (!uptr)
 		goto out;
-
-	err = get_user (p->mtype, &up->mtype);
-	err |= __copy_from_user (p->mtext, &up->mtext, second);
-	if (err)
+        if (get_user (p->mtype, &up->mtype) ||
+	    __copy_from_user (p->mtext, &up->mtext, second))
 		goto out;
 	old_fs = get_fs ();
 	set_fs (KERNEL_DS);
@@ -1953,15 +1952,17 @@
 			return -EINVAL;
 	}
 
-	spin_lock_irq(&current->sigmask_lock);
-	sig = dequeue_signal(&these, &info);
+	spin_lock_irq(&current->sig->siglock);
+	sig = dequeue_signal(&current->sig->shared_pending, &these, &info);
+	if (!sig)
+		sig = dequeue_signal(&current->pending, &these, &info);
 	if (!sig) {
 		/* None ready -- temporarily unblock those we're interested
 		   in so that we'll be awakened when they arrive.  */
-		sigset_t oldblocked = current->blocked;
+		current->real_blocked = current->blocked;
 		sigandsets(&current->blocked, &current->blocked, &these);
 		recalc_sigpending();
-		spin_unlock_irq(&current->sigmask_lock);
+		spin_unlock_irq(&current->sig->siglock);
 
 		timeout = MAX_SCHEDULE_TIMEOUT;
 		if (uts)
@@ -1971,12 +1972,15 @@
 		current->state = TASK_INTERRUPTIBLE;
 		timeout = schedule_timeout(timeout);
 
-		spin_lock_irq(&current->sigmask_lock);
-		sig = dequeue_signal(&these, &info);
-		current->blocked = oldblocked;
+		spin_lock_irq(&current->sig->siglock);
+		sig = dequeue_signal(&current->sig->shared_pending, &these, &info);
+		if (!sig)
+			sig = dequeue_signal(&current->pending, &these, &info);
+		current->blocked = current->real_blocked;
+		siginitset(&current->real_blocked, 0);
 		recalc_sigpending();
 	}
-	spin_unlock_irq(&current->sigmask_lock);
+	spin_unlock_irq(&current->sig->siglock);
 
 	if (sig) {
 		ret = sig;
@@ -3988,13 +3992,13 @@
 
 typedef __kernel_ssize_t32 ssize_t32;
 
-asmlinkage ssize_t32 sys32_pread(unsigned int fd, char *ubuf,
+asmlinkage ssize_t32 sys32_pread64(unsigned int fd, char *ubuf,
 				 __kernel_size_t32 count, u32 poshi, u32 poslo)
 {
 	return sys_pread64(fd, ubuf, count, ((loff_t)AA(poshi) << 32) | AA(poslo));
 }
 
-asmlinkage ssize_t32 sys32_pwrite(unsigned int fd, char *ubuf,
+asmlinkage ssize_t32 sys32_pwrite64(unsigned int fd, char *ubuf,
 				  __kernel_size_t32 count, u32 poshi, u32 poslo)
 {
 	return sys_pwrite64(fd, ubuf, count, ((loff_t)AA(poshi) << 32) | AA(poslo));
@@ -4522,3 +4526,34 @@
 
 	return ret;
 }
+
+asmlinkage int 
+sys_futex(void *uaddr, int op, int val, struct timespec *utime);
+
+asmlinkage int
+sys32_futex(void *uaddr, int op, int val, 
+		 struct timespec32 *timeout32)
+{
+	long ret;
+	struct timespec tmp, *timeout;
+
+	ret = -ENOMEM;
+	timeout = kmalloc(sizeof(*timeout), GFP_USER);
+	if (!timeout)
+		goto out;
+
+	ret = -EINVAL;
+	if (get_user (tmp.tv_sec,  &timeout32->tv_sec)  ||
+	    get_user (tmp.tv_nsec, &timeout32->tv_nsec) ||
+	    put_user (tmp.tv_sec,  &timeout->tv_sec)    ||
+	    put_user (tmp.tv_nsec, &timeout->tv_nsec))
+		goto out_free;
+
+	ret = sys_futex(uaddr, op, val, timeout);
+
+out_free:
+	kfree(timeout);
+out:
+	return ret;
+}
+
diff -Nru a/arch/s390x/kernel/linux32.h b/arch/s390x/kernel/linux32.h
--- a/arch/s390x/kernel/linux32.h	Mon Oct  7 11:25:39 2002
+++ b/arch/s390x/kernel/linux32.h	Mon Oct  7 11:25:39 2002
@@ -8,8 +8,6 @@
 #include <linux/nfsd/nfsd.h>
 #include <linux/nfsd/export.h>
 
-#ifdef CONFIG_S390_SUPPORT
-
 /* Macro that masks the high order bit of an 32 bit pointer and converts it*/
 /*       to a 64 bit pointer */
 #define A(__x) ((unsigned long)((__x) & 0x7FFFFFFFUL))
@@ -194,6 +192,32 @@
         __u32	addr;
 } _psw_t32 __attribute__ ((aligned(8)));
 
+#define PSW32_MASK_PER		0x40000000UL
+#define PSW32_MASK_DAT		0x04000000UL
+#define PSW32_MASK_IO		0x02000000UL
+#define PSW32_MASK_EXT		0x01000000UL
+#define PSW32_MASK_KEY		0x00F00000UL
+#define PSW32_MASK_MCHECK	0x00040000UL
+#define PSW32_MASK_WAIT		0x00020000UL
+#define PSW32_MASK_PSTATE	0x00010000UL
+#define PSW32_MASK_ASC		0x0000C000UL
+#define PSW32_MASK_CC		0x00003000UL
+#define PSW32_MASK_PM		0x00000f00UL
+
+#define PSW32_ADDR_AMODE31	0x80000000UL
+#define PSW32_ADDR_INSN		0x7FFFFFFFUL
+
+#define PSW32_BASE_BITS		0x00080000UL
+
+#define PSW32_ASC_PRIMARY	0x00000000UL
+#define PSW32_ASC_ACCREG	0x00004000UL
+#define PSW32_ASC_SECONDARY	0x00008000UL
+#define PSW32_ASC_HOME		0x0000C000UL
+
+#define PSW32_USER_BITS	(PSW32_BASE_BITS | PSW32_MASK_DAT | PSW32_ASC_HOME | \
+			 PSW32_MASK_IO | PSW32_MASK_EXT | PSW32_MASK_MCHECK | \
+			 PSW32_MASK_PSTATE)
+
 typedef struct
 {
 	_psw_t32	psw;
@@ -241,6 +265,4 @@
 	sigset_t32		uc_sigmask;	/* mask last for extensibility */
 };
 
-#endif /* !CONFIG_S390_SUPPORT */
- 
 #endif /* _ASM_S390X_S390_H */
diff -Nru a/arch/s390x/kernel/process.c b/arch/s390x/kernel/process.c
--- a/arch/s390x/kernel/process.c	Mon Oct  7 11:25:26 2002
+++ b/arch/s390x/kernel/process.c	Mon Oct  7 11:25:26 2002
@@ -15,9 +15,6 @@
  * This file handles the architecture-dependent parts of process handling..
  */
 
-#define __KERNEL_SYSCALLS__
-#include <stdarg.h>
-
 #include <linux/config.h>
 #include <linux/errno.h>
 #include <linux/sched.h>
@@ -78,9 +75,10 @@
 
 	/* 
 	 * Wait for external, I/O or machine check interrupt and
-	 * switch of machine check bit after the wait has ended.
+	 * switch off machine check bit after the wait has ended.
 	 */
-	wait_psw.mask = _WAIT_PSW_MASK;
+	wait_psw.mask = PSW_KERNEL_BITS | PSW_MASK_MCHECK | PSW_MASK_WAIT |
+		PSW_MASK_IO | PSW_MASK_EXT;
 	asm volatile (
 		"    larl  %0,0f\n"
 		"    stg   %0,8(%1)\n"
@@ -114,35 +112,39 @@
 
 	show_registers(regs);
 	/* Show stack backtrace if pt_regs is from kernel mode */
-	if (!(regs->psw.mask & PSW_PROBLEM_STATE))
+	if (!(regs->psw.mask & PSW_MASK_PSTATE))
 		show_trace((unsigned long *) regs->gprs[15]);
 }
 
+extern void kernel_thread_starter(void);
+__asm__(".align 4\n"
+	"kernel_thread_starter:\n"
+	"    lg    15,0(8)\n"
+	"    sgr   15,7\n"
+	"    stosm 48(15),3\n"
+	"    lgr   2,10\n"
+	"    basr  14,9\n"
+	"    sgr   2,2\n"
+	"    br    11\n");
+
 int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags)
 {
-        int clone_arg = flags | CLONE_VM;
-        int retval;
+	struct task_struct *p;
+	struct pt_regs regs;
 
-        __asm__ __volatile__(
-                "     slgr  2,2\n"
-                "     lgr   3,%1\n"
-                "     lg    4,%6\n"     /* load kernel stack ptr of parent */
-                "     svc   %b2\n"                     /* Linux system call*/
-                "     clg   4,%6\n"    /* compare ksp's: child or parent ? */
-                "     je    0f\n"                          /* parent - jump*/
-                "     lg    15,%6\n"            /* fix kernel stack pointer*/
-                "     aghi  15,%7\n"
-                "     xc    0(160,15),0(15)\n"          /* clear save area */
-                "     lgr   2,%4\n"                        /* load argument*/
-                "     basr  14,%5\n"                             /* call fn*/
-                "     svc   %b3\n"                     /* Linux system call*/
-                "0:   lgr   %0,2"
-                : "=a" (retval)
-                : "d" (clone_arg), "i" (__NR_clone), "i" (__NR_exit),
-                  "d" (arg), "a" (fn), "i" (__LC_KERNEL_STACK) ,
-                  "i" (-STACK_FRAME_OVERHEAD)
-                : "2", "3", "4" );
-        return retval;
+	memset(&regs, 0, sizeof(regs));
+	regs.psw.mask = PSW_KERNEL_BITS;
+	regs.psw.addr = (__u64) kernel_thread_starter;
+	regs.gprs[7] = STACK_FRAME_OVERHEAD;
+	regs.gprs[8] = __LC_KERNEL_STACK;
+	regs.gprs[9] = (unsigned long) fn;
+	regs.gprs[10] = (unsigned long) arg;
+	regs.gprs[11] = (unsigned long) do_exit;
+	regs.orig_gpr2 = -1;
+
+	/* Ok, create the new process.. */
+	p = do_fork(flags | CLONE_VM, 0, &regs, 0, NULL);
+	return IS_ERR(p) ? PTR_ERR(p) : p->pid;
 }
 
 /*
@@ -184,17 +186,20 @@
 		 (THREAD_SIZE + (unsigned long) p->thread_info)) - 1;
         p->thread.ksp = (unsigned long) frame;
         frame->childregs = *regs;
+	frame->childregs.gprs[2] = 0;	/* child returns 0 on fork. */
         frame->childregs.gprs[15] = new_stackp;
         frame->back_chain = frame->eos = 0;
 
-        /* new return point is ret_from_sys_call */
-        frame->gprs[8] = (unsigned long) &ret_from_fork;
+        /* new return point is ret_from_fork */
+        frame->gprs[8] = (unsigned long) ret_from_fork;
 
         /* fake return stack for resume(), don't go back to schedule */
         frame->gprs[9] = (unsigned long) frame;
-        /* save fprs, if used in last task */
+        /* save fprs */
 	save_fp_regs(&p->thread.fp_regs);
         p->thread.user_seg = __pa((unsigned long) p->mm->pgd) | _REGION_TABLE;
+	/* start new process with ar4 pointing to the correct address space */
+	p->thread.ar4 = get_fs().ar4;
         /* Don't copy debug registers */
         memset(&p->thread.per_info,0,sizeof(p->thread.per_info));
         return 0;
@@ -203,7 +208,7 @@
 asmlinkage int sys_fork(struct pt_regs regs)
 {
 	struct task_struct *p;
-        p = do_fork(SIGCHLD, regs.gprs[15], &regs, 0);
+        p = do_fork(SIGCHLD, regs.gprs[15], &regs, 0, NULL);
 	return IS_ERR(p) ? PTR_ERR(p) : p->pid;
 }
 
@@ -212,12 +217,14 @@
         unsigned long clone_flags;
         unsigned long newsp;
 	struct task_struct *p;
+	int *user_tid;
 
         clone_flags = regs.gprs[3];
         newsp = regs.orig_gpr2;
+	user_tid = (int *) regs.gprs[4];
         if (!newsp)
                 newsp = regs.gprs[15];
-        p = do_fork(clone_flags & ~CLONE_IDLETASK, newsp, &regs, 0);
+        p = do_fork(clone_flags & ~CLONE_IDLETASK, newsp, &regs, 0, user_tid);
 	return IS_ERR(p) ? PTR_ERR(p) : p->pid;
 }
 
@@ -234,7 +241,8 @@
 asmlinkage int sys_vfork(struct pt_regs regs)
 {
 	struct task_struct *p;
-	p = do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs.gprs[15], &regs, 0);
+	p = do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD,
+		    regs.gprs[15], &regs, 0, NULL);
 	return IS_ERR(p) ? PTR_ERR(p) : p->pid;
 }
 
@@ -250,15 +258,12 @@
         error = PTR_ERR(filename);
         if (IS_ERR(filename))
                 goto out;
-        error = do_execve(filename, (char **) regs.gprs[3], (char **) regs.gprs[4], &regs);
-	if (error == 0)
-	{
+        error = do_execve(filename, (char **) regs.gprs[3],
+			  (char **) regs.gprs[4], &regs);
+	if (error == 0) {
 		current->ptrace &= ~PT_DTRACE;
-		current->thread.fp_regs.fpc=0;
-		__asm__ __volatile__
-		        ("sr  0,0\n\t"
-		         "sfpc 0,0\n\t"
-			 : : :"0");
+		current->thread.fp_regs.fpc = 0;
+		asm volatile("sfpc %0,%0" : : "d" (0));
 	}
         putname(filename);
 out:
@@ -285,15 +290,15 @@
 	dump->magic = CMAGIC;
 	dump->start_code = 0;
 	dump->start_stack = regs->gprs[15] & ~(PAGE_SIZE - 1);
-	dump->u_tsize = ((unsigned long) current->mm->end_code) >> PAGE_SHIFT;
-	dump->u_dsize = ((unsigned long) (current->mm->brk + (PAGE_SIZE-1))) >> PAGE_SHIFT;
+	dump->u_tsize = current->mm->end_code >> PAGE_SHIFT;
+	dump->u_dsize = (current->mm->brk + PAGE_SIZE - 1) >> PAGE_SHIFT;
 	dump->u_dsize -= dump->u_tsize;
 	dump->u_ssize = 0;
 	if (dump->start_stack < TASK_SIZE)
-		dump->u_ssize = ((unsigned long) (TASK_SIZE - dump->start_stack)) >> PAGE_SHIFT;
-	memcpy(&dump->regs.gprs[0],regs,sizeof(s390_regs));
+		dump->u_ssize = (TASK_SIZE - dump->start_stack) >> PAGE_SHIFT;
+	memcpy(&dump->regs, regs, sizeof(s390_regs));
 	dump_fpu (regs, &dump->regs.fp_regs);
-	memcpy(&dump->regs.per_info,&current->thread.per_info,sizeof(per_struct));
+	dump->regs.per_info = current->thread.per_info;
 }
 
 /*
diff -Nru a/arch/s390x/kernel/ptrace.c b/arch/s390x/kernel/ptrace.c
--- a/arch/s390x/kernel/ptrace.c	Mon Oct  7 11:25:25 2002
+++ b/arch/s390x/kernel/ptrace.c	Mon Oct  7 11:25:25 2002
@@ -4,6 +4,7 @@
  *  S390 version
  *    Copyright (C) 1999,2000 IBM Deutschland Entwicklung GmbH, IBM Corporation
  *    Author(s): Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com),
+ *               Martin Schwidefsky (schwidefsky@de.ibm.com)
  *
  *  Based on PowerPC version 
  *    Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org)
@@ -38,49 +39,48 @@
 #include <asm/pgalloc.h>
 #include <asm/system.h>
 #include <asm/uaccess.h>
+
 #ifdef CONFIG_S390_SUPPORT
-#include "linux32.h"
-#else
-#define parent_31bit 0
+#include "ptrace32.h"
 #endif
 
-
-void FixPerRegisters(struct task_struct *task)
+static void FixPerRegisters(struct task_struct *task)
 {
-	struct pt_regs *regs = __KSTK_PTREGS(task);
-	per_struct *per_info=
-			(per_struct *)&task->thread.per_info;
+	struct pt_regs *regs;
+	per_struct *per_info;
 
+	regs = __KSTK_PTREGS(task);
+	per_info = (per_struct *) &task->thread.per_info;
 	per_info->control_regs.bits.em_instruction_fetch =
-	        per_info->single_step | per_info->instruction_fetch;
+		per_info->single_step | per_info->instruction_fetch;
 	
 	if (per_info->single_step) {
-		per_info->control_regs.bits.starting_addr=0;
+		per_info->control_regs.bits.starting_addr = 0;
 #ifdef CONFIG_S390_SUPPORT
-		if (current->thread.flags & S390_FLAG_31BIT) {
-			per_info->control_regs.bits.ending_addr=0x7fffffffUL;
-	        }
-		else 
-#endif      
-		{
-		per_info->control_regs.bits.ending_addr=-1L;
-		}
+		if (current->thread.flags & S390_FLAG_31BIT)
+			per_info->control_regs.bits.ending_addr = 0x7fffffffUL;
+		else
+#endif
+			per_info->control_regs.bits.ending_addr = -1;
 	} else {
-		per_info->control_regs.bits.starting_addr=
-		        per_info->starting_addr;
-		per_info->control_regs.bits.ending_addr=
-		        per_info->ending_addr;
+		per_info->control_regs.bits.starting_addr =
+			per_info->starting_addr;
+		per_info->control_regs.bits.ending_addr =
+			per_info->ending_addr;
 	}
-	/* if any of the control reg tracing bits are on 
-	   we switch on per in the psw */
+	/*
+	 * if any of the control reg tracing bits are on 
+	 * we switch on per in the psw
+	 */
 	if (per_info->control_regs.words.cr[0] & PER_EM_MASK)
-		regs->psw.mask |= PSW_PER_MASK;
+		regs->psw.mask |= PSW_MASK_PER;
 	else
-		regs->psw.mask &= ~PSW_PER_MASK;
-	if (per_info->control_regs.bits.storage_alt_space_ctl)
-		task->thread.user_seg |= USER_STD_MASK;
+		regs->psw.mask &= ~PSW_MASK_PER;
+
+	if (per_info->control_regs.bits.em_storage_alteration)
+		per_info->control_regs.bits.storage_alt_space_ctl = 1;
 	else
-		task->thread.user_seg &= ~USER_STD_MASK;
+		per_info->control_regs.bits.storage_alt_space_ctl = 0;
 }
 
 void set_single_step(struct task_struct *task)
@@ -99,424 +99,406 @@
 	FixPerRegisters (task);
 }
 
-int ptrace_usercopy(addr_t realuseraddr, addr_t copyaddr, int len,
-                    int tofromuser, int writeuser, unsigned long mask)
+/*
+ * Called by kernel/ptrace.c when detaching..
+ *
+ * Make sure single step bits etc are not set.
+ */
+void ptrace_disable(struct task_struct *child)
 {
-        unsigned long *realuserptr, *copyptr;
-	unsigned long tempuser;
-	int retval;
-
-        retval = 0;
-        realuserptr = (unsigned long *) realuseraddr;
-        copyptr = (unsigned long *) copyaddr;
+	/* make sure the single step bit is not set. */
+	clear_single_step(child);
+}
 
-	if (writeuser && realuserptr == NULL)
-		return 0;
+/*
+ * Read the word at offset addr from the user area of a process. The
+ * trouble here is that the information is littered over different
+ * locations. The process registers are found on the kernel stack,
+ * the floating point stuff and the trace settings are stored in
+ * the task structure. In addition the different structures in
+ * struct user contain pad bytes that should be read as zeroes.
+ * Lovely...
+ */
+static int peek_user(struct task_struct *child, addr_t addr, addr_t data)
+{
+	struct user *dummy = NULL;
+	addr_t offset;
+	__u64 tmp;
+
+	if ((addr & 7) || addr > sizeof(struct user) - 7)
+		return -EIO;
+
+	if (addr <= (addr_t) &dummy->regs.orig_gpr2) {
+		/*
+		 * psw, gprs, acrs and orig_gpr2 are stored on the stack
+		 */
+		tmp = *(__u64 *)((addr_t) __KSTK_PTREGS(child) + addr);
+
+	} else if (addr >= (addr_t) &dummy->regs.fp_regs &&
+		   addr < (addr_t) (&dummy->regs.fp_regs + 1)) {
+		/*
+		 * floating point regs. are stored in the thread structure
+		 */
+		offset = addr - (addr_t) &dummy->regs.fp_regs;
+		tmp = *(__u64 *)((addr_t) &child->thread.fp_regs + offset);
+
+	} else if (addr >= (addr_t) &dummy->regs.per_info &&
+		   addr < (addr_t) (&dummy->regs.per_info + 1)) {
+		/*
+		 * per_info is found in the thread structure
+		 */
+		offset = addr - (addr_t) &dummy->regs.per_info;
+		tmp = *(__u64 *)((addr_t) &child->thread.per_info + offset);
 
-	if (mask != -1L) {
-		tempuser = *realuserptr;
-		if (!writeuser) {
-			tempuser &= mask;
-			realuserptr = &tempuser;
-		}
-	}
-	if (tofromuser) {
-		if (writeuser) {
-			retval = copy_from_user(realuserptr, copyptr, len);
-		} else {
-			if (realuserptr == NULL)
-				retval = clear_user(copyptr, len);
-			else
-				retval = copy_to_user(copyptr,realuserptr,len);
-                        retval = retval ? -EIO : 0;
-		}      
-	} else {
-		if (writeuser)
-			memcpy(realuserptr, copyptr, len);
-		else
-			memcpy(copyptr, realuserptr, len);
-	}
-	if (mask != -1L && writeuser)
-                *realuserptr = (*realuserptr & mask) | (tempuser & ~mask);
-	return retval;
-}
+	} else
+		tmp = 0;
 
-#ifdef CONFIG_S390_SUPPORT
+	return put_user(tmp, (__u64 *) data);
+}
 
-typedef struct
+/*
+ * Write a word to the user area of a process at location addr. This
+ * operation does have an additional problem compared to peek_user.
+ * Stores to the program status word and on the floating point
+ * control register needs to get checked for validity.
+ */
+static int poke_user(struct task_struct *child, addr_t addr, addr_t data)
 {
-	__u32 cr[3];
-} per_cr_words32  __attribute__((packed));
+	struct user *dummy = NULL;
+	addr_t offset;
 
-typedef struct
-{
-	__u16          perc_atmid;          /* 0x096 */
-	__u32          address;             /* 0x098 */
-	__u8           access_id;           /* 0x0a1 */
-} per_lowcore_words32  __attribute__((packed));
+	if ((addr & 7) || addr > sizeof(struct user) - 7)
+		return -EIO;
 
-typedef struct
-{
-	union {
-		per_cr_words32   words;
-	} control_regs  __attribute__((packed));
-	/*
-	 * Use these flags instead of setting em_instruction_fetch
-	 * directly they are used so that single stepping can be
-	 * switched on & off while not affecting other tracing
-	 */
-	unsigned  single_step       : 1;
-	unsigned  instruction_fetch : 1;
-	unsigned                    : 30;
-	/*
-	 * These addresses are copied into cr10 & cr11 if single
-	 * stepping is switched off
-	 */
-	__u32     starting_addr;
-	__u32     ending_addr;
-	union {
-		per_lowcore_words32 words;
-	} lowcore; 
-} per_struct32 __attribute__((packed));
-
-struct user_regs_struct32
-{
-	_psw_t32 psw;
-	u32 gprs[NUM_GPRS];
-	u32 acrs[NUM_ACRS];
-	u32 orig_gpr2;
-	s390_fp_regs fp_regs;
-	/*
-	 * These per registers are in here so that gdb can modify them
-	 * itself as there is no "official" ptrace interface for hardware
-	 * watchpoints. This is the way intel does it.
-	 */
-	per_struct32 per_info;
-	u32  ieee_instruction_pointer; 
-	/* Used to give failing instruction back to user for ieee exceptions */
-};
-
-struct user32 {
-                                  /* We start with the registers, to mimic the way that "memory" is returned
-                                   from the ptrace(3,...) function.  */
-  struct user_regs_struct32 regs; /* Where the registers are actually stored */
-                                  /* The rest of this junk is to help gdb figure out what goes where */
-  u32 u_tsize;	                  /* Text segment size (pages). */
-  u32 u_dsize;	                  /* Data segment size (pages). */
-  u32 u_ssize;	                  /* Stack segment size (pages). */
-  u32 start_code;                 /* Starting virtual address of text. */
-  u32 start_stack;	          /* Starting virtual address of stack area.
-				   This is actually the bottom of the stack,
-				   the top of the stack is always found in the
-				   esp register.  */
-  s32 signal;     		  /* Signal that caused the core dump. */
-  u32 u_ar0;                      /* Used by gdb to help find the values for */
-				  /* the registers. */
-  u32 magic;		          /* To uniquely identify a core file */
-  char u_comm[32];		  /* User command that was responsible */
-};
-
-
-#define PT32_PSWMASK  0x0
-#define PT32_PSWADDR  0x04
-#define PT32_GPR0     0x08
-#define PT32_GPR15    0x44
-#define PT32_ACR0     0x48
-#define PT32_ACR15    0x84
-#define PT32_ORIGGPR2 0x88
-#define PT32_FPC      0x90
-#define PT32_FPR0_HI  0x98
-#define PT32_FPR15_LO 0x114
-#define PT32_CR_9     0x118
-#define PT32_CR_11    0x120
-#define PT32_IEEE_IP  0x13C
-#define PT32_LASTOFF  PT32_IEEE_IP
-#define PT32_ENDREGS  0x140-1
-#define U32OFFSETOF(member) offsetof(struct user32,regs.member)
-#define U64OFFSETOF(member) offsetof(struct user,regs.member)
-#define U6432DIFF(member) (U64OFFSETOF(member) - U32OFFSETOF(member))
-#define PT_SINGLE_STEP   (PT_CR_11+8)
-#define PT32_SINGLE_STEP (PT32_CR_11+4)
-
-#endif /* CONFIG_S390_SUPPORT */
-
-int copy_user(struct task_struct *task,saddr_t useraddr, addr_t copyaddr,
-              int len, int tofromuser, int writingtouser)
-{
-	int copylen=0,copymax;
-	addr_t  realuseraddr;
-	saddr_t enduseraddr;
-	unsigned long mask;
-#ifdef CONFIG_S390_SUPPORT
-	int     parent_31bit=current->thread.flags & S390_FLAG_31BIT;
-	int     skip;
-#endif
-	enduseraddr=useraddr+len;
-	if ((useraddr<0||useraddr&3||enduseraddr&3)||
-#ifdef CONFIG_S390_SUPPORT
-	    (parent_31bit && enduseraddr > sizeof(struct user32)) ||
-#endif
-	    enduseraddr > sizeof(struct user))
-		return (-EIO);
+	if (addr <= (addr_t) &dummy->regs.orig_gpr2) {
+		/*
+		 * psw, gprs, acrs and orig_gpr2 are stored on the stack
+		 */
+		if (addr == (addr_t) &dummy->regs.psw.mask &&
+#ifdef CONFIG_S390_SUPPORT
+		    (data & ~PSW_MASK_CC) != PSW_USER32_BITS &&
+#endif
+		    (data & ~PSW_MASK_CC) != PSW_USER_BITS)
+			/* Invalid psw mask. */
+			return -EINVAL;
+		*(__u64 *)((addr_t) __KSTK_PTREGS(child) + addr) = data;
+
+	} else if (addr >= (addr_t) &dummy->regs.fp_regs &&
+		   addr < (addr_t) (&dummy->regs.fp_regs + 1)) {
+		/*
+		 * floating point regs. are stored in the thread structure
+		 */
+		if (addr == (addr_t) &dummy->regs.fp_regs.fpc &&
+		    ((data >> 32) & ~FPC_VALID_MASK) != 0)
+			/* Invalid floating pointer control. */
+			return -EINVAL;
+		offset = addr - (addr_t) &dummy->regs.fp_regs;
+		*(__u64 *)((addr_t) &child->thread.fp_regs + offset) = data;
+
+	} else if (addr >= (addr_t) &dummy->regs.per_info &&
+		   addr < (addr_t) (&dummy->regs.per_info + 1)) {
+		/*
+		 * per_info is found in the thread structure
+		 */
+		offset = addr - (addr_t) &dummy->regs.per_info;
+		*(__u64 *)((addr_t) &child->thread.per_info + offset) = data;
 
-#ifdef CONFIG_S390_SUPPORT
-	if(parent_31bit)
-	{
-		if(useraddr != PT32_PSWMASK)
-		{
-			if (useraddr == PT32_PSWADDR)
-				useraddr = PT_PSWADDR+4;
-			else if(useraddr <= PT32_GPR15)
-				useraddr = ((useraddr-PT32_GPR0)*2) + PT_GPR0+4;
-			else if(useraddr <= PT32_ACR15)
-				useraddr += PT_ACR0-PT32_ACR0;
-			else if(useraddr == PT32_ORIGGPR2)
-				useraddr = PT_ORIGGPR2+4;
-			else if(useraddr <= PT32_FPR15_LO)
-				useraddr += PT_FPR0-PT32_FPR0_HI;
-			else if(useraddr <= PT32_CR_11)
-				useraddr = ((useraddr-PT32_CR_9)*2) + PT_CR_9+4;
-			else if(useraddr ==  PT32_SINGLE_STEP)
-				useraddr = PT_SINGLE_STEP; 
-			else if(useraddr <= U32OFFSETOF(per_info.ending_addr))	
-				useraddr = (((useraddr-U32OFFSETOF(per_info.starting_addr)))*2) + 
-					U64OFFSETOF(per_info.starting_addr)+4;
-			else if( useraddr == U32OFFSETOF(per_info.lowcore.words.perc_atmid))
-				useraddr = U64OFFSETOF(per_info.lowcore.words.perc_atmid);
-			else if( useraddr == U32OFFSETOF(per_info.lowcore.words.address))
-				useraddr = U64OFFSETOF(per_info.lowcore.words.address)+4;
-			else if(useraddr == U32OFFSETOF(per_info.lowcore.words.access_id))
-				useraddr = U64OFFSETOF(per_info.lowcore.words.access_id);
-			else if(useraddr == PT32_IEEE_IP)
-				useraddr = PT_IEEE_IP+4;
-		}
 	}
-#endif /* CONFIG_S390_SUPPORT */
 
-	while(len>0)
-	{
-#ifdef CONFIG_S390_SUPPORT
-		skip=0;
-#endif
-		mask=PSW_ADDR_MASK;
-		if(useraddr<PT_FPC)
-		{
-			realuseraddr=((addr_t) __KSTK_PTREGS(task)) + useraddr;
-			if(useraddr<(PT_PSWMASK+8))
-			{
-				if(parent_31bit)
-				{
-					copymax=PT_PSWMASK+4;
-#ifdef CONFIG_S390_SUPPORT
-					skip=8;
-#endif
-				}
-				else
-				{
-					copymax=PT_PSWMASK+8;
-				}
-				if(writingtouser)
-					mask=PSW_MASK_DEBUGCHANGE;
-			}
-			else if(useraddr<(PT_PSWADDR+8))
-			{
-				copymax=PT_PSWADDR+8;
-				mask=PSW_ADDR_DEBUGCHANGE;
-#ifdef CONFIG_S390_SUPPORT
-				if(parent_31bit)
-					skip=4;
-#endif
+	FixPerRegisters(child);
+	return 0;
+}
 
-			}
-			else
-			{
-#ifdef CONFIG_S390_SUPPORT
-				if(parent_31bit && useraddr <= PT_GPR15+4)
-				{
-					copymax=useraddr+4;
-					if(useraddr<PT_GPR15+4)
-						skip=4;
-				}
-				else
-#endif
-					copymax=PT_FPC;
-			}
-		}
-		else if(useraddr<(PT_FPR15+sizeof(freg_t)))
-		{
-			copymax=(PT_FPR15+sizeof(freg_t));
-			realuseraddr=(addr_t)&(((u8 *)&task->thread.fp_regs)[useraddr-PT_FPC]);
-		}
-		else if(useraddr<sizeof(struct user_regs_struct))
-		{
-#ifdef CONFIG_S390_SUPPORT
-			if( parent_31bit && useraddr <= PT_IEEE_IP+4)
-			{
-				switch(useraddr)
-				{
-				case PT_CR_11+4:
-				case U64OFFSETOF(per_info.ending_addr)+4:
-				case U64OFFSETOF(per_info.lowcore.words.address)+4:
-					copymax=useraddr+4;
-					break;
-				case  PT_SINGLE_STEP:
-				case  U64OFFSETOF(per_info.lowcore.words.perc_atmid):
-					/* We copy 2 bytes in excess for the atmid member this also gets around */
-					/* alignment for this member in 32 bit */
-					skip=8;
-					copymax=useraddr+4;
-					break;
-				default: 
-					copymax=useraddr+4;
-					skip=4;
-				}
-			}
+static int
+do_ptrace_normal(struct task_struct *child, long request, long addr, long data)
+{
+	unsigned long tmp;
+	ptrace_area parea; 
+	int copied, ret;
+
+	switch (request) {
+	case PTRACE_PEEKTEXT:
+	case PTRACE_PEEKDATA:
+		/* read word at location addr. */
+		copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0);
+		if (copied != sizeof(tmp))
+			return -EIO;
+		return put_user(tmp, (unsigned long *) data);
+
+	case PTRACE_PEEKUSR:
+		/* read the word at location addr in the USER area. */
+		return peek_user(child, addr, data);
+
+	case PTRACE_POKETEXT:
+	case PTRACE_POKEDATA:
+		/* write the word at location addr. */
+		copied = access_process_vm(child, addr, &data, sizeof(data),1);
+		if (copied != sizeof(data))
+			return -EIO;
+		return 0;
+
+	case PTRACE_POKEUSR:
+		/* write the word at location addr in the USER area */
+		return poke_user(child, addr, data);
+
+	case PTRACE_PEEKUSR_AREA:
+	case PTRACE_POKEUSR_AREA:
+		if (!copy_from_user(&parea, (void *) addr, sizeof(parea)))
+			return -EFAULT;
+		addr = parea.kernel_addr;
+		data = parea.process_addr;
+		copied = 0;
+		while (copied < parea.len) {
+			if (request == PTRACE_PEEKUSR_AREA)
+				ret = peek_user(child, addr, data);
 			else
-#endif
-			{
-				copymax=sizeof(struct user_regs_struct);
-			}
-			realuseraddr=(addr_t)&(((u8 *)&task->thread.per_info)[useraddr-PT_CR_9]);
-		}
-		else 
-		{
-			copymax=sizeof(struct user);
-			realuseraddr=(addr_t)NULL;
+				ret = poke_user(child, addr, data);
+			if (ret)
+				return ret;
+			addr += sizeof(unsigned long);
+			data += sizeof(unsigned long);
+			copied += sizeof(unsigned long);
 		}
-		copylen=copymax-useraddr;
-		copylen=(copylen>len ? len:copylen);
-		if(ptrace_usercopy(realuseraddr,copyaddr,copylen,tofromuser,writingtouser,mask))
-			return (-EIO);
-		copyaddr+=copylen;
-		len-=copylen;
-		useraddr+=copylen
-#if CONFIG_S390_SUPPORT
-			+skip
-#endif
-			;
+		return 0;
 	}
-	FixPerRegisters(task);
-	return(0);
+	return -EIO;
 }
 
+#ifdef CONFIG_S390_SUPPORT
 /*
- * Called by kernel/ptrace.c when detaching..
- *
- * Make sure single step bits etc are not set.
+ * Now the fun part starts... a 31 bit program running in the
+ * 31 bit emulation tracing another program. PTRACE_PEEKTEXT,
+ * PTRACE_PEEKDATA, PTRACE_POKETEXT and PTRACE_POKEDATA are easy
+ * to handle, the difference to the 64 bit versions of the requests
+ * is that the access is done in multiples of 4 byte instead of
+ * 8 bytes (sizeof(unsigned long) on 31/64 bit).
+ * The ugly part are PTRACE_PEEKUSR, PTRACE_PEEKUSR_AREA,
+ * PTRACE_POKEUSR and PTRACE_POKEUSR_AREA. If the traced program
+ * is a 31 bit program too, the content of struct user can be
+ * emulated. A 31 bit program peeking into the struct user of
+ * a 64 bit program is a no-no.
  */
-void ptrace_disable(struct task_struct *child)
+
+/*
+ * Same as peek_user but for a 31 bit program.
+ */
+static int peek_user_emu31(struct task_struct *child, addr_t addr, addr_t data)
 {
-	/* make sure the single step bit is not set. */
-	clear_single_step(child);
+	struct user32 *dummy32 = NULL;
+	per_struct32 *dummy_per32 = NULL;
+	addr_t offset;
+	__u32 tmp;
+
+	if (!(child->thread.flags & S390_FLAG_31BIT) ||
+	    (addr & 3) || addr > sizeof(struct user) - 3)
+		return -EIO;
+
+	if (addr <= (addr_t) &dummy32->regs.orig_gpr2) {
+		/*
+		 * psw, gprs, acrs and orig_gpr2 are stored on the stack
+		 */
+		if (addr == (addr_t) &dummy32->regs.psw.mask) {
+			/* Fake a 31 bit psw mask. */
+			tmp = (__u32)(__KSTK_PTREGS(child)->psw.mask >> 32);
+			tmp = (tmp & PSW32_MASK_CC) | PSW32_USER_BITS;
+		} else if (addr == (addr_t) &dummy32->regs.psw.addr) {
+			/* Fake a 31 bit psw address. */
+			tmp = (__u32) __KSTK_PTREGS(child)->psw.addr |
+				PSW32_ADDR_AMODE31;
+		} else
+			tmp = *(__u32 *)((addr_t) __KSTK_PTREGS(child) + 
+					 addr*2 + 4);
+	} else if (addr >= (addr_t) &dummy32->regs.fp_regs &&
+		   addr < (addr_t) (&dummy32->regs.fp_regs + 1)) {
+		/*
+		 * floating point regs. are stored in the thread structure 
+		 */
+	        offset = addr - (addr_t) &dummy32->regs.fp_regs;
+		tmp = *(__u32 *)((addr_t) &child->thread.fp_regs + offset);
+
+	} else if (addr >= (addr_t) &dummy32->regs.per_info &&
+		   addr < (addr_t) (&dummy32->regs.per_info + 1)) {
+		/*
+		 * per_info is found in the thread structure
+		 */
+		offset = addr - (addr_t) &dummy32->regs.per_info;
+		/* This is magic. See per_struct and per_struct32. */
+		if ((offset >= (addr_t) &dummy_per32->control_regs &&
+		     offset < (addr_t) (&dummy_per32->control_regs + 1)) ||
+		    (offset >= (addr_t) &dummy_per32->starting_addr &&
+		     offset <= (addr_t) &dummy_per32->ending_addr) ||
+		    offset == (addr_t) &dummy_per32->lowcore.words.address)
+			offset = offset*2 + 4;
+		else
+			offset = offset*2;
+		tmp = *(__u32 *)((addr_t) &child->thread.per_info + offset);
+
+	} else
+		tmp = 0;
+
+	return put_user(tmp, (__u32 *) data);
 }
 
-typedef struct
+/*
+ * Same as poke_user but for a 31 bit program.
+ */
+static int poke_user_emu31(struct task_struct *child, addr_t addr, addr_t data)
 {
-__u32	len;
-__u32	kernel_addr;
-__u32	process_addr;
-} ptrace_area_emu31;
+	struct user32 *dummy32 = NULL;
+	per_struct32 *dummy_per32 = NULL;
+	addr_t offset;
+	__u32 tmp;
+	int ret;
+
+	if (!(child->thread.flags & S390_FLAG_31BIT) ||
+	    (addr & 3) || addr > sizeof(struct user32) - 3)
+		return -EIO;
+
+	tmp = (__u32) data;
+
+	if (addr <= (addr_t) &dummy32->regs.orig_gpr2) {
+		/*
+		 * psw, gprs, acrs and orig_gpr2 are stored on the stack
+		 */
+		if (addr == (addr_t) &dummy32->regs.psw.mask) {
+			/* Build a 64 bit psw mask from 31 bit mask. */
+			if ((tmp & ~PSW32_MASK_CC) != PSW32_USER_BITS)
+				/* Invalid psw mask. */
+				return -EINVAL;
+			__KSTK_PTREGS(child)->psw.mask = PSW_USER_BITS |
+				((tmp & PSW32_MASK_CC) << 32);
+		} else if (addr == (addr_t) &dummy32->regs.psw.addr) {
+			/* Build a 64 bit psw address from 31 bit address. */
+			__KSTK_PTREGS(child)->psw.addr = 
+				(__u64) tmp & PSW32_ADDR_INSN;
+		} else
+			*(__u32*)((addr_t) __KSTK_PTREGS(child) + addr*2 + 4) =
+				tmp;
+	} else if (addr >= (addr_t) &dummy32->regs.fp_regs &&
+		   addr < (addr_t) (&dummy32->regs.fp_regs + 1)) {
+		/*
+		 * floating point regs. are stored in the thread structure 
+		 */
+		if (addr == (addr_t) &dummy32->regs.fp_regs.fpc &&
+		    (tmp & ~FPC_VALID_MASK) != 0)
+			/* Invalid floating pointer control. */
+			return -EINVAL;
+	        offset = addr - (addr_t) &dummy32->regs.fp_regs;
+		*(__u32 *)((addr_t) &child->thread.fp_regs + offset) = tmp;
+
+	} else if (addr >= (addr_t) &dummy32->regs.per_info &&
+		   addr < (addr_t) (&dummy32->regs.per_info + 1)) {
+		/*
+		 * per_info is found in the thread structure.
+		 */
+		offset = addr - (addr_t) &dummy32->regs.per_info;
+		/*
+		 * This is magic. See per_struct and per_struct32.
+		 * By incident the offsets in per_struct are exactly
+		 * twice the offsets in per_struct32 for all fields.
+		 * The 8 byte fields need special handling though,
+		 * because the second half (bytes 4-7) is needed and
+		 * not the first half.
+		 */
+		if ((offset >= (addr_t) &dummy_per32->control_regs &&
+		     offset < (addr_t) (&dummy_per32->control_regs + 1)) ||
+		    (offset >= (addr_t) &dummy_per32->starting_addr &&
+		     offset <= (addr_t) &dummy_per32->ending_addr) ||
+		    offset == (addr_t) &dummy_per32->lowcore.words.address)
+			offset = offset*2 + 4;
+		else
+			offset = offset*2;
+		*(__u32 *)((addr_t) &child->thread.per_info + offset) = tmp;
 
+	}
 
-asmlinkage int sys_ptrace(long request, long pid, long addr, long data)
+	FixPerRegisters(child);
+	return 0;
+}
+
+static int
+do_ptrace_emu31(struct task_struct *child, long request, long addr, long data)
 {
-	struct task_struct *child;
-	int ret = -EPERM;
-	int copied;
-#ifdef CONFIG_S390_SUPPORT
-	int           parent_31bit;
-	int           sizeof_parent_long;
-	u8            *dataptr;
-#else
-#define sizeof_parent_long 8
-#define dataptr (u8 *)&data
-#endif
-	lock_kernel();
-	if (request == PTRACE_TRACEME) 
-	{
-		/* are we already being traced? */
-		if (current->ptrace & PT_PTRACED)
-			goto out;
-		/* set the ptrace bit in the process flags. */
-		current->ptrace |= PT_PTRACED;
-		ret = 0;
-		goto out;
-	}
-	ret = -ESRCH;
-	read_lock(&tasklist_lock);
-	child = find_task_by_pid(pid);
-	if (child)
-		get_task_struct(child);
-	read_unlock(&tasklist_lock);
-	if (!child)
-		goto out;
-	ret = -EPERM;
-	if (pid == 1)		/* you may not mess with init */
-		goto out_tsk;
-	if (request == PTRACE_ATTACH) 
-	{
-		ret = ptrace_attach(child);
-		goto out_tsk;
-	}
-	ret = -ESRCH;
-	// printk("child=%lX child->flags=%lX",child,child->flags);
-	/* I added child!=current line so we can get the */
-	/* ieee_instruction_pointer from the user structure DJB */
-	if(child!=current)
-	{
-		if (!(child->ptrace & PT_PTRACED))
-			goto out_tsk;
-		if (child->state != TASK_STOPPED) 
-		{
-			if (request != PTRACE_KILL)
-				goto out_tsk;
+	unsigned int tmp;  /* 4 bytes !! */
+	ptrace_area_emu31 parea; 
+	int copied, ret;
+
+	switch (request) {
+	case PTRACE_PEEKTEXT:
+	case PTRACE_PEEKDATA:
+		/* read word at location addr. */
+		copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0);
+		if (copied != sizeof(tmp))
+			return -EIO;
+		return put_user(tmp, (unsigned int *) data);
+
+	case PTRACE_PEEKUSR:
+		/* read the word at location addr in the USER area. */
+		return peek_user_emu31(child, addr, data);
+
+	case PTRACE_POKETEXT:
+	case PTRACE_POKEDATA:
+		/* write the word at location addr. */
+		tmp = data;
+		copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 1);
+		if (copied != sizeof(tmp))
+			return -EIO;
+		return 0;
+
+	case PTRACE_POKEUSR:
+		/* write the word at location addr in the USER area */
+		return poke_user_emu31(child, addr, data);
+
+	case PTRACE_PEEKUSR_AREA:
+	case PTRACE_POKEUSR_AREA:
+		if (!copy_from_user(&parea, (void *) addr, sizeof(parea)))
+			return -EFAULT;
+		addr = parea.kernel_addr;
+		data = parea.process_addr;
+		copied = 0;
+		while (copied < parea.len) {
+			if (request == PTRACE_PEEKUSR_AREA)
+				ret = peek_user_emu31(child, addr, data);
+			else
+				ret = poke_user_emu31(child, addr, data);
+			if (ret)
+				return ret;
+			addr += sizeof(unsigned int);
+			data += sizeof(unsigned int);
+			copied += sizeof(unsigned int);
 		}
-		if (child->parent != current)
-			goto out_tsk;
+		return 0;
 	}
-#ifdef CONFIG_S390_SUPPORT
-	parent_31bit=(current->thread.flags & S390_FLAG_31BIT);
-	sizeof_parent_long=(parent_31bit ? 4:8);
-	dataptr=&(((u8 *)&data)[parent_31bit ? 4:0]);
+	return -EIO;
+}
 #endif
-	switch (request) 
-	{
-		/* If I and D space are separate, these will need to be fixed. */
-	case PTRACE_PEEKTEXT: /* read word at location addr. */ 
-	case PTRACE_PEEKDATA: 
-	{
-		u8 tmp[8];
-		copied = access_process_vm(child, addr, tmp, sizeof_parent_long, 0);
-		ret = -EIO;
-		if (copied != sizeof_parent_long)
-			break;
-		ret = copy_to_user((void *)data,tmp,sizeof_parent_long);
-		break;
-	
+
+static int
+do_ptrace(struct task_struct *child, long request, long addr, long data)
+{
+	int ret;
+
+	if (request == PTRACE_ATTACH)
+		return ptrace_attach(child);
+
+	/*
+	 * I added child != current line so we can get the
+	 * ieee_instruction_pointer from the user structure DJB
+	 */
+	if (child != current) {
+		ret = ptrace_check_attach(child, request == PTRACE_KILL);
+		if (ret < 0)
+			return ret;
 	}
-		/* read the word at location addr in the USER area. */
-	case PTRACE_PEEKUSR:
-		ret=copy_user(child,addr,data,sizeof_parent_long,1,0);
-		break;
 
-		/* If I and D space are separate, this will have to be fixed. */
-	case PTRACE_POKETEXT: /* write the word at location addr. */
-	case PTRACE_POKEDATA:
-		ret = 0;
-		if (access_process_vm(child, addr,dataptr, sizeof_parent_long, 1) == sizeof_parent_long)
-			break;
-		ret = -EIO;
-		break;
-
-	case PTRACE_POKEUSR: /* write the word at location addr in the USER area */
-		ret=copy_user(child,addr,(addr_t)dataptr,sizeof_parent_long,0,1);
-		break;
-
-	case PTRACE_SYSCALL: 	/* continue and stop at next (return from) syscall */
-	case PTRACE_CONT: 	 /* restart after signal. */
-		ret = -EIO;
+	switch (request) {
+	/* First the common request for 31/64 bit */
+	case PTRACE_SYSCALL:
+		/* continue and stop at next (return from) syscall */
+	case PTRACE_CONT:
+		/* restart after signal. */
 		if ((unsigned long) data >= _NSIG)
-			break;
+			return -EIO;
 		if (request == PTRACE_SYSCALL)
 			set_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
 		else
@@ -525,86 +507,104 @@
 		/* make sure the single step bit is not set. */
 		clear_single_step(child);
 		wake_up_process(child);
-		ret = 0;
-		break;
+		return 0;
 
-/*
- * make the child exit.  Best I can do is send it a sigkill. 
- * perhaps it should be put in the status that it wants to 
- * exit.
- */
 	case PTRACE_KILL:
-		ret = 0;
+		/*
+		 * make the child exit.  Best I can do is send it a sigkill. 
+		 * perhaps it should be put in the status that it wants to 
+		 * exit.
+		 */
 		if (child->state == TASK_ZOMBIE) /* already dead */
-			break;
+			return 0;
 		child->exit_code = SIGKILL;
+		/* make sure the single step bit is not set. */
 		clear_single_step(child);
 		wake_up_process(child);
-		/* make sure the single step bit is not set. */
-		break;
+		return 0;
 
-	case PTRACE_SINGLESTEP:  /* set the trap flag. */
-		ret = -EIO;
+	case PTRACE_SINGLESTEP:
+		/* set the trap flag. */
 		if ((unsigned long) data >= _NSIG)
-			break;
+			return -EIO;
 		clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
 		child->exit_code = data;
 		set_single_step(child);
 		/* give it a chance to run. */
 		wake_up_process(child);
-		ret = 0;
-		break;
+		return 0;
 
-	case PTRACE_DETACH:  /* detach a process that was attached. */
-		ret = ptrace_detach(child, data);
-		break;
+	case PTRACE_DETACH:
+		/* detach a process that was attached. */
+		return ptrace_detach(child, data);
 
-	case PTRACE_PEEKUSR_AREA:
-	case PTRACE_POKEUSR_AREA:
-		if(parent_31bit)
-		{
-			ptrace_area_emu31   parea; 
-			if((ret=copy_from_user(&parea,(void *)addr,sizeof(parea)))==0)  
-				ret=copy_user(child,parea.kernel_addr,parea.process_addr,
-					      parea.len,1,(request==PTRACE_POKEUSR_AREA));
-		}
-		else
-		{
-			ptrace_area   parea; 
-			if((ret=copy_from_user(&parea,(void *)addr,sizeof(parea)))==0)  
-				ret=copy_user(child,parea.kernel_addr,parea.process_addr,
-					      parea.len,1,(request==PTRACE_POKEUSR_AREA));
-		}
-		break;
-	case PTRACE_SETOPTIONS: {
+	case PTRACE_SETOPTIONS:
 		if (data & PTRACE_O_TRACESYSGOOD)
 			child->ptrace |= PT_TRACESYSGOOD;
 		else
 			child->ptrace &= ~PT_TRACESYSGOOD;
-		ret = 0;
-		break;
-	}
+		return 0;
+	/* Do requests that differ for 31/64 bit */
 	default:
-		ret = -EIO;
-		break;
+#ifdef CONFIG_S390_SUPPORT
+		if (current->thread.flags & S390_FLAG_31BIT)
+			return do_ptrace_emu31(child, request, addr, data);
+#endif
+		return do_ptrace_normal(child, request, addr, data);
+		
 	}
- out_tsk:
+	return -EIO;
+}
+
+asmlinkage int sys_ptrace(long request, long pid, long addr, long data)
+{
+	struct task_struct *child;
+	int ret;
+
+	lock_kernel();
+
+	if (request == PTRACE_TRACEME) {
+		/* are we already being traced? */
+		ret = -EPERM;
+		if (current->ptrace & PT_PTRACED)
+			goto out;
+		ret = security_ops->ptrace(current->parent, current);
+		if (ret)
+			goto out;
+		/* set the ptrace bit in the process flags. */
+		current->ptrace |= PT_PTRACED;
+		goto out;
+	}
+
+	ret = -EPERM;
+	if (pid == 1)		/* you may not mess with init */
+		goto out;
+
+	ret = -ESRCH;
+	read_lock(&tasklist_lock);
+	child = find_task_by_pid(pid);
+	if (child)
+		get_task_struct(child);
+	read_unlock(&tasklist_lock);
+	if (!child)
+		goto out;
+
+	ret = do_ptrace(child, request, addr, data);
+
 	put_task_struct(child);
- out:
+out:
 	unlock_kernel();
 	return ret;
 }
 
-
-
 asmlinkage void syscall_trace(void)
 {
 	if (!test_thread_flag(TIF_SYSCALL_TRACE))
 		return;
 	if (!(current->ptrace & PT_PTRACED))
 		return;
-	current->exit_code = SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD)
-					? 0x80 : 0);
+	current->exit_code =
+		SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) ? 0x80 : 0);
 	current->state = TASK_STOPPED;
 	notify_parent(current, SIGCHLD);
 	schedule();
diff -Nru a/arch/s390x/kernel/ptrace32.h b/arch/s390x/kernel/ptrace32.h
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/arch/s390x/kernel/ptrace32.h	Mon Oct  7 11:26:06 2002
@@ -0,0 +1,86 @@
+#ifndef _PTRACE32_H
+#define _PTRACE32_H
+
+#include "linux32.h"  /* needed for _psw_t32 */
+
+typedef struct
+{
+	__u32 cr[3];
+} per_cr_words32  __attribute__((packed));
+
+typedef struct
+{
+	__u16          perc_atmid;          /* 0x096 */
+	__u32          address;             /* 0x098 */
+	__u8           access_id;           /* 0x0a1 */
+} per_lowcore_words32  __attribute__((packed));
+
+typedef struct
+{
+	union {
+		per_cr_words32   words;
+	} control_regs  __attribute__((packed));
+	/*
+	 * Use these flags instead of setting em_instruction_fetch
+	 * directly they are used so that single stepping can be
+	 * switched on & off while not affecting other tracing
+	 */
+	unsigned  single_step       : 1;
+	unsigned  instruction_fetch : 1;
+	unsigned                    : 30;
+	/*
+	 * These addresses are copied into cr10 & cr11 if single
+	 * stepping is switched off
+	 */
+	__u32     starting_addr;
+	__u32     ending_addr;
+	union {
+		per_lowcore_words32 words;
+	} lowcore; 
+} per_struct32 __attribute__((packed));
+
+struct user_regs_struct32
+{
+	_psw_t32 psw;
+	u32 gprs[NUM_GPRS];
+	u32 acrs[NUM_ACRS];
+	u32 orig_gpr2;
+	s390_fp_regs fp_regs;
+	/*
+	 * These per registers are in here so that gdb can modify them
+	 * itself as there is no "official" ptrace interface for hardware
+	 * watchpoints. This is the way intel does it.
+	 */
+	per_struct32 per_info;
+	u32  ieee_instruction_pointer; 
+	/* Used to give failing instruction back to user for ieee exceptions */
+};
+
+struct user32 {
+	/* We start with the registers, to mimic the way that "memory"
+	   is returned from the ptrace(3,...) function.  */
+	struct user_regs_struct32 regs; /* Where the registers are actually stored */
+	/* The rest of this junk is to help gdb figure out what goes where */
+	u32 u_tsize;		/* Text segment size (pages). */
+	u32 u_dsize;	        /* Data segment size (pages). */
+	u32 u_ssize;	        /* Stack segment size (pages). */
+	u32 start_code;         /* Starting virtual address of text. */
+	u32 start_stack;	/* Starting virtual address of stack area.
+				   This is actually the bottom of the stack,
+				   the top of the stack is always found in the
+				   esp register.  */
+	s32 signal;     	 /* Signal that caused the core dump. */
+	u32 u_ar0;               /* Used by gdb to help find the values for */
+	                         /* the registers. */
+	u32 magic;		 /* To uniquely identify a core file */
+	char u_comm[32];	 /* User command that was responsible */
+};
+
+typedef struct
+{
+	__u32   len;
+	__u32   kernel_addr;
+	__u32   process_addr;
+} ptrace_area_emu31;
+
+#endif /* _PTRACE32_H */
diff -Nru a/arch/s390x/kernel/s390fpu.c b/arch/s390x/kernel/s390fpu.c
--- a/arch/s390x/kernel/s390fpu.c	Mon Oct  7 11:25:39 2002
+++ /dev/null	Wed Dec 31 16:00:00 1969
@@ -1,87 +0,0 @@
-/*
- *  arch/s390/kernel/s390fpu.c
- *
- *  S390 version
- *    Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation
- *    Author(s): Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com)
- *
- *  s390fpu.h functions for saving & restoring the fpu state.
- *
- *  I couldn't inline these as linux/sched.h included half the world
- *  & was required to at the task structure.
- *  & the functions were too complex to make macros from.
- *  ( & as usual I didn't feel like debugging inline code ).
- */
-
-#include <linux/sched.h>
-
-void save_fp_regs(s390_fp_regs *fpregs)
-{
-/*
- * I don't think we can use STE here as this would load
- * fp registers 0 & 2 into memory locations 0 & 1 etc. 
- */	
-	asm volatile ("STFPC 0(%0)\n\t"
-		      "STD   0,8(%0)\n\t"
-		      "STD   1,16(%0)\n\t"
-		      "STD   2,24(%0)\n\t"
-		      "STD   3,32(%0)\n\t"
-		      "STD   4,40(%0)\n\t"
-		      "STD   5,48(%0)\n\t"
-		      "STD   6,56(%0)\n\t"
-		      "STD   7,64(%0)\n\t"
-		      "STD   8,72(%0)\n\t"
-		      "STD   9,80(%0)\n\t"
-		      "STD   10,88(%0)\n\t"
-		      "STD   11,96(%0)\n\t"
-		      "STD   12,104(%0)\n\t"
-		      "STD   13,112(%0)\n\t"
-		      "STD   14,120(%0)\n\t"
-		      "STD   15,128(%0)\n\t"
-                      : 
-		      : "a" (fpregs)
-		      : "memory"
-		);
-}
-
-void restore_fp_regs(s390_fp_regs *fpregs)
-{
-	/* If we don't mask with the FPC_VALID_MASK here
-	 * we've got a very quick shutdown -h now command
-         * via a kernel specification exception.
-	 */
-	fpregs->fpc&=FPC_VALID_MASK;
-	asm volatile ("LFPC 0(%0)\n\t"
-		      "LD   0,8(%0)\n\t"
-		      "LD   1,16(%0)\n\t"
-		      "LD   2,24(%0)\n\t"
-		      "LD   3,32(%0)\n\t"
-		      "LD   4,40(%0)\n\t"
-		      "LD   5,48(%0)\n\t"
-		      "LD   6,56(%0)\n\t"
-		      "LD   7,64(%0)\n\t"
-		      "LD   8,72(%0)\n\t"
-		      "LD   9,80(%0)\n\t"
-		      "LD   10,88(%0)\n\t"
-		      "LD   11,96(%0)\n\t"
-		      "LD   12,104(%0)\n\t"
-		      "LD   13,112(%0)\n\t"
-		      "LD   14,120(%0)\n\t"
-		      "LD   15,128(%0)\n\t"
-                      : 
-		      : "a" (fpregs)
-		      : "memory"
-		);
-}
-
-
-
-
-
-
-
-
-
-
-
-
diff -Nru a/arch/s390x/kernel/setup.c b/arch/s390x/kernel/setup.c
--- a/arch/s390x/kernel/setup.c	Mon Oct  7 11:25:32 2002
+++ b/arch/s390x/kernel/setup.c	Mon Oct  7 11:25:32 2002
@@ -52,7 +52,6 @@
 struct { unsigned long addr, size, type; } memory_chunk[16] = { { 0 } };
 #define CHUNK_READ_WRITE 0
 #define CHUNK_READ_ONLY 1
-__u16 boot_cpu_addr;
 int cpus_initialized = 0;
 unsigned long cpu_initialized = 0;
 volatile int __cpu_logical_map[NR_CPUS]; /* logical cpu to cpu address */
@@ -79,7 +78,7 @@
 /*
  * cpu_init() initializes state that is per-CPU.
  */
-void __init cpu_init (void)
+void __devinit cpu_init (void)
 {
         int nr = smp_processor_id();
         int addr = hard_smp_processor_id();
@@ -305,7 +304,7 @@
 	unsigned long start_pfn, end_pfn;
         static unsigned int smptrap=0;
         unsigned long delay = 0;
-	struct _lowcore *lowcore;
+	struct _lowcore *lc;
 	int i;
 
         if (smptrap)
@@ -442,30 +441,32 @@
         /*
          * Setup lowcore for boot cpu
          */
-	lowcore = (struct _lowcore *) 
-		__alloc_bootmem(2*PAGE_SIZE, 2*PAGE_SIZE, 0);
-	memset(lowcore, 0, 2*PAGE_SIZE);
-	lowcore->restart_psw.mask = _RESTART_PSW_MASK;
-	lowcore->restart_psw.addr = (addr_t) &restart_int_handler;
-	lowcore->external_new_psw.mask = _EXT_PSW_MASK;
-	lowcore->external_new_psw.addr = (addr_t) &ext_int_handler;
-	lowcore->svc_new_psw.mask = _SVC_PSW_MASK;
-	lowcore->svc_new_psw.addr = (addr_t) &system_call;
-	lowcore->program_new_psw.mask = _PGM_PSW_MASK;
-	lowcore->program_new_psw.addr = (addr_t) &pgm_check_handler;
-	lowcore->mcck_new_psw.mask = _MCCK_PSW_MASK;
-	lowcore->mcck_new_psw.addr = (addr_t) &mcck_int_handler;
-	lowcore->io_new_psw.mask = _IO_PSW_MASK;
-	lowcore->io_new_psw.addr = (addr_t) &io_int_handler;
-	lowcore->ipl_device = S390_lowcore.ipl_device;
-	lowcore->kernel_stack = ((__u64) &init_thread_union) + 16384;
-	lowcore->async_stack = (__u64)
+	lc = (struct _lowcore *) __alloc_bootmem(2*PAGE_SIZE, 2*PAGE_SIZE, 0);
+	memset(lc, 0, 2*PAGE_SIZE);
+	lc->restart_psw.mask = PSW_BASE_BITS;
+	lc->restart_psw.addr = (addr_t) &restart_int_handler;
+	lc->external_new_psw.mask = PSW_KERNEL_BITS;
+	lc->external_new_psw.addr = (addr_t) &ext_int_handler;
+	lc->svc_new_psw.mask = PSW_KERNEL_BITS;
+	lc->svc_new_psw.addr = (addr_t) &system_call;
+	lc->program_new_psw.mask = PSW_KERNEL_BITS;
+	lc->program_new_psw.addr = (addr_t) &pgm_check_handler;
+	lc->mcck_new_psw.mask = PSW_KERNEL_BITS;
+	lc->mcck_new_psw.addr = (addr_t) &mcck_int_handler;
+	lc->io_new_psw.mask = PSW_KERNEL_BITS;
+	lc->io_new_psw.addr = (addr_t) &io_int_handler;
+	lc->ipl_device = S390_lowcore.ipl_device;
+	lc->kernel_stack = ((__u64) &init_thread_union) + 16384;
+	lc->async_stack = (__u64)
 		__alloc_bootmem(4*PAGE_SIZE, 4*PAGE_SIZE, 0) + 16384;
-	lowcore->jiffy_timer = -1LL;
-	set_prefix((__u32)(__u64) lowcore);
+	lc->jiffy_timer = -1LL;
+	if (MACHINE_HAS_DIAG44)
+		lc->diag44_opcode = 0x83000044;
+	else
+		lc->diag44_opcode = 0x07000700;
+	set_prefix((__u32)(__u64) lc);
         cpu_init();
-        boot_cpu_addr = S390_lowcore.cpu_data.cpu_addr;
-        __cpu_logical_map[0] = boot_cpu_addr;
+        __cpu_logical_map[0] = S390_lowcore.cpu_data.cpu_addr;
 
 	/*
 	 * Create kernel page tables and switch to virtual addressing.
@@ -514,11 +515,14 @@
 		seq_printf(m, "vendor_id       : IBM/S390\n"
 				"# processors    : %i\n"
 				"bogomips per cpu: %lu.%02lu\n",
-				smp_num_cpus, loops_per_jiffy/(500000/HZ),
+				num_online_cpus(), loops_per_jiffy/(500000/HZ),
 				(loops_per_jiffy/(5000/HZ))%100);
 	}
 	if (cpu_online_map & (1 << n)) {
-		cpuinfo = &safe_get_cpu_lowcore(n)->cpu_data;
+		if (smp_processor_id() == n)
+			cpuinfo = &S390_lowcore.cpu_data;
+		else
+			cpuinfo = &lowcore_ptr[n]->cpu_data;
 		seq_printf(m, "processor %li: "
 				"version = %02X,  "
 				"identification = %06X,  "
diff -Nru a/arch/s390x/kernel/signal.c b/arch/s390x/kernel/signal.c
--- a/arch/s390x/kernel/signal.c	Mon Oct  7 11:25:36 2002
+++ b/arch/s390x/kernel/signal.c	Mon Oct  7 11:25:36 2002
@@ -60,11 +60,11 @@
 	sigset_t saveset;
 
 	mask &= _BLOCKABLE;
-	spin_lock_irq(&current->sigmask_lock);
+	spin_lock_irq(&current->sig->siglock);
 	saveset = current->blocked;
 	siginitset(&current->blocked, mask);
 	recalc_sigpending();
-	spin_unlock_irq(&current->sigmask_lock);
+	spin_unlock_irq(&current->sig->siglock);
 	regs->gprs[2] = -EINTR;
 
 	while (1) {
@@ -88,11 +88,11 @@
 		return -EFAULT;
 	sigdelsetmask(&newset, ~_BLOCKABLE);
 
-	spin_lock_irq(&current->sigmask_lock);
+	spin_lock_irq(&current->sig->siglock);
 	saveset = current->blocked;
 	current->blocked = newset;
 	recalc_sigpending();
-	spin_unlock_irq(&current->sigmask_lock);
+	spin_unlock_irq(&current->sig->siglock);
 	regs->gprs[2] = -EINTR;
 
 	while (1) {
@@ -144,40 +144,37 @@
 
 
 /* Returns non-zero on fault */
-static int save_sigregs(struct pt_regs *regs,_sigregs *sregs)
+static int save_sigregs(struct pt_regs *regs, _sigregs *sregs)
 {
 	int err;
-	s390_fp_regs fpregs;
   
-	err = __copy_to_user(&sregs->regs,regs,sizeof(_s390_regs_common));
-	if(!err)
-	{
-		save_fp_regs(&fpregs);
-		err=__copy_to_user(&sregs->fpregs,&fpregs,sizeof(fpregs));
-	}
-	return(err);
-	
+	err = __copy_to_user(&sregs->regs, regs, sizeof(_s390_regs_common));
+	if (err != 0)
+		return err;
+	save_fp_regs(&current->thread.fp_regs);
+	return __copy_to_user(&sregs->fpregs, &current->thread.fp_regs,
+			      sizeof(s390_fp_regs));
 }
 
 /* Returns positive number on error */
-static int restore_sigregs(struct pt_regs *regs,_sigregs *sregs)
+static int restore_sigregs(struct pt_regs *regs, _sigregs *sregs)
 {
 	int err;
-	s390_fp_regs fpregs;
-	psw_t saved_psw=regs->psw;
-	err=__copy_from_user(regs,&sregs->regs,sizeof(_s390_regs_common));
-	if(!err)
-	{
-		regs->trap = -1;		/* disable syscall checks */
-		regs->psw.mask=(saved_psw.mask&~PSW_MASK_DEBUGCHANGE)|
-		(regs->psw.mask&PSW_MASK_DEBUGCHANGE);
-		regs->psw.addr=(saved_psw.addr&~PSW_ADDR_DEBUGCHANGE)|
-		(regs->psw.addr&PSW_ADDR_DEBUGCHANGE);
-		err=__copy_from_user(&fpregs,&sregs->fpregs,sizeof(fpregs));
-		if(!err)
-			restore_fp_regs(&fpregs);
-	}
-	return(err);
+
+	err = __copy_from_user(regs, &sregs->regs, sizeof(_s390_regs_common));
+	regs->psw.mask = PSW_USER_BITS | (regs->psw.mask & PSW_MASK_CC);
+	if (err)
+		return err;
+
+	err = __copy_from_user(&current->thread.fp_regs, &sregs->fpregs,
+			       sizeof(s390_fp_regs));
+	current->thread.fp_regs.fpc &= FPC_VALID_MASK;
+	if (err)
+		return err;
+
+	restore_fp_regs(&current->thread.fp_regs);
+	regs->trap = -1;	/* disable syscall checks */
+	return 0;
 }
 
 asmlinkage long sys_sigreturn(struct pt_regs *regs)
@@ -191,10 +188,10 @@
 		goto badframe;
 
 	sigdelsetmask(&set, ~_BLOCKABLE);
-	spin_lock_irq(&current->sigmask_lock);
+	spin_lock_irq(&current->sig->siglock);
 	current->blocked = set;
 	recalc_sigpending();
-	spin_unlock_irq(&current->sigmask_lock);
+	spin_unlock_irq(&current->sig->siglock);
 
 	if (restore_sigregs(regs, &frame->sregs))
 		goto badframe;
@@ -217,10 +214,10 @@
 		goto badframe;
 
 	sigdelsetmask(&set, ~_BLOCKABLE);
-	spin_lock_irq(&current->sigmask_lock);
+	spin_lock_irq(&current->sig->siglock);
 	current->blocked = set;
 	recalc_sigpending();
-	spin_unlock_irq(&current->sigmask_lock);
+	spin_unlock_irq(&current->sig->siglock);
 
 	if (restore_sigregs(regs, &frame->uc.uc_mcontext))
 		goto badframe;
@@ -295,9 +292,9 @@
 	/* Set up to return from userspace.  If provided, use a stub
 	   already in userspace.  */
 	if (ka->sa.sa_flags & SA_RESTORER) {
-                regs->gprs[14] = FIX_PSW(ka->sa.sa_restorer);
+                regs->gprs[14] = (__u64) ka->sa.sa_restorer;
 	} else {
-                regs->gprs[14] = FIX_PSW(frame->retcode);
+                regs->gprs[14] = (__u64) frame->retcode;
 		if (__put_user(S390_SYSCALL_OPCODE | __NR_sigreturn, 
 	                       (u16 *)(frame->retcode)))
 			goto give_sigsegv;
@@ -308,12 +305,12 @@
 		goto give_sigsegv;
 
 	/* Set up registers for signal handler */
-	regs->gprs[15] = (addr_t)frame;
-	regs->psw.addr = FIX_PSW(ka->sa.sa_handler);
-	regs->psw.mask = _USER_PSW_MASK;
+	regs->gprs[15] = (__u64) frame;
+	regs->psw.addr = (__u64) ka->sa.sa_handler;
+	regs->psw.mask = PSW_USER_BITS;
 
 	regs->gprs[2] = map_signal(sig);
-	regs->gprs[3] = (addr_t)&frame->sc;
+	regs->gprs[3] = (__u64) &frame->sc;
 
 	/* We forgot to include these in the sigcontext.
 	   To avoid breaking binary compatibility, they are passed as args. */
@@ -353,9 +350,9 @@
 	/* Set up to return from userspace.  If provided, use a stub
 	   already in userspace.  */
 	if (ka->sa.sa_flags & SA_RESTORER) {
-                regs->gprs[14] = FIX_PSW(ka->sa.sa_restorer);
+                regs->gprs[14] = (__u64) ka->sa.sa_restorer;
 	} else {
-                regs->gprs[14] = FIX_PSW(frame->retcode);
+                regs->gprs[14] = (__u64) frame->retcode;
 		err |= __put_user(S390_SYSCALL_OPCODE | __NR_rt_sigreturn, 
 	                          (u16 *)(frame->retcode));
 	}
@@ -365,13 +362,13 @@
 		goto give_sigsegv;
 
 	/* Set up registers for signal handler */
-	regs->gprs[15] = (addr_t)frame;
-	regs->psw.addr = FIX_PSW(ka->sa.sa_handler);
-	regs->psw.mask = _USER_PSW_MASK;
+	regs->gprs[15] = (__u64) frame;
+	regs->psw.addr = (__u64) ka->sa.sa_handler;
+	regs->psw.mask = PSW_USER_BITS;
 
 	regs->gprs[2] = map_signal(sig);
-	regs->gprs[3] = (addr_t)&frame->info;
-	regs->gprs[4] = (addr_t)&frame->uc;
+	regs->gprs[3] = (__u64) &frame->info;
+	regs->gprs[4] = (__u64) &frame->uc;
 	return;
 
 give_sigsegv:
@@ -420,11 +417,11 @@
 		ka->sa.sa_handler = SIG_DFL;
 
 	if (!(ka->sa.sa_flags & SA_NODEFER)) {
-		spin_lock_irq(&current->sigmask_lock);
+		spin_lock_irq(&current->sig->siglock);
 		sigorsets(&current->blocked,&current->blocked,&ka->sa.sa_mask);
 		sigaddset(&current->blocked,sig);
 		recalc_sigpending();
-		spin_unlock_irq(&current->sigmask_lock);
+		spin_unlock_irq(&current->sig->siglock);
 	}
 }
 
diff -Nru a/arch/s390x/kernel/signal32.c b/arch/s390x/kernel/signal32.c
--- a/arch/s390x/kernel/signal32.c	Mon Oct  7 11:25:35 2002
+++ b/arch/s390x/kernel/signal32.c	Mon Oct  7 11:25:35 2002
@@ -29,11 +29,10 @@
 #include <asm/ucontext.h>
 #include <asm/uaccess.h>
 #include "linux32.h"
+#include "ptrace32.h"
 
 #define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
 
-#define _USER_PSW_MASK32 0x0705C00080000000
-
 typedef struct 
 {
 	__u8 callee_used_stack[__SIGNAL_FRAMESIZE32];
@@ -112,11 +111,11 @@
 	sigset_t saveset;
 
 	mask &= _BLOCKABLE;
-	spin_lock_irq(&current->sigmask_lock);
+	spin_lock_irq(&current->sig->siglock);
 	saveset = current->blocked;
 	siginitset(&current->blocked, mask);
 	recalc_sigpending();
-	spin_unlock_irq(&current->sigmask_lock);
+	spin_unlock_irq(&current->sig->siglock);
 	regs->gprs[2] = -EINTR;
 
 	while (1) {
@@ -147,11 +146,11 @@
 	}
         sigdelsetmask(&newset, ~_BLOCKABLE);
 
-        spin_lock_irq(&current->sigmask_lock);
+        spin_lock_irq(&current->sig->siglock);
         saveset = current->blocked;
         current->blocked = newset;
         recalc_sigpending();
-        spin_unlock_irq(&current->sigmask_lock);
+        spin_unlock_irq(&current->sig->siglock);
         regs->gprs[2] = -EINTR;
 
         while (1) {
@@ -290,55 +289,48 @@
 
 static int save_sigregs32(struct pt_regs *regs,_sigregs32 *sregs)
 {
-	int err = 0;
-	s390_fp_regs fpregs;
-	int i;
+	_s390_regs_common32 regs32;
+	int err, i;
 
-	for(i=0; i<NUM_GPRS; i++) 
-		err |= __put_user(regs->gprs[i], &sregs->regs.gprs[i]);  
-	for(i=0; i<NUM_ACRS; i++)
-		err |= __put_user(regs->acrs[i], &sregs->regs.acrs[i]);  
-	err |= __copy_to_user(&sregs->regs.psw.mask, &regs->psw.mask, 4);
-	err |= __copy_to_user(&sregs->regs.psw.addr, ((char*)&regs->psw.addr)+4, 4);
-	if(!err)
-	{
-		save_fp_regs(&fpregs);
-		__put_user(fpregs.fpc, &sregs->fpregs.fpc);
-		for(i=0; i<NUM_FPRS; i++)
-			err |= __put_user(fpregs.fprs[i].d, &sregs->fpregs.fprs[i].d);  
-	}
-	return(err);
-	
+	regs32.psw.mask = PSW32_USER_BITS |
+		((__u32)(regs->psw.mask >> 32) & PSW32_MASK_CC);
+	regs32.psw.addr = PSW32_ADDR_AMODE31 | (__u32) regs->psw.addr;
+	for (i = 0; i < NUM_GPRS; i++)
+		regs32.gprs[i] = (__u32) regs->gprs[i];
+	memcpy(regs32.acrs, regs->acrs, sizeof(regs32.acrs));
+	err = __copy_to_user(&sregs->regs, &regs32, sizeof(regs32));
+	if (err)
+		return err;
+	save_fp_regs(&current->thread.fp_regs);
+	/* s390_fp_regs and _s390_fp_regs32 are the same ! */
+	return __copy_to_user(&sregs->fpregs, &current->thread.fp_regs,
+			      sizeof(_s390_fp_regs32));
 }
 
 static int restore_sigregs32(struct pt_regs *regs,_sigregs32 *sregs)
 {
-	int err = 0;
-	s390_fp_regs fpregs;
-	psw_t saved_psw=regs->psw;
-	int i;
-
-	for(i=0; i<NUM_GPRS; i++)
-		err |= __get_user(regs->gprs[i], &sregs->regs.gprs[i]);  
-	for(i=0; i<NUM_ACRS; i++)
-		err |= __get_user(regs->acrs[i], &sregs->regs.acrs[i]);  
-	err |= __copy_from_user(&regs->psw.mask, &sregs->regs.psw.mask, 4);
-	err |= __copy_from_user(((char*)&regs->psw.addr)+4, &sregs->regs.psw.addr, 4);
-
-	if(!err)
-	{
-		regs->trap = -1;		/* disable syscall checks */
-		regs->psw.mask=(saved_psw.mask&~PSW_MASK_DEBUGCHANGE)|
-		(regs->psw.mask&PSW_MASK_DEBUGCHANGE);
-		regs->psw.addr=(saved_psw.addr&~PSW_ADDR_DEBUGCHANGE)|
-		(regs->psw.addr&PSW_ADDR_DEBUGCHANGE);
-		__get_user(fpregs.fpc, &sregs->fpregs.fpc);
-                for(i=0; i<NUM_FPRS; i++)
-                        err |= __get_user(fpregs.fprs[i].d, &sregs->fpregs.fprs[i].d);              
-		if(!err)
-			restore_fp_regs(&fpregs);
-	}
-	return(err);
+	_s390_regs_common32 regs32;
+	int err, i;
+
+	err = __copy_from_user(&regs32, &sregs->regs, sizeof(regs32));
+	if (err)
+		return err;
+	regs->psw.mask = PSW_USER32_BITS |
+		(__u64)(regs32.psw.mask & PSW32_MASK_CC) << 32;
+	regs->psw.addr = (__u64)(regs32.psw.addr & PSW32_ADDR_INSN);
+	for (i = 0; i < NUM_GPRS; i++)
+		regs->gprs[i] = (__u64) regs32.gprs[i];
+	memcpy(regs->acrs, regs32.acrs, sizeof(regs32.acrs));
+
+	err = __copy_from_user(&current->thread.fp_regs, &sregs->fpregs,
+			       sizeof(_s390_fp_regs32));
+	current->thread.fp_regs.fpc &= FPC_VALID_MASK;
+	if (err)
+		return err;
+
+	restore_fp_regs(&current->thread.fp_regs);
+	regs->trap = -1;	/* disable syscall checks */
+	return 0;
 }
 
 asmlinkage long sys32_sigreturn(struct pt_regs *regs)
@@ -352,10 +344,10 @@
 		goto badframe;
 
 	sigdelsetmask(&set, ~_BLOCKABLE);
-	spin_lock_irq(&current->sigmask_lock);
+	spin_lock_irq(&current->sig->siglock);
 	current->blocked = set;
 	recalc_sigpending();
-	spin_unlock_irq(&current->sigmask_lock);
+	spin_unlock_irq(&current->sig->siglock);
 
 	if (restore_sigregs32(regs, &frame->sregs))
 		goto badframe;
@@ -382,10 +374,10 @@
 		goto badframe;
 
 	sigdelsetmask(&set, ~_BLOCKABLE);
-	spin_lock_irq(&current->sigmask_lock);
+	spin_lock_irq(&current->sig->siglock);
 	current->blocked = set;
 	recalc_sigpending();
-	spin_unlock_irq(&current->sigmask_lock);
+	spin_unlock_irq(&current->sig->siglock);
 
 	if (restore_sigregs32(regs, &frame->uc.uc_mcontext))
 		goto badframe;
@@ -470,9 +462,9 @@
 	/* Set up to return from userspace.  If provided, use a stub
 	   already in userspace.  */
 	if (ka->sa.sa_flags & SA_RESTORER) {
-		regs->gprs[14] = FIX_PSW(ka->sa.sa_restorer);
+		regs->gprs[14] = (__u64) ka->sa.sa_restorer;
 	} else {
-		regs->gprs[14] = FIX_PSW(frame->retcode);
+		regs->gprs[14] = (__u64) frame->retcode;
 		if (__put_user(S390_SYSCALL_OPCODE | __NR_sigreturn,
 		               (u16 *)(frame->retcode)))
 			goto give_sigsegv;
@@ -483,12 +475,12 @@
 		goto give_sigsegv;
 
 	/* Set up registers for signal handler */
-	regs->gprs[15] = (addr_t)frame;
-	regs->psw.addr = FIX_PSW(ka->sa.sa_handler);
-	regs->psw.mask = _USER_PSW_MASK32;
+	regs->gprs[15] = (__u64) frame;
+	regs->psw.addr = (__u64) ka->sa.sa_handler;
+	regs->psw.mask = PSW_USER32_BITS;
 
 	regs->gprs[2] = map_signal(sig);
-	regs->gprs[3] = (addr_t)&frame->sc;
+	regs->gprs[3] = (__u64) &frame->sc;
 
 	/* We forgot to include these in the sigcontext.
 	   To avoid breaking binary compatibility, they are passed as args. */
@@ -528,9 +520,9 @@
 	/* Set up to return from userspace.  If provided, use a stub
 	   already in userspace.  */
 	if (ka->sa.sa_flags & SA_RESTORER) {
-		regs->gprs[14] = FIX_PSW(ka->sa.sa_restorer);
+		regs->gprs[14] = (__u64) ka->sa.sa_restorer;
 	} else {
-		regs->gprs[14] = FIX_PSW(frame->retcode);
+		regs->gprs[14] = (__u64) frame->retcode;
 		err |= __put_user(S390_SYSCALL_OPCODE | __NR_rt_sigreturn,
 		                  (u16 *)(frame->retcode));
 	}
@@ -540,13 +532,13 @@
 		goto give_sigsegv;
 
 	/* Set up registers for signal handler */
-	regs->gprs[15] = (addr_t)frame;
-	regs->psw.addr = FIX_PSW(ka->sa.sa_handler);
-	regs->psw.mask = _USER_PSW_MASK32;
+	regs->gprs[15] = (__u64) frame;
+	regs->psw.addr = (__u64) ka->sa.sa_handler;
+	regs->psw.mask = PSW_USER32_BITS;
 
 	regs->gprs[2] = map_signal(sig);
-	regs->gprs[3] = (addr_t)&frame->info;
-	regs->gprs[4] = (addr_t)&frame->uc;
+	regs->gprs[3] = (__u64) &frame->info;
+	regs->gprs[4] = (__u64) &frame->uc;
 	return;
 
 give_sigsegv:
@@ -595,11 +587,11 @@
 		ka->sa.sa_handler = SIG_DFL;
 
 	if (!(ka->sa.sa_flags & SA_NODEFER)) {
-		spin_lock_irq(&current->sigmask_lock);
+		spin_lock_irq(&current->sig->siglock);
 		sigorsets(&current->blocked,&current->blocked,&ka->sa.sa_mask);
 		sigaddset(&current->blocked,sig);
 		recalc_sigpending();
-		spin_unlock_irq(&current->sigmask_lock);
+		spin_unlock_irq(&current->sig->siglock);
 	}
 }
 
diff -Nru a/arch/s390x/kernel/smp.c b/arch/s390x/kernel/smp.c
--- a/arch/s390x/kernel/smp.c	Mon Oct  7 11:25:35 2002
+++ b/arch/s390x/kernel/smp.c	Mon Oct  7 11:25:35 2002
@@ -40,51 +40,21 @@
 /* prototypes */
 extern int cpu_idle(void * unused);
 
-extern __u16 boot_cpu_addr;
 extern volatile int __cpu_logical_map[];
 
 /*
  * An array with a pointer the lowcore of every CPU.
  */
-static int       max_cpus = NR_CPUS;	  /* Setup configured maximum number of CPUs to activate	*/
-int              smp_num_cpus;
+
 struct _lowcore *lowcore_ptr[NR_CPUS];
 cycles_t         cacheflush_time=0;
 int              smp_threads_ready=0;      /* Set when the idlers are all forked. */
-static atomic_t  smp_commenced = ATOMIC_INIT(0);
 
-volatile unsigned long phys_cpu_present_map;
 volatile unsigned long cpu_online_map;
+volatile unsigned long cpu_possible_map;
 unsigned long    cache_decay_ticks = 0;
 
 /*
- *      Setup routine for controlling SMP activation
- *
- *      Command-line option of "nosmp" or "maxcpus=0" will disable SMP
- *      activation entirely (the MPS table probe still happens, though).
- *
- *      Command-line option of "maxcpus=<NUM>", where <NUM> is an integer
- *      greater than 0, limits the maximum number of CPUs activated in
- *      SMP mode to <NUM>.
- */
-
-static int __init nosmp(char *str)
-{
-	max_cpus = 0;
-	return 1;
-}
-
-__setup("nosmp", nosmp);
-
-static int __init maxcpus(char *str)
-{
-	get_option(&str, &max_cpus);
-	return 1;
-}
-
-__setup("maxcpus=", maxcpus);
-
-/*
  * Reboot, halt and power_off routines for SMP.
  */
 extern char vmhalt_cmd[];
@@ -147,9 +117,10 @@
  */
 {
 	struct call_data_struct data;
-	int cpus = smp_num_cpus-1;
+	int cpus = num_online_cpus()-1;
 
-	if (!cpus || !atomic_read(&smp_commenced))
+	/* FIXME: get cpu lock -hc */
+	if (cpus <= 0)
 		return 0;
 
 	data.func = func;
@@ -182,8 +153,8 @@
         int i, rc;
 
         /* stop all processors */
-        for (i =  0; i < smp_num_cpus; i++) {
-                if (smp_processor_id() == i)
+        for (i =  0; i < NR_CPUS; i++) {
+                if (!cpu_online(i) || smp_processor_id() == i)
 			continue;
 		do {
 			rc = signal_processor_ps(&dummy, 0, i, sigp_stop);
@@ -198,10 +169,10 @@
         int i, rc;
 
         /* store status of all processors in their lowcores (real 0) */
-        for (i =  0; i < smp_num_cpus; i++) {
-                if (smp_processor_id() == i) 
+        for (i =  0; i < NR_CPUS; i++) {
+                if (!cpu_online(i) || smp_processor_id() == i) 
 			continue;
-		low_core_addr = (unsigned long)get_cpu_lowcore(i);
+		low_core_addr = (unsigned long) lowcore_ptr[i];
 		do {
 			rc = signal_processor_ps(&dummy, low_core_addr, i,
 						 sigp_store_status_at_address);
@@ -216,7 +187,7 @@
 void smp_send_stop(void)
 {
 	/* write magic number to zero page (absolute 0) */
-	get_cpu_lowcore(smp_processor_id())->panic_magic = __PANIC_MAGIC;
+	lowcore_ptr[smp_processor_id()]->panic_magic = __PANIC_MAGIC;
 
 	/* stop other processors. */
 	do_send_stop();
@@ -327,7 +298,7 @@
         /*
          * Set signaling bit in lowcore of target cpu and kick it
          */
-	set_bit(sig, &(get_cpu_lowcore(cpu)->ext_call_fast));
+	set_bit(sig, &lowcore_ptr[cpu]->ext_call_fast);
         ccode = signal_processor(cpu, sigp_external_call);
         return ccode;
 }
@@ -340,13 +311,13 @@
 {
         int i;
 
-        for (i = 0; i < smp_num_cpus; i++) {
-                if (smp_processor_id() == i)
+        for (i = 0; i < NR_CPUS; i++) {
+                if (!cpu_online(i) || smp_processor_id() == i)
                         continue;
                 /*
                  * Set signaling bit in lowcore of target cpu and kick it
                  */
-		set_bit(sig, &(get_cpu_lowcore(i)->ext_call_fast));
+		set_bit(sig, &lowcore_ptr[i]->ext_call_fast);
                 while (signal_processor(i, sigp_external_call) == sigp_busy)
 			udelay(10);
         }
@@ -405,13 +376,11 @@
 void smp_ctl_set_bit(int cr, int bit) {
         ec_creg_mask_parms parms;
 
-        if (atomic_read(&smp_commenced) != 0) {
-                parms.start_ctl = cr;
-                parms.end_ctl = cr;
-                parms.orvals[cr] = 1 << bit;
-                parms.andvals[cr] = -1L;
-                smp_call_function(smp_ctl_bit_callback, &parms, 0, 1);
-        }
+	parms.start_ctl = cr;
+	parms.end_ctl = cr;
+	parms.orvals[cr] = 1 << bit;
+	parms.andvals[cr] = -1L;
+	smp_call_function(smp_ctl_bit_callback, &parms, 0, 1);
         __ctl_set_bit(cr, bit);
 }
 
@@ -421,13 +390,11 @@
 void smp_ctl_clear_bit(int cr, int bit) {
         ec_creg_mask_parms parms;
 
-        if (atomic_read(&smp_commenced) != 0) {
-                parms.start_ctl = cr;
-                parms.end_ctl = cr;
-                parms.orvals[cr] = 0;
-                parms.andvals[cr] = ~(1L << bit);
-                smp_call_function(smp_ctl_bit_callback, &parms, 0, 1);
-        }
+	parms.start_ctl = cr;
+	parms.end_ctl = cr;
+	parms.orvals[cr] = 0;
+	parms.andvals[cr] = ~(1L << bit);
+	smp_call_function(smp_ctl_bit_callback, &parms, 0, 1);
         __ctl_clear_bit(cr, bit);
 }
 
@@ -436,26 +403,28 @@
  * Lets check how many CPUs we have.
  */
 
-void smp_count_cpus(void)
+void __init smp_check_cpus(unsigned int max_cpus)
 {
-        int curr_cpu;
+        int curr_cpu, num_cpus;
+	__u16 boot_cpu_addr;
 
+	boot_cpu_addr = S390_lowcore.cpu_data.cpu_addr;
         current_thread_info()->cpu = 0;
-        smp_num_cpus = 1;
-	phys_cpu_present_map = 1;
+        num_cpus = 1;
+	cpu_possible_map = 1;
         cpu_online_map = 1;
         for (curr_cpu = 0;
-             curr_cpu <= 65535 && smp_num_cpus < max_cpus; curr_cpu++) {
+             curr_cpu <= 65535 && num_cpus < max_cpus; curr_cpu++) {
                 if ((__u16) curr_cpu == boot_cpu_addr)
                         continue;
-                __cpu_logical_map[smp_num_cpus] = (__u16) curr_cpu;
-                if (signal_processor(smp_num_cpus, sigp_sense) ==
+                __cpu_logical_map[num_cpus] = (__u16) curr_cpu;
+                if (signal_processor(num_cpus, sigp_sense) ==
                     sigp_not_operational)
                         continue;
-		set_bit(smp_num_cpus, &phys_cpu_present_map);
-                smp_num_cpus++;
+		set_bit(num_cpus, &cpu_possible_map);
+                num_cpus++;
         }
-        printk("Detected %d CPU's\n",(int) smp_num_cpus);
+        printk("Detected %d CPU's\n",(int) num_cpus);
         printk("Boot cpu address %2X\n", boot_cpu_addr);
 }
 
@@ -466,46 +435,61 @@
 extern void init_cpu_timer(void);
 extern int pfault_init(void);
 
-int __init start_secondary(void *cpuvoid)
+int __devinit start_secondary(void *cpuvoid)
 {
         /* Setup the cpu */
         cpu_init();
-	/* Mark this cpu as online. */
-	set_bit(smp_processor_id(), &cpu_online_map);
-        /* Print info about this processor */
-        print_cpu_info(&safe_get_cpu_lowcore(smp_processor_id())->cpu_data);
-        /* Wait for completion of smp startup */
-        while (!atomic_read(&smp_commenced))
-                /* nothing */ ;
         /* init per CPU timer */
         init_cpu_timer();
 #ifdef CONFIG_PFAULT
 	/* Enable pfault pseudo page faults on this cpu. */
 	pfault_init();
 #endif
+	/* Mark this cpu as online. */
+	set_bit(smp_processor_id(), &cpu_online_map);
+	/* Switch on interrupts */
+	local_irq_enable();
+        /* Print info about this processor */
+        print_cpu_info(&S390_lowcore.cpu_data);
         /* cpu_idle will call schedule for us */
         return cpu_idle(NULL);
 }
 
-static struct task_struct * __init fork_by_hand(void)
+static struct task_struct * __devinit fork_by_hand(void)
 {
        struct pt_regs regs;
        /* don't care about the psw and regs settings since we'll never
           reschedule the forked task. */
        memset(&regs,0,sizeof(struct pt_regs));
-       return do_fork(CLONE_VM|CLONE_IDLETASK, 0, &regs, 0);
+       return do_fork(CLONE_VM|CLONE_IDLETASK, 0, &regs, 0, NULL);
 }
 
-static void __init do_boot_cpu(int cpu)
+int __cpu_up(unsigned int cpu)
 {
         struct task_struct *idle;
         struct _lowcore    *cpu_lowcore;
+	sigp_ccode          ccode;
+
+	/*
+	 * Set prefix page for new cpu
+	 */
+
+	ccode = signal_processor_p((u64) lowcore_ptr[cpu],
+				   cpu, sigp_set_prefix);
+	if(ccode){
+		printk("sigp_set_prefix failed for cpu %d "
+		       "with condition code %d\n",
+		       (int) cpu, (int) ccode);
+		return -EIO;
+	}
 
         /* We can't use kernel_thread since we must _avoid_ to reschedule
            the child. */
         idle = fork_by_hand();
-	if (IS_ERR(idle))
-                panic("failed fork for CPU %d", cpu);
+	if (IS_ERR(idle)){
+                printk("failed fork for CPU %d", cpu);
+		return -EIO;
+	}
 
         /*
          * We remove it from the pidhash and the runqueue
@@ -515,9 +499,9 @@
 
         unhash_process(idle);
 
-        cpu_lowcore = get_cpu_lowcore(cpu);
+        cpu_lowcore = lowcore_ptr[cpu];
 	cpu_lowcore->save_area[15] = idle->thread.ksp;
-	cpu_lowcore->kernel_stack = (__u64) idle->thread_info + 16384;
+	cpu_lowcore->kernel_stack = (__u64) idle->thread_info + (4*PAGE_SIZE);
         __asm__ __volatile__("la    1,%0\n\t"
 			     "stctg 0,15,0(1)\n\t"
 			     "la    1,%1\n\t"
@@ -528,75 +512,47 @@
 
         eieio();
         signal_processor(cpu,sigp_restart);
-}
-
-/*
- *      Architecture specific routine called by the kernel just before init is
- *      fired off. This allows the BP to have everything in order [we hope].
- *      At the end of this all the APs will hit the system scheduling and off
- *      we go. Each AP will load the system gdt's and jump through the kernel
- *      init into idle(). At this point the scheduler will one day take over
- *      and give them jobs to do. smp_callin is a standard routine
- *      we use to track CPUs as they power up.
- */
 
-void __init smp_commence(void)
-{
-        /*
-         *      Lets the callins below out of their loop.
-         */
-        atomic_set(&smp_commenced,1);
+	while (!cpu_online(cpu));
+	return 0;
 }
 
 /*
- *	Cycle through the processors sending restart sigps to boot each.
+ *	Cycle through the processors and setup structures.
  */
 
-void __init smp_boot_cpus(void)
+void __init smp_prepare_cpus(unsigned int max_cpus)
 {
 	unsigned long async_stack;
-        sigp_ccode   ccode;
         int i;
 
         /* request the 0x1202 external interrupt */
         if (register_external_interrupt(0x1202, do_ext_call_interrupt) != 0)
                 panic("Couldn't request external interrupt 0x1202");
-        smp_count_cpus();
+        smp_check_cpus(max_cpus);
         memset(lowcore_ptr,0,sizeof(lowcore_ptr));  
         
         /*
-         *      Initialize the logical to physical CPU number mapping
+         *  Initialize prefix pages and stacks for all possible cpus
          */
-        print_cpu_info(&safe_get_cpu_lowcore(0)->cpu_data);
+        print_cpu_info(&S390_lowcore.cpu_data);
 
-        for(i = 0; i < smp_num_cpus; i++) {
-                lowcore_ptr[i] = (struct _lowcore *)
-                                    __get_free_pages(GFP_KERNEL|GFP_DMA, 1);
+        for(i = 0; i < NR_CPUS; i++) {
+		if (!cpu_possible(i))
+			continue;
+		lowcore_ptr[i] = (struct _lowcore *)
+			__get_free_pages(GFP_KERNEL|GFP_DMA, 1);
 		async_stack = __get_free_pages(GFP_KERNEL,2);
 		if (lowcore_ptr[i] == NULL || async_stack == 0ULL)
 			panic("smp_boot_cpus failed to allocate memory\n");
                 memcpy(lowcore_ptr[i], &S390_lowcore, sizeof(struct _lowcore));
 		lowcore_ptr[i]->async_stack = async_stack + (4 * PAGE_SIZE);
-                /*
-                 * Most of the parameters are set up when the cpu is
-                 * started up.
-                 */
-                if (smp_processor_id() == i) {
-                        set_prefix((u32)(u64) lowcore_ptr[i]);
-			continue;
-		}
-		ccode = signal_processor_p((u64) lowcore_ptr[i],
-					   i, sigp_set_prefix);
-		if(ccode)
-			panic("sigp_set_prefix failed for cpu %d "
-			      "with condition code %d\n",
-			      (int) i, (int) ccode);
-		do_boot_cpu(i);
-        }
-	/*
-	 * Now wait until all of the cpus are online.
-	 */
-	while (phys_cpu_present_map != cpu_online_map);
+	}
+	set_prefix((u32)(u64) lowcore_ptr[smp_processor_id()]);
+}
+
+void smp_cpus_done(unsigned int max_cpis)
+{
 }
 
 /*
@@ -613,5 +569,4 @@
 EXPORT_SYMBOL(lowcore_ptr);
 EXPORT_SYMBOL(smp_ctl_set_bit);
 EXPORT_SYMBOL(smp_ctl_clear_bit);
-EXPORT_SYMBOL(smp_num_cpus);
 EXPORT_SYMBOL(smp_call_function);
diff -Nru a/arch/s390x/kernel/time.c b/arch/s390x/kernel/time.c
--- a/arch/s390x/kernel/time.c	Mon Oct  7 11:25:37 2002
+++ b/arch/s390x/kernel/time.c	Mon Oct  7 11:25:37 2002
@@ -37,28 +37,35 @@
 #define USECS_PER_JIFFY     ((unsigned long) 1000000/HZ)
 #define CLK_TICKS_PER_JIFFY ((unsigned long) USECS_PER_JIFFY << 12)
 
+/*
+ * Create a small time difference between the timer interrupts
+ * on the different cpus to avoid lock contention.
+ */
+#define CPU_DEVIATION       (smp_processor_id() << 12)
+
 #define TICK_SIZE tick
 
 u64 jiffies_64;
 
 static ext_int_info_t ext_int_info_timer;
+static uint64_t xtime_cc;
 static uint64_t init_timer_cc;
 
 extern rwlock_t xtime_lock;
 extern unsigned long wall_jiffies;
 
-void tod_to_timeval(__u64 todval, struct timeval *xtime)
+void tod_to_timeval(__u64 todval, struct timespec *xtime)
 {
-        todval >>= 12;
-        xtime->tv_sec = todval / 1000000;
-        xtime->tv_usec = todval % 1000000;
+        xtime->tv_sec = (todval >> 12) / 1000000;
+	todval -= (xtime->tv_sec * 1000000) << 12;
+	xtime->tv_nsec = ((todval * 1000) >> 12);
 }
 
 static inline unsigned long do_gettimeoffset(void) 
 {
 	__u64 now;
 
-	asm ("STCK 0(%0)" : : "a" (&now) : "memory", "cc");
+	asm volatile ("STCK 0(%0)" : : "a" (&now) : "memory", "cc");
         now = (now - init_timer_cc) >> 12;
 	/* We require the offset from the latest update of xtime */
 	now -= (__u64) wall_jiffies*USECS_PER_JIFFY;
@@ -75,7 +82,7 @@
 
 	read_lock_irqsave(&xtime_lock, flags);
 	sec = xtime.tv_sec;
-	usec = xtime.tv_usec + do_gettimeoffset();
+	usec = xtime.tv_nsec + do_gettimeoffset();
 	read_unlock_irqrestore(&xtime_lock, flags);
 
 	while (usec >= 1000000) {
@@ -104,7 +111,8 @@
 		tv->tv_sec--;
 	}
 
-	xtime = *tv;
+	xtime.tv_sec = tv->tv_sec;
+	xtime.tv_nsec = tv->tv_usec * 1000;
 	time_adjust = 0;		/* stop active adjtime() */
 	time_status |= STA_UNSYNC;
 	time_maxerror = NTP_PHASE_LIMIT;
@@ -116,54 +124,77 @@
  * timer_interrupt() needs to keep up the real-time clock,
  * as well as call the "do_timer()" routine every clocktick
  */
-
-#ifdef CONFIG_SMP
-extern __u16 boot_cpu_addr;
-#endif
-
 static void do_comparator_interrupt(struct pt_regs *regs, __u16 error_code)
 {
 	int cpu = smp_processor_id();
+	__u64 tmp;
+	__u32 ticks;
 
-	irq_enter(cpu, 0);
-
-	/*
-	 * set clock comparator for next tick
-	 */
-        S390_lowcore.jiffy_timer += CLK_TICKS_PER_JIFFY;
-        asm volatile ("SCKC %0" : : "m" (S390_lowcore.jiffy_timer));
+	/* Calculate how many ticks have passed. */
+	asm volatile ("STCK 0(%0)" : : "a" (&tmp) : "memory", "cc");
+	tmp = tmp - S390_lowcore.jiffy_timer;
+	if (tmp >= 2*CLK_TICKS_PER_JIFFY) {  /* more than one tick ? */
+		ticks = tmp / CLK_TICKS_PER_JIFFY;
+		S390_lowcore.jiffy_timer +=
+			CLK_TICKS_PER_JIFFY * (__u64) ticks;
+	} else {
+		ticks = 1;
+		S390_lowcore.jiffy_timer += CLK_TICKS_PER_JIFFY;
+	}
 
-#ifdef CONFIG_SMP
-	if (S390_lowcore.cpu_data.cpu_addr == boot_cpu_addr)
-		write_lock(&xtime_lock);
+	/* set clock comparator for next tick */
+	tmp = S390_lowcore.jiffy_timer + CLK_TICKS_PER_JIFFY + CPU_DEVIATION;
+        asm volatile ("SCKC %0" : : "m" (tmp));
 
-	update_process_times(user_mode(regs));
+	irq_enter();
 
-	if (S390_lowcore.cpu_data.cpu_addr == boot_cpu_addr) {
-		do_timer(regs);
-		write_unlock(&xtime_lock);
+#ifdef CONFIG_SMP
+	/*
+	 * Do not rely on the boot cpu to do the calls to do_timer.
+	 * Spread it over all cpus instead.
+	 */
+	write_lock(&xtime_lock);
+	if (S390_lowcore.jiffy_timer > xtime_cc) {
+		__u32 xticks;
+
+		tmp = S390_lowcore.jiffy_timer - xtime_cc;
+		if (tmp >= 2*CLK_TICKS_PER_JIFFY) {
+			xticks = tmp / CLK_TICKS_PER_JIFFY;
+			xtime_cc += (__u64) xticks * CLK_TICKS_PER_JIFFY;
+		} else {
+			xticks = 1;
+			xtime_cc += CLK_TICKS_PER_JIFFY;
+		}
+		while (xticks--)
+			do_timer(regs);
 	}
+	write_unlock(&xtime_lock);
+	while (ticks--)
+		update_process_times(user_mode(regs));
 #else
-	do_timer(regs);
+	while (ticks--)
+		do_timer(regs);
 #endif
 
-	irq_exit(cpu, 0);
+	irq_exit();
 }
 
 /*
- * Start the clock comparator on the current CPU
+ * Start the clock comparator on the current CPU.
  */
 void init_cpu_timer(void)
 {
 	unsigned long cr0;
+	__u64 timer;
 
         /* allow clock comparator timer interrupt */
         asm volatile ("STCTG 0,0,%0" : "=m" (cr0) : : "memory");
         cr0 |= 0x800;
         asm volatile ("LCTLG 0,0,%0" : : "m" (cr0) : "memory");
-	S390_lowcore.jiffy_timer = (__u64) jiffies * CLK_TICKS_PER_JIFFY;
-	S390_lowcore.jiffy_timer += init_timer_cc + CLK_TICKS_PER_JIFFY;
-	asm volatile ("SCKC %0" : : "m" (S390_lowcore.jiffy_timer));
+	timer = init_timer_cc + jiffies_64 * CLK_TICKS_PER_JIFFY;
+	S390_lowcore.jiffy_timer = timer;
+	timer += CLK_TICKS_PER_JIFFY + CPU_DEVIATION;
+	asm volatile ("SCKC %0" : : "m" (timer));
 }
 
 /*
@@ -172,7 +203,7 @@
  */
 void __init time_init(void)
 {
-        __u64 set_time_cc;
+	__u64 set_time_cc;
 	int cc;
 
         /* kick the TOD clock */
@@ -195,8 +226,9 @@
         }
 
 	/* set xtime */
-        set_time_cc = init_timer_cc - 0x8126d60e46000000LL +
-                      (0x3c26700LL*1000000*4096);
+	xtime_cc = init_timer_cc;
+	set_time_cc = init_timer_cc - 0x8126d60e46000000LL +
+		(0x3c26700LL*1000000*4096);
         tod_to_timeval(set_time_cc, &xtime);
 
         /* request the 0x1004 external interrupt */
diff -Nru a/arch/s390x/kernel/traps.c b/arch/s390x/kernel/traps.c
--- a/arch/s390x/kernel/traps.c	Mon Oct  7 11:25:36 2002
+++ b/arch/s390x/kernel/traps.c	Mon Oct  7 11:25:36 2002
@@ -118,22 +118,22 @@
 		stack = (unsigned long*)&stack;
 
 	printk("Call Trace: ");
-	low_addr = ((unsigned long) stack) & PSW_ADDR_MASK;
+	low_addr = (unsigned long) stack;
 	high_addr = (low_addr & (-THREAD_SIZE)) + THREAD_SIZE;
 	/* Skip the first frame (biased stack) */
-	backchain = *((unsigned long *) low_addr) & PSW_ADDR_MASK;
+	backchain = *(unsigned long *) low_addr;
 	/* Print up to 8 lines */
 	for (i = 0; i < 8; i++) {
 		if (backchain < low_addr || backchain >= high_addr)
 			break;
-		ret_addr = *((unsigned long *) (backchain+112)) & PSW_ADDR_MASK;
+		ret_addr = *(unsigned long *) (backchain+112);
 		if (!kernel_text_address(ret_addr))
 			break;
 		if (i && ((i % 3) == 0))
 			printk("\n   ");
 		printk("[<%016lx>] ", ret_addr);
 		low_addr = backchain;
-		backchain = *((unsigned long *) backchain) & PSW_ADDR_MASK;
+		backchain = *(unsigned long *) backchain;
 	}
 	printk("\n");
 }
@@ -172,13 +172,21 @@
 	show_trace(sp);
 }
 
+/*
+ * The architecture-independent dump_stack generator
+ */
+void dump_stack(void)
+{
+	show_stack(0);
+}
+
 void show_registers(struct pt_regs *regs)
 {
 	mm_segment_t old_fs;
 	char *mode;
 	int i;
 
-	mode = (regs->psw.mask & PSW_PROBLEM_STATE) ? "User" : "Krnl";
+	mode = (regs->psw.mask & PSW_MASK_PSTATE) ? "User" : "Krnl";
 	printk("%s PSW : %016lx %016lx\n",
 	       mode, (unsigned long) regs->psw.mask,
 	       (unsigned long) regs->psw.addr);
@@ -204,7 +212,7 @@
 	 * time of the fault.
 	 */
 	old_fs = get_fs();
-	if (regs->psw.mask & PSW_PROBLEM_STATE)
+	if (regs->psw.mask & PSW_MASK_PSTATE)
 		set_fs(USER_DS);
 	else
 		set_fs(KERNEL_DS);
@@ -281,10 +289,10 @@
 	 * We got all needed information from the lowcore and can
 	 * now safely switch on interrupts.
 	 */
-	if (regs->psw.mask & PSW_PROBLEM_STATE)
+	if (regs->psw.mask & PSW_MASK_PSTATE)
 		local_irq_enable();
 
-        if (regs->psw.mask & PSW_PROBLEM_STATE) {
+        if (regs->psw.mask & PSW_MASK_PSTATE) {
                 struct task_struct *tsk = current;
                 tsk->thread.trap_no = interruption_code & 0xffff;
 		if (info)
@@ -315,12 +323,12 @@
 
 static inline void *get_check_address(struct pt_regs *regs)
 {
-	return (void *) ADDR_BITS_REMOVE(regs->psw.addr-S390_lowcore.pgm_ilc);
+	return (void *)(regs->psw.addr - S390_lowcore.pgm_ilc);
 }
 
 int do_debugger_trap(struct pt_regs *regs,int signal)
 {
-	if(regs->psw.mask&PSW_PROBLEM_STATE)
+	if(regs->psw.mask&PSW_MASK_PSTATE)
 	{
 		if(current->ptrace & PT_PTRACED)
 			force_sig(signal,current);
@@ -418,14 +426,14 @@
 	 * We got all needed information from the lowcore and can
 	 * now safely switch on interrupts.
 	 */
-	if (regs->psw.mask & PSW_PROBLEM_STATE)
+	if (regs->psw.mask & PSW_MASK_PSTATE)
 		local_irq_enable();
 
 	/* WARNING don't change this check back to */
-	/* int problem_state=(regs->psw.mask & PSW_PROBLEM_STATE); */
+	/* int problem_state=(regs->psw.mask & PSW_MASK_PSTATE); */
 	/* & then doing if(problem_state) an int is too small for this */
 	/* check on 64 bit. */
-	if(regs->psw.mask & PSW_PROBLEM_STATE)
+	if(regs->psw.mask & PSW_MASK_PSTATE)
 		get_user(*((__u16 *) opcode), location);
 	else
 		*((__u16 *)opcode)=*((__u16 *)location);
@@ -451,7 +459,7 @@
 	 * We got all needed information from the lowcore and can
 	 * now safely switch on interrupts.
 	 */
-	if (regs->psw.mask & PSW_PROBLEM_STATE)
+	if (regs->psw.mask & PSW_MASK_PSTATE)
 		local_irq_enable();
 
 	__asm__ volatile ("stfpc %0\n\t" 
@@ -519,21 +527,19 @@
 
 void handle_per_exception(struct pt_regs *regs)
 {
-	if(regs->psw.mask&PSW_PROBLEM_STATE)
-	{
+	if (regs->psw.mask&PSW_MASK_PSTATE) {
 		per_struct *per_info=&current->thread.per_info;
 		per_info->lowcore.words.perc_atmid=S390_lowcore.per_perc_atmid;
 		per_info->lowcore.words.address=S390_lowcore.per_address;
 		per_info->lowcore.words.access_id=S390_lowcore.per_access_id;
 	}
-	if(do_debugger_trap(regs,SIGTRAP))
-	{
+	if (do_debugger_trap(regs,SIGTRAP)) {
 		/* I've seen this possibly a task structure being reused ? */
 		printk("Spurious per exception detected\n");
 		printk("switching off per tracing for this task.\n");
 		show_regs(regs);
 		/* Hopefully switching off per tracing will help us survive */
-		regs->psw.mask &= ~PSW_PER_MASK;
+		regs->psw.mask &= ~PSW_MASK_PER;
 	}
 }
 
diff -Nru a/arch/s390x/kernel/wrapper32.S b/arch/s390x/kernel/wrapper32.S
--- a/arch/s390x/kernel/wrapper32.S	Mon Oct  7 11:25:35 2002
+++ b/arch/s390x/kernel/wrapper32.S	Mon Oct  7 11:25:35 2002
@@ -872,23 +872,23 @@
 
 #sys32_rt_sigsuspend_wrapper		# done in rt_sigsuspend_glue 
 
-	.globl  sys32_pread_wrapper 
-sys32_pread_wrapper:
+	.globl  sys32_pread64_wrapper 
+sys32_pread64_wrapper:
 	llgfr	%r2,%r2			# unsigned int
 	llgtr	%r3,%r3			# char *
 	llgfr	%r4,%r4			# size_t
 	llgfr	%r5,%r5			# u32
 	llgfr	%r6,%r6			# u32
-	jg	sys32_pread		# branch to system call
+	jg	sys32_pread64		# branch to system call
 
-	.globl  sys32_pwrite_wrapper 
-sys32_pwrite_wrapper:
+	.globl  sys32_pwrite64_wrapper 
+sys32_pwrite64_wrapper:
 	llgfr	%r2,%r2			# unsigned int
 	llgtr	%r3,%r3			# const char *
 	llgfr	%r4,%r4			# size_t
 	llgfr	%r5,%r5			# u32
 	llgfr	%r6,%r6			# u32
-	jg	sys32_pwrite		# branch to system call
+	jg	sys32_pwrite64		# branch to system call
 
 	.globl  sys32_chown16_wrapper 
 sys32_chown16_wrapper:
@@ -1114,7 +1114,7 @@
 	lgfr	%r3,%r3			# int
 	lgfr	%r4,%r4			# int
 	llgtr	%r5,%r5			# struct timespec *
-	jg	sys_futex		# branch to system call
+	jg	sys32_futex		# branch to system call
 
 	.globl	sys32_setxattr_wrapper
 sys32_setxattr_wrapper:
@@ -1220,3 +1220,7 @@
 	llgtr	%r4,%r4			# unsigned long *
 	jg	sys32_sched_getaffinity
 
+	.globl  sys32_exit_group_wrapper
+sys32_exit_group_wrapper:
+	lgfr	%r2,%r2			# int
+	jg	sys_exit_group		# branch to system call
diff -Nru a/arch/s390x/lib/Makefile b/arch/s390x/lib/Makefile
--- a/arch/s390x/lib/Makefile	Mon Oct  7 11:25:26 2002
+++ b/arch/s390x/lib/Makefile	Mon Oct  7 11:25:26 2002
@@ -6,8 +6,7 @@
 
 EXTRA_AFLAGS := -traditional
 
-obj-y = checksum.o delay.o memset.o misaligned.o strcmp.o strncpy.o uaccess.o
-export-objs += misaligned.o
+obj-y = delay.o memset.o strcmp.o strncpy.o uaccess.o
 
 include $(TOPDIR)/Rules.make
 
diff -Nru a/arch/s390x/lib/checksum.c b/arch/s390x/lib/checksum.c
--- a/arch/s390x/lib/checksum.c	Mon Oct  7 11:25:35 2002
+++ /dev/null	Wed Dec 31 16:00:00 1969
@@ -1,40 +0,0 @@
-/*
- *  arch/s390/lib/checksum.c
- *    S390 fast network checksum routines
- *
- *  S390 version
- *    Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation
- *    Author(s): Ulrich Hild        (first version),
- *               Martin Schwidefsky (schwidefsky@de.ibm.com),
- *               Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com),
- *
- * This file contains network checksum routines
- */
- 
-#include <linux/string.h>
-#include <linux/types.h>
-#include <asm/uaccess.h>
-#include <asm/byteorder.h>
-#include <asm/checksum.h>
-
-/*
- * computes a partial checksum, e.g. for TCP/UDP fragments
- */
-unsigned int
-csum_partial (const unsigned char *buff, int len, unsigned int sum)
-{
-	  /*
-	   * Experiments with ethernet and slip connections show that buff
-	   * is aligned on either a 2-byte or 4-byte boundary.
-	   */
-        __asm__ __volatile__ (
-                "    lgr  2,%1\n"    /* address in gpr 2 */
-                "    lgfr 3,%2\n"    /* length in gpr 3 */
-                "0:  cksm %0,2\n"    /* do checksum on longs */
-                "    jo   0b\n"
-                : "+&d" (sum)
-                : "d" (buff), "d" (len)
-                : "cc", "2", "3" );
-        return sum;
-}
-
diff -Nru a/arch/s390x/lib/misaligned.c b/arch/s390x/lib/misaligned.c
--- a/arch/s390x/lib/misaligned.c	Mon Oct  7 11:25:36 2002
+++ /dev/null	Wed Dec 31 16:00:00 1969
@@ -1,34 +0,0 @@
-/*
- *  arch/s390/lib/misaligned.c
- *    S390 misalignment panic stubs
- *
- *  S390 version
- *    Copyright (C) 2001 IBM Deutschland Entwicklung GmbH, IBM Corporation
- *    Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com).
- *
- * xchg wants to panic if the pointer is not aligned. To avoid multiplying
- * the panic message over and over again, the panic is done in the helper
- * functions __misaligned_u64, __misaligned_u32 and __misaligned_u16.
- */
- 
-#include <linux/module.h>
-#include <linux/kernel.h>
-
-void __misaligned_u16(void)
-{
-	panic("misaligned (__u16 *) in __xchg\n");
-}
-
-void __misaligned_u32(void)
-{
-	panic("misaligned (__u32 *) in __xchg\n");
-}
-
-void __misaligned_u64(void)
-{
-	panic("misaligned (__u64 *) in __xchg\n");
-}
-
-EXPORT_SYMBOL(__misaligned_u16);
-EXPORT_SYMBOL(__misaligned_u32);
-EXPORT_SYMBOL(__misaligned_u64);
diff -Nru a/arch/s390x/mm/fault.c b/arch/s390x/mm/fault.c
--- a/arch/s390x/mm/fault.c	Mon Oct  7 11:25:30 2002
+++ b/arch/s390x/mm/fault.c	Mon Oct  7 11:25:30 2002
@@ -45,7 +45,6 @@
  */
 void bust_spinlocks(int yes)
 {
-	spin_lock_init(&timerlist_lock);
 	if (yes) {
 		oops_in_progress = 1;
 	} else {
@@ -167,7 +166,7 @@
 
 		/* Low-address protection hit in kernel mode means 
 		   NULL pointer write access in kernel mode.  */
- 		if (!(regs->psw.mask & PSW_PROBLEM_STATE)) {
+ 		if (!(regs->psw.mask & PSW_MASK_PSTATE)) {
 			address = 0;
 			user_address = 0;
 			goto no_context;
@@ -234,16 +233,18 @@
 	 * the fault.
 	 */
 	switch (handle_mm_fault(mm, vma, address, error_code == 4)) {
-	case 1:
+	case VM_FAULT_MINOR:
 		tsk->min_flt++;
 		break;
-	case 2:
+	case VM_FAULT_MAJOR:
 		tsk->maj_flt++;
 		break;
-	case 0:
+	case VM_FAULT_SIGBUS:
 		goto do_sigbus;
-	default:
+	case VM_FAULT_OOM:
 		goto out_of_memory;
+	default:
+		BUG();
 	}
 
         up_read(&mm->mmap_sem);
@@ -257,7 +258,7 @@
         up_read(&mm->mmap_sem);
 
         /* User mode accesses just cause a SIGSEGV */
-        if (regs->psw.mask & PSW_PROBLEM_STATE) {
+        if (regs->psw.mask & PSW_MASK_PSTATE) {
                 tsk->thread.prot_addr = address;
                 tsk->thread.trap_no = error_code;
 		force_sigsegv(regs, error_code, si_code, address);
@@ -297,7 +298,7 @@
 		goto survive;
 	}
 	printk("VM: killing process %s\n", tsk->comm);
-	if (regs->psw.mask & PSW_PROBLEM_STATE)
+	if (regs->psw.mask & PSW_MASK_PSTATE)
 		do_exit(SIGKILL);
 	goto no_context;
 
@@ -313,7 +314,7 @@
 	force_sig(SIGBUS, tsk);
 
 	/* Kernel mode? Handle exceptions or die */
-	if (!(regs->psw.mask & PSW_PROBLEM_STATE))
+	if (!(regs->psw.mask & PSW_MASK_PSTATE))
 		goto no_context;
 }
 
@@ -439,7 +440,7 @@
 	 * We got all needed information from the lowcore and can
 	 * now safely switch on interrupts.
 	 */
-	if (regs->psw.mask & PSW_PROBLEM_STATE)
+	if (regs->psw.mask & PSW_MASK_PSTATE)
 		local_irq_enable();
 
 	if (subcode & 0x0080) {
diff -Nru a/arch/s390x/vmlinux-shared.lds b/arch/s390x/vmlinux-shared.lds
--- a/arch/s390x/vmlinux-shared.lds	Mon Oct  7 11:25:36 2002
+++ /dev/null	Wed Dec 31 16:00:00 1969
@@ -1,93 +0,0 @@
-/* ld script to make s390 Linux kernel
- * Written by Martin Schwidefsky (schwidefsky@de.ibm.com)
- */
-OUTPUT_FORMAT("elf64-s390", "elf64-s390", "elf64-s390")
-OUTPUT_ARCH(s390)
-ENTRY(_start)
-jiffies = jiffies_64;
-SECTIONS
-{
-  . = 0x00000000;
-  _text = .;			/* Text and read-only data */
-  .text : {
-	*(.text)
-	*(.fixup)
-	*(.gnu.warning)
-	} = 0x0700
-
-  _etext = .;			/* End of text section */
-
-  .rodata : { *(.rodata) }
-  .kstrtab : { *(.kstrtab) }
-
-  . = ALIGN(16);		/* Exception table */
-  __start___ex_table = .;
-  __ex_table : { *(__ex_table) }
-  __stop___ex_table = .;
-
-  __start___ksymtab = .;	/* Kernel symbol table */
-  __ksymtab : { *(__ksymtab) }
-  __stop___ksymtab = .;
-
-  . = ALIGN(1048576);		/* VM shared segments are 1MB aligned */
-
-  _eshared = .;			/* End of shareable data */
-
-  .data : {			/* Data */
-	*(.data)
-	CONSTRUCTORS
-	}
-
-  _edata = .;			/* End of data section */
-
-  . = ALIGN(16384);		/* init_task */
-  .data.init_task : { *(.data.init_task) }
-
-  . = ALIGN(4096);		/* Init code and data */
-  __init_begin = .;
-  .text.init : { *(.text.init) }
-  .data.init : { *(.data.init) }
-  . = ALIGN(256);
-  __setup_start = .;
-  .setup.init : { *(.setup.init) }
-  __setup_end = .;
-  __initcall_start = .;
-  .initcall.init : {
-	*(.initcall1.init) 
-	*(.initcall2.init) 
-	*(.initcall3.init) 
-	*(.initcall4.init) 
-	*(.initcall5.init) 
-	*(.initcall6.init) 
-	*(.initcall7.init)
-  }
-  __initcall_end = .;
-  . = ALIGN(256);
-  __per_cpu_start = .;
-  .date.percpu  : { *(.data.percpu) }
-  __per_cpu_end = .;
-  . = ALIGN(4096);
-  __init_end = .;
-
-  . = ALIGN(32);
-  .data.cacheline_aligned : { *(.data.cacheline_aligned) }
-
-  . = ALIGN(4096);
-  .data.page_aligned : { *(.data.idt) }
-
-
-  __bss_start = .;		/* BSS */
-  .bss : {
-	*(.bss)
-	}
-  _end = . ;
-
-  /* Stabs debugging sections.  */
-  .stab 0 : { *(.stab) }
-  .stabstr 0 : { *(.stabstr) }
-  .stab.excl 0 : { *(.stab.excl) }
-  .stab.exclstr 0 : { *(.stab.exclstr) }
-  .stab.index 0 : { *(.stab.index) }
-  .stab.indexstr 0 : { *(.stab.indexstr) }
-  .comment 0 : { *(.comment) }
-}
diff -Nru a/arch/s390x/vmlinux.lds b/arch/s390x/vmlinux.lds
--- a/arch/s390x/vmlinux.lds	Mon Oct  7 11:25:37 2002
+++ /dev/null	Wed Dec 31 16:00:00 1969
@@ -1,89 +0,0 @@
-/* ld script to make s390 Linux kernel
- * Written by Martin Schwidefsky (schwidefsky@de.ibm.com)
- */
-OUTPUT_FORMAT("elf64-s390", "elf64-s390", "elf64-s390")
-OUTPUT_ARCH(s390)
-ENTRY(_start)
-jiffies = jiffies_64;
-SECTIONS
-{
-  . = 0x00000000;
-  _text = .;			/* Text and read-only data */
-  .text : {
-	*(.text)
-	*(.fixup)
-	*(.gnu.warning)
-	} = 0x0700
-
-  _etext = .;			/* End of text section */
-
-  .rodata : { *(.rodata) *(.rodata.*) }
-  .kstrtab : { *(.kstrtab) }
-
-  . = ALIGN(16);		/* Exception table */
-  __start___ex_table = .;
-  __ex_table : { *(__ex_table) }
-  __stop___ex_table = .;
-
-  __start___ksymtab = .;	/* Kernel symbol table */
-  __ksymtab : { *(__ksymtab) }
-  __stop___ksymtab = .;
-
-  .data : {			/* Data */
-	*(.data)
-	CONSTRUCTORS
-	}
-
-  _edata = .;			/* End of data section */
-
-  . = ALIGN(16384);		/* init_task */
-  .data.init_task : { *(.data.init_task) }
-
-  . = ALIGN(4096);		/* Init code and data */
-  __init_begin = .;
-  .text.init : { *(.text.init) }
-  .data.init : { *(.data.init) }
-  . = ALIGN(256);
-  __setup_start = .;
-  .setup.init : { *(.setup.init) }
-  __setup_end = .;
-  __initcall_start = .;
-  .initcall.init : {
-	*(.initcall1.init) 
-	*(.initcall2.init) 
-	*(.initcall3.init) 
-	*(.initcall4.init) 
-	*(.initcall5.init) 
-	*(.initcall6.init) 
-	*(.initcall7.init)
-  }
-  __initcall_end = .;
-  . = ALIGN(256);
-  __per_cpu_start = .;
-  .date.percpu  : { *(.data.percpu) }
-  __per_cpu_end = .;
-  . = ALIGN(4096);
-  __init_end = .;
-
-  . = ALIGN(32);
-  .data.cacheline_aligned : { *(.data.cacheline_aligned) }
-
-  . = ALIGN(4096);
-  .data.page_aligned : { *(.data.idt) }
-
-
-  __bss_start = .;		/* BSS */
-  .bss : {
-	*(.bss)
-	}
-  _end = . ;
-
-  /* Stabs debugging sections.  */
-  .stab 0 : { *(.stab) }
-  .stabstr 0 : { *(.stabstr) }
-  .stab.excl 0 : { *(.stab.excl) }
-  .stab.exclstr 0 : { *(.stab.exclstr) }
-  .stab.index 0 : { *(.stab.index) }
-  .stab.indexstr 0 : { *(.stab.indexstr) }
-  .comment 0 : { *(.comment) }
-}
diff -Nru a/arch/s390x/vmlinux.lds.S b/arch/s390x/vmlinux.lds.S
--- a/arch/s390x/vmlinux.lds.S	Mon Oct  7 11:25:29 2002
+++ b/arch/s390x/vmlinux.lds.S	Mon Oct  7 11:25:29 2002
@@ -1,7 +1,95 @@
-#include <linux/config.h>
+/* ld script to make s390 Linux kernel
+ * Written by Martin Schwidefsky (schwidefsky@de.ibm.com)
+ */
+OUTPUT_FORMAT("elf64-s390", "elf64-s390", "elf64-s390")
+OUTPUT_ARCH(s390:64-bit)
+ENTRY(_start)
+jiffies = jiffies_64;
+SECTIONS
+{
+  . = 0x00000000;
+  _text = .;			/* Text and read-only data */
+  .text : {
+	*(.text)
+	*(.fixup)
+	*(.gnu.warning)
+	} = 0x0700
+
+  _etext = .;			/* End of text section */
+
+  .rodata : { *(.rodata) *(.rodata.*) }
+  .kstrtab : { *(.kstrtab) }
+
+  . = ALIGN(16);		/* Exception table */
+  __start___ex_table = .;
+  __ex_table : { *(__ex_table) }
+  __stop___ex_table = .;
+
+  __start___ksymtab = .;	/* Kernel symbol table */
+  __ksymtab : { *(__ksymtab) }
+  __stop___ksymtab = .;
 
 #ifdef CONFIG_SHARED_KERNEL
-#include "vmlinux-shared.lds"
-#else
-#include "vmlinux.lds"
+  . = ALIGN(1048576);		/* VM shared segments are 1MB aligned */
+
+  _eshared = .;			/* End of shareable data */
 #endif
+
+  .data : {			/* Data */
+	*(.data)
+	CONSTRUCTORS
+	}
+
+  _edata = .;			/* End of data section */
+
+  . = ALIGN(16384);		/* init_task */
+  .data.init_task : { *(.data.init_task) }
+
+  . = ALIGN(4096);		/* Init code and data */
+  __init_begin = .;
+  .text.init : { *(.text.init) }
+  .data.init : { *(.data.init) }
+  . = ALIGN(256);
+  __setup_start = .;
+  .setup.init : { *(.setup.init) }
+  __setup_end = .;
+  __initcall_start = .;
+  .initcall.init : {
+	*(.initcall1.init) 
+	*(.initcall2.init) 
+	*(.initcall3.init) 
+	*(.initcall4.init) 
+	*(.initcall5.init) 
+	*(.initcall6.init) 
+	*(.initcall7.init)
+  }
+  __initcall_end = .;
+  . = ALIGN(256);
+  __per_cpu_start = .;
+  .date.percpu  : { *(.data.percpu) }
+  __per_cpu_end = .;
+  . = ALIGN(4096);
+  __init_end = .;
+
+  . = ALIGN(32);
+  .data.cacheline_aligned : { *(.data.cacheline_aligned) }
+
+  . = ALIGN(4096);
+  .data.page_aligned : { *(.data.idt) }
+
+
+  __bss_start = .;		/* BSS */
+  .bss : {
+	*(.bss)
+	}
+  _end = . ;
+
+  /* Stabs debugging sections.  */
+  .stab 0 : { *(.stab) }
+  .stabstr 0 : { *(.stabstr) }
+  .stab.excl 0 : { *(.stab.excl) }
+  .stab.exclstr 0 : { *(.stab.exclstr) }
+  .stab.index 0 : { *(.stab.index) }
+  .stab.indexstr 0 : { *(.stab.indexstr) }
+  .comment 0 : { *(.comment) }
+}
diff -Nru a/arch/sparc/Makefile b/arch/sparc/Makefile
--- a/arch/sparc/Makefile	Mon Oct  7 11:25:26 2002
+++ b/arch/sparc/Makefile	Mon Oct  7 11:25:26 2002
@@ -41,8 +41,7 @@
 libs-y += arch/sparc/prom/ arch/sparc/lib/
 
 # Export what is needed by arch/sparc/boot/Makefile
-export CORE_FILES
-export INIT
+export init-y core-y drivers-y net-y libs-y HEAD
 
 image: vmlinux
 	$(MAKE) -C arch/sparc/boot image
diff -Nru a/arch/sparc/boot/Makefile b/arch/sparc/boot/Makefile
--- a/arch/sparc/boot/Makefile	Mon Oct  7 11:25:38 2002
+++ b/arch/sparc/boot/Makefile	Mon Oct  7 11:25:39 2002
@@ -22,9 +22,8 @@
 clean:
 	rm -f btfixupprep piggyback tftpboot.img btfix.o btfix.s image
 
-BTOBJS := $(HEAD) $(INIT)
-BTLIBS := $(CORE_FILES) $(LIBS) \
-	$(DRIVERS) $(NETWORKS)
+BTOBJS := $(HEAD) $(init-y)
+BTLIBS := $(core-y) $(LIBS) $(drivers-y) $(net-y)
 
 # Actual linking
 image: btfix.o
diff -Nru a/arch/sparc/kernel/signal.c b/arch/sparc/kernel/signal.c
--- a/arch/sparc/kernel/signal.c	Mon Oct  7 11:25:36 2002
+++ b/arch/sparc/kernel/signal.c	Mon Oct  7 11:25:36 2002
@@ -1095,18 +1095,9 @@
 		sigset_t *mask = &current->blocked;
 		unsigned long signr = 0;
 
-		local_irq_disable();
-		if (current->sig->shared_pending.head) {
-			spin_lock(&current->sig->siglock);
-			signr = dequeue_signal(&current->sig->shared_pending, mask, &info);
-			spin_unlock(&current->sig->siglock);
-		}
-		if (!signr) {
-			spin_lock(&current->sig->siglock);
-			signr = dequeue_signal(&current->pending, mask, &info);
-			spin_unlock(&current->sig->siglock);
-		}
-		local_irq_enable();
+		spin_lock_irq(&current->sig->siglock);
+		signr = dequeue_signal(mask, &info);
+		spin_unlock_irq(&current->sig->siglock);
 
 		if (!signr)
 			break;
diff -Nru a/arch/sparc/kernel/sparc_ksyms.c b/arch/sparc/kernel/sparc_ksyms.c
--- a/arch/sparc/kernel/sparc_ksyms.c	Mon Oct  7 11:25:25 2002
+++ b/arch/sparc/kernel/sparc_ksyms.c	Mon Oct  7 11:25:25 2002
@@ -23,6 +23,9 @@
 #include <linux/pci.h>
 #endif
 #include <linux/pm.h>
+#ifdef CONFIG_HIGHMEM
+#include <linux/highmem.h>
+#endif
 
 #include <asm/oplib.h>
 #include <asm/delay.h>
@@ -197,6 +200,12 @@
 /* Actually, ioremap/iounmap are not PCI specific. But it is ok for drivers. */
 EXPORT_SYMBOL(ioremap);
 EXPORT_SYMBOL(iounmap);
+#endif
+
+/* in arch/sparc/mm/highmem.c */
+#ifdef CONFIG_HIGHMEM
+EXPORT_SYMBOL(kmap_atomic);
+EXPORT_SYMBOL(kunmap_atomic);
 #endif
 
 /* Solaris/SunOS binary compatibility */
diff -Nru a/arch/sparc/kernel/sun4d_irq.c b/arch/sparc/kernel/sun4d_irq.c
--- a/arch/sparc/kernel/sun4d_irq.c	Mon Oct  7 11:25:25 2002
+++ b/arch/sparc/kernel/sun4d_irq.c	Mon Oct  7 11:25:25 2002
@@ -547,7 +547,7 @@
 	BTFIXUPSET_CALL(clear_profile_irq, sun4d_clear_profile_irq, BTFIXUPCALL_NORM);
 	BTFIXUPSET_CALL(load_profile_irq, sun4d_load_profile_irq, BTFIXUPCALL_NORM);
 	BTFIXUPSET_CALL(__irq_itoa, sun4d_irq_itoa, BTFIXUPCALL_NORM);
-	init_timers = sun4d_init_timers;
+	sparc_init_timers = sun4d_init_timers;
 #ifdef CONFIG_SMP
 	BTFIXUPSET_CALL(set_cpu_int, sun4d_set_cpu_int, BTFIXUPCALL_NORM);
 	BTFIXUPSET_CALL(clear_cpu_int, sun4d_clear_ipi, BTFIXUPCALL_NOP);
diff -Nru a/arch/sparc/mm/Makefile b/arch/sparc/mm/Makefile
--- a/arch/sparc/mm/Makefile	Mon Oct  7 11:25:36 2002
+++ b/arch/sparc/mm/Makefile	Mon Oct  7 11:25:36 2002
@@ -12,6 +12,10 @@
 obj-y	 += srmmu.o iommu.o io-unit.o hypersparc.o viking.o tsunami.o swift.o
 endif
 
+ifdef CONFIG_HIGHMEM
+obj-y	+= highmem.o
+endif
+
 ifdef CONFIG_SMP
 obj-y   += nosun4c.o
 else
diff -Nru a/arch/sparc/mm/generic.c b/arch/sparc/mm/generic.c
--- a/arch/sparc/mm/generic.c	Mon Oct  7 11:25:38 2002
+++ b/arch/sparc/mm/generic.c	Mon Oct  7 11:25:38 2002
@@ -13,6 +13,8 @@
 #include <asm/pgalloc.h>
 #include <asm/pgtable.h>
 #include <asm/page.h>
+#include <asm/cacheflush.h>
+#include <asm/tlbflush.h>
 
 static inline void forget_pte(pte_t page)
 {
diff -Nru a/arch/sparc/mm/highmem.c b/arch/sparc/mm/highmem.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/arch/sparc/mm/highmem.c	Mon Oct  7 11:25:40 2002
@@ -0,0 +1,99 @@
+/*
+ *  highmem.c: virtual kernel memory mappings for high memory
+ *
+ *  Provides kernel-static versions of atomic kmap functions originally
+ *  found as inlines in include/asm-sparc/highmem.h.  These became
+ *  needed as kmap_atomic() and kunmap_atomic() started getting
+ *  called from within modules.
+ *  -- Tomas Szepe <szepe@pinerecords.com>, September 2002
+ *
+ *  But kmap_atomic() and kunmap_atomic() cannot be inlined in
+ *  modules because they are loaded with btfixup-ped functions.
+ */
+
+/*
+ * The use of kmap_atomic/kunmap_atomic is discouraged - kmap/kunmap
+ * gives a more generic (and caching) interface. But kmap_atomic can
+ * be used in IRQ contexts, so in some (very limited) cases we need it.
+ *
+ * XXX This is an old text. Actually, it's good to use atomic kmaps,
+ * provided you remember that they are atomic and not try to sleep
+ * with a kmap taken, much like a spinlock. Non-atomic kmaps are
+ * shared by CPUs, and so precious, and establishing them requires IPI.
+ * Atomic kmaps are lightweight and we may have NCPUS more of them.
+ */
+#include <linux/mm.h>
+#include <linux/highmem.h>
+#include <asm/pgalloc.h>
+#include <asm/cacheflush.h>
+#include <asm/tlbflush.h>
+
+void *kmap_atomic(struct page *page, enum km_type type)
+{
+	unsigned long idx;
+	unsigned long vaddr;
+
+	inc_preempt_count();
+	if (page < highmem_start_page)
+		return page_address(page);
+
+	idx = type + KM_TYPE_NR*smp_processor_id();
+	vaddr = FIX_KMAP_BEGIN + idx * PAGE_SIZE;
+
+/* XXX Fix - Anton */
+#if 0
+	__flush_cache_one(vaddr);
+#else
+	flush_cache_all();
+#endif
+
+#if HIGHMEM_DEBUG
+	if (!pte_none(*(kmap_pte+idx)))
+		BUG();
+#endif
+	set_pte(kmap_pte+idx, mk_pte(page, kmap_prot));
+/* XXX Fix - Anton */
+#if 0
+	__flush_tlb_one(vaddr);
+#else
+	flush_tlb_all();
+#endif
+
+	return (void*) vaddr;
+}
+
+void kunmap_atomic(void *kvaddr, enum km_type type)
+{
+	unsigned long vaddr = (unsigned long) kvaddr;
+	unsigned long idx = type + KM_TYPE_NR*smp_processor_id();
+
+	if (vaddr < FIX_KMAP_BEGIN) { // FIXME
+		dec_preempt_count();
+		return;
+	}
+
+	if (vaddr != FIX_KMAP_BEGIN + idx * PAGE_SIZE)
+		BUG();
+
+/* XXX Fix - Anton */
+#if 0
+	__flush_cache_one(vaddr);
+#else
+	flush_cache_all();
+#endif
+
+#ifdef HIGHMEM_DEBUG
+	/*
+	 * force other mappings to Oops if they'll try to access
+	 * this pte without first remap it
+	 */
+	pte_clear(kmap_pte+idx);
+/* XXX Fix - Anton */
+#if 0
+	__flush_tlb_one(vaddr);
+#else
+	flush_tlb_all();
+#endif
+#endif
+	dec_preempt_count();
+}
diff -Nru a/arch/sparc/mm/srmmu.c b/arch/sparc/mm/srmmu.c
--- a/arch/sparc/mm/srmmu.c	Mon Oct  7 11:25:35 2002
+++ b/arch/sparc/mm/srmmu.c	Mon Oct  7 11:25:35 2002
@@ -38,6 +38,8 @@
 #include <asm/a.out.h>
 #include <asm/mmu_context.h>
 #include <asm/io-unit.h>
+#include <asm/cacheflush.h>
+#include <asm/tlbflush.h>
 
 /* Now the cpu specific definitions. */
 #include <asm/viking.h>
diff -Nru a/arch/sparc64/defconfig b/arch/sparc64/defconfig
--- a/arch/sparc64/defconfig	Mon Oct  7 11:25:29 2002
+++ b/arch/sparc64/defconfig	Mon Oct  7 11:25:29 2002
@@ -29,7 +29,7 @@
 CONFIG_VT=y
 CONFIG_VT_CONSOLE=y
 CONFIG_HW_CONSOLE=y
-CONFIG_HUGETLB_PAGE=y
+# CONFIG_HUGETLB_PAGE is not set
 CONFIG_SMP=y
 # CONFIG_PREEMPT is not set
 CONFIG_SPARC64=y
@@ -157,7 +157,7 @@
 CONFIG_OBP_FLASH=m
 # CONFIG_SUN_BPP is not set
 # CONFIG_SUN_VIDEOPIX is not set
-CONFIG_SUN_AURORA=m
+# CONFIG_SUN_AURORA is not set
 
 #
 # Memory Technology Devices (MTD)
@@ -335,26 +335,7 @@
 #
 # IEEE 1394 (FireWire) support (EXPERIMENTAL)
 #
-CONFIG_IEEE1394=m
-
-#
-# Device Drivers
-#
-# CONFIG_IEEE1394_PCILYNX is not set
-CONFIG_IEEE1394_OHCI1394=m
-
-#
-# Protocol Drivers
-#
-# CONFIG_IEEE1394_VIDEO1394 is not set
-CONFIG_IEEE1394_SBP2=m
-# CONFIG_IEEE1394_SBP2_PHYS_DMA is not set
-CONFIG_IEEE1394_ETH1394=m
-CONFIG_IEEE1394_DV1394=m
-CONFIG_IEEE1394_RAWIO=m
-CONFIG_IEEE1394_CMP=m
-CONFIG_IEEE1394_AMDTP=m
-# CONFIG_IEEE1394_VERBOSEDEBUG is not set
+# CONFIG_IEEE1394 is not set
 
 #
 # Networking options
@@ -465,6 +446,18 @@
 # CONFIG_LANCE is not set
 # CONFIG_NET_VENDOR_SMC is not set
 # CONFIG_NET_VENDOR_RACAL is not set
+
+#
+# Tulip family network device support
+#
+CONFIG_NET_TULIP=y
+CONFIG_DE2104X=m
+CONFIG_TULIP=m
+# CONFIG_TULIP_MWI is not set
+# CONFIG_TULIP_MMIO is not set
+CONFIG_DE4X5=m
+CONFIG_WINBOND_840=m
+# CONFIG_DM9102 is not set
 # CONFIG_HP100 is not set
 # CONFIG_NET_ISA is not set
 CONFIG_NET_PCI=y
@@ -548,18 +541,6 @@
 # CONFIG_WAN is not set
 
 #
-# Tulip family network device support
-#
-CONFIG_NET_TULIP=y
-CONFIG_DE2104X=m
-CONFIG_TULIP=m
-# CONFIG_TULIP_MWI is not set
-# CONFIG_TULIP_MMIO is not set
-CONFIG_DE4X5=m
-CONFIG_WINBOND_840=m
-# CONFIG_DM9102 is not set
-
-#
 # Amateur Radio support
 #
 CONFIG_HAMRADIO=y
@@ -864,7 +845,7 @@
 #
 CONFIG_SND_ALI5451=m
 CONFIG_SND_CS46XX=m
-CONFIG_SND_CS46XX_ACCEPT_VALID=y
+# CONFIG_SND_CS46XX_NEW_DSP is not set
 CONFIG_SND_CS4281=m
 CONFIG_SND_EMU10K1=m
 CONFIG_SND_KORG1212=m
@@ -886,8 +867,12 @@
 CONFIG_SND_ICE1712=m
 CONFIG_SND_INTEL8X0=m
 CONFIG_SND_SONICVIBES=m
-CONFIG_SND_VIA686=m
-CONFIG_SND_VIA8233=m
+# CONFIG_SND_VIA82XX is not set
+
+#
+# ALSA USB devices
+#
+# CONFIG_SND_USB_AUDIO is not set
 
 #
 # ALSA Sparc devices
@@ -1031,6 +1016,7 @@
 # CONFIG_USB_BRLVGER is not set
 CONFIG_USB_LCD=m
 # CONFIG_USB_SPEEDTOUCH is not set
+CONFIG_USB_TEST=m
 
 #
 # Bluetooth support
diff -Nru a/arch/sparc64/kernel/signal.c b/arch/sparc64/kernel/signal.c
--- a/arch/sparc64/kernel/signal.c	Mon Oct  7 11:25:35 2002
+++ b/arch/sparc64/kernel/signal.c	Mon Oct  7 11:25:35 2002
@@ -645,18 +645,9 @@
 		sigset_t *mask = &current->blocked;
 		unsigned long signr = 0;
 
-		local_irq_disable();
-		if (current->sig->shared_pending.head) {
-			spin_lock(&current->sig->siglock);
-			signr = dequeue_signal(&current->sig->shared_pending, mask, &info);
-			spin_unlock(&current->sig->siglock);
-		}
-		if (!signr) {
-			spin_lock(&current->sig->siglock);
-			signr = dequeue_signal(&current->pending, mask, &info);
-			spin_unlock(&current->sig->siglock);
-		}
-		local_irq_enable();
+		spin_lock_irq(&current->sig->siglock);
+		signr = dequeue_signal(mask, &info);
+		spin_unlock_irq(&current->sig->siglock);
 		
 		if (!signr)
 			break;
diff -Nru a/arch/sparc64/kernel/signal32.c b/arch/sparc64/kernel/signal32.c
--- a/arch/sparc64/kernel/signal32.c	Mon Oct  7 11:25:25 2002
+++ b/arch/sparc64/kernel/signal32.c	Mon Oct  7 11:25:25 2002
@@ -1284,18 +1284,9 @@
 		sigset_t *mask = &current->blocked;
 		unsigned long signr = 0;
 
-		local_irq_disable();
-		if (current->sig->shared_pending.head) {
-			spin_lock(&current->sig->siglock);
-			signr = dequeue_signal(&current->sig->shared_pending, mask, &info);
-			spin_unlock(&current->sig->siglock);
-		}
-		if (!signr) {
-			spin_lock(&current->sig->siglock);
-			signr = dequeue_signal(&current->pending, mask, &info);
-			spin_unlock(&current->sig->siglock);
-		}
-		local_irq_enable();
+		spin_lock_irq(&current->sig->siglock);
+		signr = dequeue_signal(mask, &info);
+		spin_unlock_irq(&current->sig->siglock);
 		
 		if (!signr)
 			break;
diff -Nru a/arch/sparc64/kernel/sys_sparc32.c b/arch/sparc64/kernel/sys_sparc32.c
--- a/arch/sparc64/kernel/sys_sparc32.c	Mon Oct  7 11:25:36 2002
+++ b/arch/sparc64/kernel/sys_sparc32.c	Mon Oct  7 11:25:36 2002
@@ -1972,10 +1972,7 @@
 	}
 
 	spin_lock_irq(&current->sig->siglock);
-	spin_lock(&current->sig->siglock);
-	sig = dequeue_signal(&current->sig->shared_pending, &these, &info);
-	if (!sig)
-		sig = dequeue_signal(&current->pending, &these, &info);
+	sig = dequeue_signal(&these, &info);
 	if (!sig) {
 		timeout = MAX_SCHEDULE_TIMEOUT;
 		if (uts)
@@ -1989,23 +1986,18 @@
 			current->real_blocked = current->blocked;
 			sigandsets(&current->blocked, &current->blocked, &these);
 			recalc_sigpending();
-			spin_unlock(&current->sig->siglock);
 			spin_unlock_irq(&current->sig->siglock);
 
 			current->state = TASK_INTERRUPTIBLE;
 			timeout = schedule_timeout(timeout);
 
 			spin_lock_irq(&current->sig->siglock);
-			spin_lock(&current->sig->siglock);
-			sig = dequeue_signal(&current->sig->shared_pending, &these, &info);
-			if (!sig)
-				sig = dequeue_signal(&current->pending, &these, &info);
+			sig = dequeue_signal(&these, &info);
 			current->blocked = current->real_blocked;
 			siginitset(&current->real_blocked, 0);
 			recalc_sigpending();
 		}
 	}
-	spin_unlock(&current->sig->siglock);
 	spin_unlock_irq(&current->sig->siglock);
 
 	if (sig) {
diff -Nru a/arch/sparc64/mm/init.c b/arch/sparc64/mm/init.c
--- a/arch/sparc64/mm/init.c	Mon Oct  7 11:25:32 2002
+++ b/arch/sparc64/mm/init.c	Mon Oct  7 11:25:32 2002
@@ -1366,6 +1366,8 @@
 	}
 #endif	
 	/* Initialize the boot-time allocator. */
+	max_pfn = max_low_pfn = end_pfn;
+	min_low_pfn = pfn_base;
 	bootmap_size = init_bootmem_node(NODE_DATA(0), bootmap_pfn, pfn_base, end_pfn);
 
 	/* Now register the available physical memory with the
diff -Nru a/arch/um/drivers/ubd_kern.c b/arch/um/drivers/ubd_kern.c
--- a/arch/um/drivers/ubd_kern.c	Mon Oct  7 11:25:35 2002
+++ b/arch/um/drivers/ubd_kern.c	Mon Oct  7 11:25:35 2002
@@ -65,8 +65,8 @@
 static request_queue_t *ubd_queue;
 
 static int fake_major = 0;
-static struct gendisk ubd_gendisk[MAX_DEV];
-static struct gendisk fake_gendisk[MAX_DEV];
+static struct gendisk *ubd_gendisk[MAX_DEV];
+static struct gendisk *fake_gendisk[MAX_DEV];
  
 #ifdef CONFIG_BLK_DEV_UBD_SYNC
 #define OPEN_FLAGS ((struct openflags) { r : 1, w : 1, s : 1, c : 0 })
@@ -398,31 +398,43 @@
  	devfs_handle_t real, fake;
 	char name[sizeof("nnnnnn\0")];
 	struct ubd *dev = &ubd_dev[n];
+	struct gendisk *disk, *fake_disk;
 	u64 size;
 
 	if (!dev->file)
 		return -1;
 
-	ubd_gendisk[n].major = MAJOR_NR;
-	ubd_gendisk[n].first_minor = n << UBD_SHIFT;
-	ubd_gendisk[n].minor_shift = UBD_SHIFT;
-	ubd_gendisk[n].fops = &ubd_fops;
+	disk = alloc_disk();
+	if (!disk)
+		return -1;
+	disk->major = MAJOR_NR;
+	disk->first_minor = n << UBD_SHIFT;
+	disk->minor_shift = UBD_SHIFT;
+	disk->fops = &ubd_fops;
 	if (fakehd_set)
-		sprintf(ubd_gendisk[n].disk_name, "hd%c", n + 'a');
+		sprintf(disk->disk_name, "hd%c", n + 'a');
 	else
-		sprintf(ubd_gendisk[n].disk_name, "ubd%d", n);
+		sprintf(disk->disk_name, "ubd%d", n);
 
 	if (fake_major) {
-		fake_gendisk[n].major = fake_major;
-		fake_gendisk[n].first_minor = n << UBD_SHIFT;
-		fake_gendisk[n].minor_shift = UBD_SHIFT;
-		fake_gendisk[n].fops = &ubd_fops;
-		sprintf(fake_gendisk[n].disk_name, "ubd%d", n);
+		fake_disk = alloc_disk();
+		if (!fake_disk) {
+			put_disk(disk);
+			return -1;
+		}
+		fake_disk->major = fake_major;
+		fake_disk->first_minor = n << UBD_SHIFT;
+		fake_disk->minor_shift = UBD_SHIFT;
+		fake_disk->fops = &ubd_fops;
+		sprintf(fake_disk->disk_name, "ubd%d", n);
+		fake_gendisk[n] = fake_disk;
 	}
 
+	ubd_gendisk[n] = disk;
+
 	if (!dev->is_dir && ubd_file_size(dev, &size) == 0) {
-		set_capacity(&ubd_gendisk[n], size/512);
-		set_capacity(&fake_gendisk[n], size/512);
+		set_capacity(disk, size/512);
+		set_capacity(fake_disk, size/512);
 	}
  
 	sprintf(name, "%d", n);
@@ -430,14 +442,14 @@
 			      MAJOR_NR, n << UBD_SHIFT,
 			      S_IFBLK | S_IRUSR | S_IWUSR | S_IRGRP |S_IWGRP,
 			      &ubd_blops, NULL);
-	add_disk(&ubd_gendisk[n]);
+	add_disk(disk);
 	if (fake_major) {
 		fake = devfs_register(ubd_fake_dir_handle, name, 
 				      DEVFS_FL_REMOVABLE, fake_major,
 				      n << UBD_SHIFT, 
 				      S_IFBLK | S_IRUSR | S_IWUSR | S_IRGRP |
 				      S_IWGRP, &ubd_blops, NULL);
-		add_disk(&fake_gendisk[n]);
+		add_disk(fake_disk);
  		if(fake == NULL) return(-1);
  		ubd_dev[n].fake = fake;
 	}
@@ -445,7 +457,7 @@
  	if(real == NULL) return(-1);
  	ubd_dev[n].real = real;
  
-	make_ide_entries(ubd_gendisk[n].name);
+	make_ide_entries(disk->disk_name);
 	return(0);
 }
 
@@ -483,9 +495,14 @@
 	n = *str - '0';
 	if(n > MAX_DEV) return(-1);
 	dev = &ubd_dev[n];
-	del_gendisk(&ubd_gendisk[n]);
-	if (fake_major)
-		del_gendisk(&fake_gendisk[n]);
+	del_gendisk(ubd_gendisk[n]);
+	put_disk(ubd_gendisk[n]);
+	ubd_gendisk[n] = NULL;
+	if (fake_major) {
+		del_gendisk(fake_gendisk[n]);
+		put_disk(fake_gendisk[n]);
+		fake_gendisk[n] = NULL;
+	}
 	if(dev->file == NULL) return(0);
 	if(dev->count > 0) return(-1);
 	if(dev->real != NULL) devfs_unregister(dev->real);
@@ -878,9 +895,9 @@
 	
 	err = ubd_file_size(dev, &size);
 	if (!err) {
-		set_capacity(&ubd_gendisk[n], size / 512);
+		set_capacity(ubd_gendisk[n], size / 512);
 		if(fake_major != 0)
-			set_capacity(&fake_gendisk[n], size / 512);
+			set_capacity(fake_gendisk[n], size / 512);
 		dev->size = size;
 	}
 
diff -Nru a/drivers/acorn/block/fd1772.c b/drivers/acorn/block/fd1772.c
--- a/drivers/acorn/block/fd1772.c	Mon Oct  7 11:25:29 2002
+++ b/drivers/acorn/block/fd1772.c	Mon Oct  7 11:25:29 2002
@@ -131,7 +131,7 @@
 #include <linux/kernel.h>
 #include <linux/interrupt.h>
 #include <linux/timer.h>
-#include <linux/tqueue.h>
+#include <linux/workqueue.h>
 #include <linux/fd.h>
 #include <linux/fd1772.h>
 #include <linux/errno.h>
@@ -203,7 +203,7 @@
  */
 #define MAX_DISK_SIZE 720
 
-static struct gendisk disks[FD_MAX_UNIT];
+static struct gendisk *disks[FD_MAX_UNIT];
 
 /* current info on each unit */
 static struct archy_floppy_struct {
@@ -324,8 +324,7 @@
 
 static void fd1772_checkint(void);
 
-struct tq_struct fd1772_tq = 
-{ 0,0, (void *)fd1772_checkint, 0 };
+DECLARE_WORK(fd1772_tq, (void *)fd1772_checkint, NULL);
 /*
  * The driver is trying to determine the correct media format
  * while Probing is set. fd_rwsec_done() clears it after a
@@ -962,7 +961,7 @@
 			if (unit[SelectedDrive].disktype > disk_type) {
 				/* try another disk type */
 				unit[SelectedDrive].disktype--;
-				set_capacity(&disks[SelectedDrive],
+				set_capacity(disks[SelectedDrive],
 				    unit[SelectedDrive].disktype->blocks);
 			} else
 				Probing = 0;
@@ -970,7 +969,7 @@
 			/* record not found, but not probing. Maybe stretch wrong ? Restart probing */
 			if (unit[SelectedDrive].autoprobe) {
 				unit[SelectedDrive].disktype = disk_type + NUM_DISK_TYPES - 1;
-				set_capacity(&disks[SelectedDrive],
+				set_capacity(disks[SelectedDrive],
 				    unit[SelectedDrive].disktype->blocks);
 				Probing = 1;
 			}
@@ -1243,7 +1242,7 @@
 		if (!floppy->disktype) {
 			Probing = 1;
 			floppy->disktype = disk_type + NUM_DISK_TYPES - 1;
-			set_capacity(&disks[drive], floppy->disktype->blocks);
+			set_capacity(disks[drive], floppy->disktype->blocks);
 			floppy->autoprobe = 1;
 		}
 	} else {
@@ -1255,7 +1254,7 @@
 			goto repeat;
 		}
 		floppy->disktype = &disk_type[type];
-		set_capacity(&disks[drive], floppy->disktype->blocks);
+		set_capacity(disks[drive], floppy->disktype->blocks);
 		floppy->autoprobe = 0;
 	}
 
@@ -1288,8 +1287,7 @@
 		floppy_irqconsequencehandler();
 	if ((MultReadInProgress) && (fdc1772_bytestogo==0)) fd_readtrack_check(0);
 	if (fdc_busy) {
-		queue_task(&fd1772_tq,&tq_immediate);
-		mark_bh(IMMEDIATE_BH);
+		schedule_work(&fd1772_tq);
 	}
 }
 
@@ -1311,8 +1309,7 @@
 
 	redo_fd_request();
 
-	queue_task(&fd1772_tq,&tq_immediate);
-	mark_bh(IMMEDIATE_BH);
+	schedule_work(&fd1772_tq);
 }
 
 
@@ -1543,7 +1540,7 @@
 	int drive = minor & 3;
 	if ((minor>> 2) > NUM_DISK_TYPES || drive >= FD_MAX_UNITS)
 		return NULL;
-	return &disks[drive];
+	return disks[drive];
 }
 
 int fd1772_init(void)
@@ -1553,20 +1550,28 @@
 	if (!machine_is_archimedes())
 		return 0;
 
+	for (i = 0; i < FD_MAX_UNITS; i++) {
+		disks[i] = alloc_disk();
+		if (!disks[i])
+			goto out;
+	}
+
 	if (register_blkdev(MAJOR_NR, "fd", &floppy_fops)) {
 		printk("Unable to get major %d for floppy\n", MAJOR_NR);
-		return 1;
+		goto out;
 	}
 
 	if (request_dma(FLOPPY_DMA, "fd1772")) {
 		printk("Unable to grab DMA%d for the floppy (1772) driver\n", FLOPPY_DMA);
-		return 1;
+		unregister_blkdev(MAJOR_NR, "fd");
+		goto out;
 	};
 
 	if (request_dma(FIQ_FD1772, "fd1772 end")) {
 		printk("Unable to grab DMA%d for the floppy (1772) driver\n", FIQ_FD1772);
+		unregister_blkdev(MAJOR_NR, "fd");
 		free_dma(FLOPPY_DMA);
-		return 1;
+		goto out;
 	};
 	enable_dma(FIQ_FD1772);	/* This inserts a call to our command end routine */
 
@@ -1585,18 +1590,22 @@
 	blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), do_fd_request);
 	for (i = 0; i < FD_MAX_UNITS; i++) {
 		unit[i].track = -1;
-		disks[i].major = MAJOR_NR;
-		disks[i].first_minor = 0;
-		disks[i].fops = &floppy_fops;
-		sprintf(disks[i].disk_name, "fd%d", i);
-		set_capacity(&disks[i], MAX_DISK_SIZE * 2);
+		disks[i]->major = MAJOR_NR;
+		disks[i]->first_minor = 0;
+		disks[i]->fops = &floppy_fops;
+		sprintf(disks[i]->disk_name, "fd%d", i);
+		set_capacity(disks[i], MAX_DISK_SIZE * 2);
 	}
 	blk_set_probe(MAJOR_NR, floppy_find);
 
 	for (i = 0; i < FD_MAX_UNITS; i++)
-		add_disk(disks + i);
+		add_disk(disks[i]);
 
 	config_types();
 
 	return 0;
+out:
+	while (i--)
+		put_disk(disks[i]);
+	return 1;
 }
diff -Nru a/drivers/acorn/block/mfmhd.c b/drivers/acorn/block/mfmhd.c
--- a/drivers/acorn/block/mfmhd.c	Mon Oct  7 11:25:26 2002
+++ b/drivers/acorn/block/mfmhd.c	Mon Oct  7 11:25:26 2002
@@ -880,19 +880,7 @@
 	mfm_request();
 }
 
-static struct gendisk mfm_gendisk[2] = {
-{
-	.major		= MAJOR_NR,
-	.first_minor	= 0,
-	.disk_name	= "mfma",
-	.minor_shift	= 6,
-},
-{
-	.major		= MAJOR_NR,
-	.first_minor	= 64,
-	.disk_name	= "mfmb",
-	.minor_shift	= 6,
-};
+static struct gendisk *mfm_gendisk[2];
 
 static void mfm_request(void)
 {
@@ -908,6 +896,7 @@
 
 	while (1) {
 		unsigned int dev, block, nsect, unit;
+		struct gendisk *disk;
 
 		DBG("mfm_request: loop start\n");
 		sti();
@@ -930,14 +919,13 @@
 #ifdef DEBUG
 		/*if (unit==1) */ console_printf("mfm_request:                                raw vals: dev=%d (block=512 bytes) block=%d nblocks=%d\n", dev, block, nsect);
 #endif
-		if (unit >= mfm_drives ||
-		    block >= get_capacity(mfm_gendisk + unit) ||
-		    ((block+nsect) > get_capacity(mfm_gendisk + unit))) {
-			if (unit >= mfm_drives)
-				printk("mfm: bad minor number: device=%s\n", kdevname(CURRENT->rq_dev));
-			else
-				printk("mfm%c: bad access: block=%d, count=%d, nr_sects=%ld\n", unit+'a',
-				       block, nsect, get_capacity(mfm_gendisk+unit));
+		if (unit >= mfm_drives)
+			printk("mfm: bad disk number: %d\n", unit);
+		disk = mfm_gendisk[unit];
+		if (block >= get_capacity(disk) ||
+		    block+nsect > get_capacity(disk)) {
+			printk("%s: bad access: block=%d, count=%d, nr_sects=%ld\n",
+			       disk->disk_name, block, nsect, get_capacity(disk));
 			printk("mfm: continue 1\n");
 			end_request(CURRENT, 0);
 			Busy = 0;
@@ -1017,7 +1005,7 @@
 static void mfm_geometry(int drive)
 {
 	struct mfm_info *p = mfm_info + drive;
-	struct gendisk *disk = mfm_gendisk + drive;
+	struct gendisk *disk = mfm_gendisk[drive];
 	if (p->cylinders)
 		printk ("%s: %dMB CHS=%d/%d/%d LCC=%d RECOMP=%d\n",
 			disk->disk_name,
@@ -1255,26 +1243,6 @@
 	.ioctl		= mfm_ioctl,
 };
 
-static void mfm_geninit (void)
-{
-	int i;
-
-	mfm_drives = mfm_initdrives();
-
-	printk("mfm: detected %d hard drive%s\n", mfm_drives,
-				mfm_drives == 1 ? "" : "s");
-	if (request_irq(mfm_irq, mfm_interrupt_handler, SA_INTERRUPT, "MFM harddisk", NULL))
-		printk("mfm: unable to get IRQ%d\n", mfm_irq);
-
-	if (mfm_irqenable)
-		outw(0x80, mfm_irqenable);	/* Required to enable IRQs from MFM podule */
-
-	for (i = 0; i < mfm_drives; i++) {
-		mfm_geometry(i);
-		add_disk(mfm_gendisk + i);
-	}
-}
-
 static struct expansion_card *ecs;
 
 /*
@@ -1317,9 +1285,10 @@
  *
  * The HDC is accessed at MEDIUM IOC speeds.
  */
-int mfm_init (void)
+static int __init mfm_init (void)
 {
 	unsigned char irqmask;
+	int i;
 
 	if (mfm_probecontroller(ONBOARD_MFM_ADDRESS)) {
 		mfm_addr	= ONBOARD_MFM_ADDRESS;
@@ -1344,16 +1313,12 @@
 	}
 
 	printk("mfm: found at address %08X, interrupt %d\n", mfm_addr, mfm_irq);
-	if (!request_region (mfm_addr, 10, "mfm")) {
-		ecard_release(ecs);
-		return -1;
-	}
+	if (!request_region (mfm_addr, 10, "mfm"))
+		goto out1;
 
 	if (register_blkdev(MAJOR_NR, "mfm", &mfm_fops)) {
 		printk("mfm_init: unable to get major number %d\n", MAJOR_NR);
-		ecard_release(ecs);
-		release_region(mfm_addr, 10);
-		return -1;
+		goto out2;
 	}
 
 	/* Stuff for the assembler routines to get to */
@@ -1366,31 +1331,73 @@
 	Busy = 0;
 	lastspecifieddrive = -1;
 
-	mfm_geninit();
-	return 0;
-}
+	mfm_drives = mfm_initdrives();
+	if (!mfm_drives)
+		goto out3;
+	
+	for (i = 0; i < mfm_drives; i++) {
+		struct gendisk *disk = alloc_disk();
+		if (!disk)
+			goto Enomem;
+		disk->major = MAJOR_NR;
+		disk->first_minor = i << 6;
+		disk->minor_shift = 6;
+		disk->fops = &mfm_fops;
+		sprintf(disk->disk_name, "mfm%c", 'a'+i);
+		mfm_gendisk[i] = disk;
+	}
 
-#ifdef MODULE
+	printk("mfm: detected %d hard drive%s\n", mfm_drives,
+				mfm_drives == 1 ? "" : "s");
+	if (request_irq(mfm_irq, mfm_interrupt_handler, SA_INTERRUPT, "MFM harddisk", NULL)) {
+		printk("mfm: unable to get IRQ%d\n", mfm_irq);
+		goto out4;
+	}
 
-MODULE_LICENSE("GPL");
+	if (mfm_irqenable)
+		outw(0x80, mfm_irqenable);	/* Required to enable IRQs from MFM podule */
 
-int init_module(void)
-{
-	return mfm_init();
+	for (i = 0; i < mfm_drives; i++) {
+		mfm_geometry(i);
+		add_disk(mfm_gendisk[i]);
+	}
+	return 0;
+
+out4:
+	for (i = 0; i < mfm_drives; i++)
+		put_disk(mfm_gendisk[i]);
+out3:
+	blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR));
+	unregister_blkdev(MAJOR_NR, "mfm");
+out2:
+	release_region(mfm_addr, 10);
+out1:
+	ecard_release(ecs);
+	return -1;
+Enomem:
+	while (i--)
+		put_disk(mfm_gendisk[i]);
+	goto out3;
 }
 
-void cleanup_module(void)
+static void __exit mfm_exit(void)
 {
 	int i;
 	if (ecs && mfm_irqenable)
 		outw (0, mfm_irqenable);	/* Required to enable IRQs from MFM podule */
 	free_irq(mfm_irq, NULL);
+	for (i = 0; i < mfm_drives; i++) {
+		del_gendisk(mfm_gendisk[i]);
+		put_disk(mfm_gendisk[i]);
+	}
+	blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR));
 	unregister_blkdev(MAJOR_NR, "mfm");
-	for (i = 0; i < mfm_drives; i++)
-		del_gendisk(mfm_gendisk + i);
 	if (ecs)
 		ecard_release(ecs);
 	if (mfm_addr)
 		release_region(mfm_addr, 10);
 }
-#endif
+
+module_init(mfm_init)
+module_exit(mfm_exit)
+MODULE_LICENSE("GPL");
diff -Nru a/drivers/acpi/Makefile b/drivers/acpi/Makefile
--- a/drivers/acpi/Makefile	Mon Oct  7 11:25:39 2002
+++ b/drivers/acpi/Makefile	Mon Oct  7 11:25:39 2002
@@ -4,7 +4,7 @@
 
 export ACPI_CFLAGS
 
-ACPI_CFLAGS	:= -D_LINUX -I$(CURDIR)/include
+ACPI_CFLAGS	:= -D_LINUX -Idrivers/acpi/include
 
 ifdef CONFIG_ACPI_DEBUG
   ACPI_CFLAGS	+= -DACPI_DEBUG_OUTPUT
diff -Nru a/drivers/acpi/dispatcher/Makefile b/drivers/acpi/dispatcher/Makefile
--- a/drivers/acpi/dispatcher/Makefile	Mon Oct  7 11:25:26 2002
+++ b/drivers/acpi/dispatcher/Makefile	Mon Oct  7 11:25:26 2002
@@ -2,7 +2,8 @@
 # Makefile for all Linux ACPI interpreter subdirectories
 #
 
-obj-$(CONFIG_ACPI_INTERPRETER) := $(patsubst %.c,%.o,$(wildcard *.c))
+obj-y := dsfield.o   dsmthdat.o  dsopcode.o  dswexec.o  dswscope.o \
+	 dsmethod.o  dsobject.o  dsutils.o   dswload.o  dswstate.o
 
 EXTRA_CFLAGS += $(ACPI_CFLAGS)
 
diff -Nru a/drivers/acpi/events/Makefile b/drivers/acpi/events/Makefile
--- a/drivers/acpi/events/Makefile	Mon Oct  7 11:25:25 2002
+++ b/drivers/acpi/events/Makefile	Mon Oct  7 11:25:25 2002
@@ -2,7 +2,8 @@
 # Makefile for all Linux ACPI interpreter subdirectories
 #
 
-obj-$(CONFIG_ACPI_INTERPRETER) := $(patsubst %.c,%.o,$(wildcard *.c))
+obj-y := evevent.o  evregion.o  evsci.o    evxfevnt.o \
+	 evmisc.o   evrgnini.o  evxface.o  evxfregn.o
 
 EXTRA_CFLAGS += $(ACPI_CFLAGS)
 
diff -Nru a/drivers/acpi/executer/Makefile b/drivers/acpi/executer/Makefile
--- a/drivers/acpi/executer/Makefile	Mon Oct  7 11:25:26 2002
+++ b/drivers/acpi/executer/Makefile	Mon Oct  7 11:25:26 2002
@@ -2,7 +2,10 @@
 # Makefile for all Linux ACPI interpreter subdirectories
 #
 
-obj-$(CONFIG_ACPI_INTERPRETER) := $(patsubst %.c,%.o,$(wildcard *.c))
+obj-y := exconfig.o  exfield.o  exnames.o   exoparg6.o  exresolv.o  exstorob.o\
+	 exconvrt.o  exfldio.o  exoparg1.o  exprep.o    exresop.o   exsystem.o\
+	 excreate.o  exmisc.o   exoparg2.o  exregion.o  exstore.o   exutils.o \
+	 exdump.o    exmutex.o  exoparg3.o  exresnte.o  exstoren.o
 
 EXTRA_CFLAGS += $(ACPI_CFLAGS)
 
diff -Nru a/drivers/acpi/hardware/Makefile b/drivers/acpi/hardware/Makefile
--- a/drivers/acpi/hardware/Makefile	Mon Oct  7 11:25:39 2002
+++ b/drivers/acpi/hardware/Makefile	Mon Oct  7 11:25:39 2002
@@ -2,7 +2,7 @@
 # Makefile for all Linux ACPI interpreter subdirectories
 #
 
-obj-$(CONFIG_ACPI_INTERPRETER) := $(patsubst %.c,%.o,$(wildcard *.c))
+obj-y := hwacpi.o  hwgpe.o  hwregs.o  hwsleep.o  hwtimer.o
 
 EXTRA_CFLAGS += $(ACPI_CFLAGS)
 
diff -Nru a/drivers/acpi/namespace/Makefile b/drivers/acpi/namespace/Makefile
--- a/drivers/acpi/namespace/Makefile	Mon Oct  7 11:25:26 2002
+++ b/drivers/acpi/namespace/Makefile	Mon Oct  7 11:25:26 2002
@@ -2,7 +2,9 @@
 # Makefile for all Linux ACPI interpreter subdirectories
 #
 
-obj-$(CONFIG_ACPI_INTERPRETER) := $(patsubst %.c,%.o,$(wildcard *.c))
+obj-y := nsaccess.o  nsdumpdv.o  nsload.o    nssearch.o  nsxfeval.o \
+	 nsalloc.o   nseval.o    nsnames.o   nsutils.o   nsxfname.o \
+	 nsdump.o    nsinit.o    nsobject.o  nswalk.o    nsxfobj.o
 
 EXTRA_CFLAGS += $(ACPI_CFLAGS)
 
diff -Nru a/drivers/acpi/osl.c b/drivers/acpi/osl.c
--- a/drivers/acpi/osl.c	Mon Oct  7 11:25:37 2002
+++ b/drivers/acpi/osl.c	Mon Oct  7 11:25:37 2002
@@ -33,7 +33,7 @@
 #include <linux/interrupt.h>
 #include <linux/kmod.h>
 #include <linux/delay.h>
-#include <linux/tqueue.h>
+#include <linux/workqueue.h>
 #include <asm/io.h>
 #include "acpi.h"
 
@@ -586,8 +586,8 @@
 	 * Queue via DPC:
 	 * --------------
 	 * Note that we have to use two different processes for queuing DPCs:
-	 *	 Interrupt-Level: Use schedule_task; can't spawn a new thread.
-	 *	    Kernel-Level: Spawn a new kernel thread, as schedule_task has
+	 *	 Interrupt-Level: Use schedule_work; can't spawn a new thread.
+	 *	    Kernel-Level: Spawn a new kernel thread, as schedule_work has
 	 *			  its limitations (e.g. single-threaded model), and
 	 *			  all other task queues run at interrupt-level.
 	 */
@@ -605,9 +605,9 @@
 		 * We can save time and code by allocating the DPC and tq_structs
 		 * from the same memory.
 		 */
-		struct tq_struct *task;
+		struct work_struct *task;
 
-		dpc = kmalloc(sizeof(ACPI_OS_DPC)+sizeof(struct tq_struct), GFP_ATOMIC);
+		dpc = kmalloc(sizeof(ACPI_OS_DPC)+sizeof(struct work_struct), GFP_ATOMIC);
 		if (!dpc)
 			return_ACPI_STATUS (AE_NO_MEMORY);
 
@@ -615,10 +615,10 @@
 		dpc->context = context;
 
 		task = (void *)(dpc+1);
-		INIT_TQUEUE(task, acpi_os_schedule_exec, (void*)dpc);
+		INIT_WORK(task, acpi_os_schedule_exec, (void*)dpc);
 
-		if (schedule_task(task) < 0) {
-			ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Call to schedule_task() failed.\n"));
+		if (schedule_work(task) < 0) {
+			ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Call to schedule_work() failed.\n"));
 			kfree(dpc);
 			status = AE_ERROR;
 		}
diff -Nru a/drivers/acpi/parser/Makefile b/drivers/acpi/parser/Makefile
--- a/drivers/acpi/parser/Makefile	Mon Oct  7 11:25:35 2002
+++ b/drivers/acpi/parser/Makefile	Mon Oct  7 11:25:35 2002
@@ -2,7 +2,8 @@
 # Makefile for all Linux ACPI interpreter subdirectories
 #
 
-obj-$(CONFIG_ACPI_INTERPRETER) := $(patsubst %.c,%.o,$(wildcard *.c))
+obj-y := psargs.o    psparse.o  pstree.o   pswalk.o  \
+	 psopcode.o  psscope.o  psutils.o  psxface.o
 
 EXTRA_CFLAGS += $(ACPI_CFLAGS)
 
diff -Nru a/drivers/acpi/resources/Makefile b/drivers/acpi/resources/Makefile
--- a/drivers/acpi/resources/Makefile	Mon Oct  7 11:25:26 2002
+++ b/drivers/acpi/resources/Makefile	Mon Oct  7 11:25:26 2002
@@ -2,7 +2,8 @@
 # Makefile for all Linux ACPI interpreter subdirectories
 #
 
-obj-$(CONFIG_ACPI_INTERPRETER) := $(patsubst %.c,%.o,$(wildcard *.c))
+obj-y := rsaddr.o  rscreate.o  rsio.o   rslist.o    rsmisc.o   rsxface.o \
+	 rscalc.o  rsdump.o    rsirq.o  rsmemory.o  rsutils.o
 
 EXTRA_CFLAGS += $(ACPI_CFLAGS)
 
diff -Nru a/drivers/acpi/tables/Makefile b/drivers/acpi/tables/Makefile
--- a/drivers/acpi/tables/Makefile	Mon Oct  7 11:25:25 2002
+++ b/drivers/acpi/tables/Makefile	Mon Oct  7 11:25:25 2002
@@ -2,7 +2,8 @@
 # Makefile for all Linux ACPI interpreter subdirectories
 #
 
-obj-$(CONFIG_ACPI_INTERPRETER) := $(patsubst %.c,%.o,$(wildcard *.c))
+obj-y := tbconvrt.o  tbget.o     tbrsdt.o   tbxface.o  \
+	 tbgetall.o  tbinstal.o  tbutils.o  tbxfroot.o
 
 EXTRA_CFLAGS += $(ACPI_CFLAGS)
 
diff -Nru a/drivers/acpi/utilities/Makefile b/drivers/acpi/utilities/Makefile
--- a/drivers/acpi/utilities/Makefile	Mon Oct  7 11:25:30 2002
+++ b/drivers/acpi/utilities/Makefile	Mon Oct  7 11:25:30 2002
@@ -2,7 +2,8 @@
 # Makefile for all Linux ACPI interpreter subdirectories
 #
 
-obj-$(CONFIG_ACPI_INTERPRETER) := $(patsubst %.c,%.o,$(wildcard *.c))
+obj-y := utalloc.o  utdebug.o   uteval.o    utinit.o  utmisc.o    utxface.o \
+	 utcopy.o   utdelete.o  utglobal.o  utmath.o  utobject.o
 
 EXTRA_CFLAGS += $(ACPI_CFLAGS)
 
diff -Nru a/drivers/atm/ambassador.c b/drivers/atm/ambassador.c
--- a/drivers/atm/ambassador.c	Mon Oct  7 11:25:25 2002
+++ b/drivers/atm/ambassador.c	Mon Oct  7 11:25:25 2002
@@ -926,8 +926,7 @@
   
     if (irq_work) {
 #ifdef FILL_RX_POOLS_IN_BH
-      queue_task (&dev->bh, &tq_immediate);
-      mark_bh (IMMEDIATE_BH);
+      schedule_work (&dev->bh);
 #else
       fill_rx_pools (dev);
 #endif
@@ -2402,10 +2401,7 @@
       
 #ifdef FILL_RX_POOLS_IN_BH
       // initialise bottom half
-      INIT_LIST_HEAD(&dev->bh.list);
-      dev->bh.sync = 0;
-      dev->bh.routine = (void (*)(void *)) fill_rx_pools;
-      dev->bh.data = dev;
+      INIT_WORK(&dev->bh, (void (*)(void *)) fill_rx_pools, dev);
 #endif
       
       // semaphore for txer/rxer modifications - we cannot use a
diff -Nru a/drivers/atm/ambassador.h b/drivers/atm/ambassador.h
--- a/drivers/atm/ambassador.h	Mon Oct  7 11:25:39 2002
+++ b/drivers/atm/ambassador.h	Mon Oct  7 11:25:39 2002
@@ -632,7 +632,7 @@
   u32 *            membase;
 
 #ifdef FILL_RX_POOLS_IN_BH
-  struct tq_struct bh;
+  struct work_struct bh;
 #endif
   
   amb_cq           cq;
diff -Nru a/drivers/atm/idt77252.c b/drivers/atm/idt77252.c
--- a/drivers/atm/idt77252.c	Mon Oct  7 11:25:35 2002
+++ b/drivers/atm/idt77252.c	Mon Oct  7 11:25:35 2002
@@ -2896,8 +2896,7 @@
 		if (stat & SAR_STAT_FBQ3A)
 			card->irqstat[8]++;
 
-		queue_task(&card->tqueue, &tq_immediate);
-		mark_bh(IMMEDIATE_BH);
+		schedule_work(&card->tqueue);
 	}
 
 out:
@@ -3737,8 +3736,7 @@
 	card->pcidev = pcidev;
 	sprintf(card->name, "idt77252-%d", card->index);
 
-	card->tqueue.routine = idt77252_softint;
-	card->tqueue.data = (void *)card;
+	INIT_WORK(&card->tqueue, idt77252_softint, (void *)card);
 
 	membase = pci_resource_start(pcidev, 1);
 	srambase = pci_resource_start(pcidev, 2);
diff -Nru a/drivers/atm/idt77252.h b/drivers/atm/idt77252.h
--- a/drivers/atm/idt77252.h	Mon Oct  7 11:25:27 2002
+++ b/drivers/atm/idt77252.h	Mon Oct  7 11:25:27 2002
@@ -36,7 +36,7 @@
 
 #include <linux/ptrace.h>
 #include <linux/skbuff.h>
-#include <linux/tqueue.h>
+#include <linux/workqueue.h>
 
 
 /*****************************************************************************/
@@ -365,7 +365,7 @@
 	unsigned long		softstat;
 	unsigned long		flags;		/* see blow */
 
-	struct tq_struct	tqueue;
+	struct work_struct	tqueue;
 
 	unsigned long		tct_base;	/* TCT base address in SRAM */
         unsigned long		rct_base;	/* RCT base address in SRAM */
diff -Nru a/drivers/base/hotplug.c b/drivers/base/hotplug.c
--- a/drivers/base/hotplug.c	Mon Oct  7 11:25:25 2002
+++ b/drivers/base/hotplug.c	Mon Oct  7 11:25:25 2002
@@ -62,6 +62,7 @@
 	envp = (char **) kmalloc (NUM_ENVP * sizeof (char *), GFP_KERNEL);
 	if (!envp)
 		return -ENOMEM;
+	memset (envp, 0x00, NUM_ENVP * sizeof (char *));
 
 	buffer = kmalloc (BUFFER_SIZE, GFP_KERNEL);
 	if (!buffer) {
diff -Nru a/drivers/block/DAC960.c b/drivers/block/DAC960.c
--- a/drivers/block/DAC960.c	Mon Oct  7 11:25:36 2002
+++ b/drivers/block/DAC960.c	Mon Oct  7 11:25:36 2002
@@ -1958,8 +1958,7 @@
 
   Controller->RequestQueue = RequestQueue;
   for (n = 0; n < DAC960_MaxLogicalDrives; n++) {
-	struct gendisk *disk = &Controller->disks[n];
-	memset(disk, 0, sizeof(struct gendisk));
+	struct gendisk *disk = Controller->disks[n];
 	sprintf(disk->disk_name, "rd/c%dd%d", Controller->ControllerNumber, n);
 	disk->major = MajorNumber;
 	disk->first_minor = n << DAC960_MaxPartitionsBits;
@@ -1983,7 +1982,7 @@
   int MajorNumber = DAC960_MAJOR + Controller->ControllerNumber;
   int disk;
   for (disk = 0; disk < DAC960_MaxLogicalDrives; disk++)
-	  del_gendisk(&Controller->disks[disk]);
+	  del_gendisk(Controller->disks[disk]);
   /*
     Unregister the Block Device Major Number for this DAC960 Controller.
   */
@@ -2018,7 +2017,7 @@
 {
 	int disk;
 	for (disk = 0; disk < DAC960_MaxLogicalDrives; disk++)
-		set_capacity(Controller->disks + disk, disk_size(Controller, disk));
+		set_capacity(Controller->disks[disk], disk_size(Controller, disk));
 }
 
 static int DAC960_revalidate(kdev_t dev)
@@ -2026,7 +2025,7 @@
 	int ctlr = DAC960_ControllerNumber(dev);
 	int disk = DAC960_LogicalDriveNumber(dev);
 	DAC960_Controller_T *p = DAC960_Controllers[ctlr];
-	set_capacity(&p->disks[disk], disk_size(p, disk));
+	set_capacity(p->disks[disk], disk_size(p, disk));
 	return 0;
 }
 
@@ -2099,6 +2098,7 @@
   unsigned short VendorID = 0, DeviceID = 0;
   unsigned int MemoryWindowSize = 0;
   PCI_Device_T *PCI_Device = NULL;
+  int i;
   switch (HardwareType)
     {
     case DAC960_BA_Controller:
@@ -2199,6 +2199,11 @@
 	  goto Failure;
 	}
       memset(Controller, 0, sizeof(DAC960_Controller_T));
+      for (i = 0; i < DAC960_MaxLogicalDrives; i++) {
+		Controller->disks[i] = alloc_disk();
+		if (!Controller->disks[i])
+			goto Enomem;
+      }
       Controller->ControllerNumber = DAC960_ControllerCount;
       init_waitqueue_head(&Controller->CommandWaitQueue);
       init_waitqueue_head(&Controller->HealthStatusWaitQueue);
@@ -2455,6 +2460,12 @@
       if (Controller->IRQ_Channel > 0)
 	free_irq(IRQ_Channel, Controller);
     }
+    return;
+Enomem:
+      while (i--)
+	put_disk(Controller->disks[i]);
+      kfree(Controller);
+      goto Failure;
 }
 
 
@@ -2493,7 +2504,10 @@
       DAC960_Controller_T *Controller = DAC960_Controllers[ControllerNumber];
       if (!Controller->ControllerDetectionSuccessful)
 	{
+	  int i;
 	  DAC960_Controllers[ControllerNumber] = NULL;
+	  for (i = 0; i < DAC960_MaxLogicalDrives; i++)
+		put_disk(Controller->disks[i]);
 	  kfree(Controller);
 	}
     }
@@ -2534,6 +2548,7 @@
 
 static void DAC960_FinalizeController(DAC960_Controller_T *Controller)
 {
+  int i;
   if (Controller->ControllerInitialized)
     {
       del_timer(&Controller->MonitoringTimer);
@@ -2578,6 +2593,8 @@
   DAC960_UnregisterBlockDevice(Controller);
   DAC960_DestroyAuxiliaryStructures(Controller);
   DAC960_Controllers[Controller->ControllerNumber] = NULL;
+  for (i = 0; i < DAC960_MaxLogicalDrives; i++)
+	put_disk(Controller->disks[i]);
   kfree(Controller);
 }
 
@@ -2606,8 +2623,8 @@
       if (Controller == NULL) continue;
       DAC960_InitializeController(Controller);
       for (disk = 0; disk < DAC960_MaxLogicalDrives; disk++) {
-	set_capacity(&Controller->disks[disk], disk_size(Controller, disk));
-	add_disk(&Controller->disks[disk]);
+	set_capacity(Controller->disks[disk], disk_size(Controller, disk));
+	add_disk(Controller->disks[disk]);
       }
     }
   DAC960_CreateProcEntries();
@@ -5250,10 +5267,10 @@
       Controller->LogicalDriveInitiallyAccessible[LogicalDriveNumber] = true;
       size = disk_size(Controller, LogicalDriveNumber);
       /* BROKEN, same as modular ide-floppy/ide-disk; same fix - ->probe() */
-      set_capacity(&Controller->disks[LogicalDriveNumber], size);
-      add_disk(&Controller->disks[LogicalDriveNumber]);
+      set_capacity(Controller->disks[LogicalDriveNumber], size);
+      add_disk(Controller->disks[LogicalDriveNumber]);
     }
-  if (!get_capacity(&Controller->disks[LogicalDriveNumber]))
+  if (!get_capacity(Controller->disks[LogicalDriveNumber]))
     return -ENXIO;
   /*
     Increment Controller and Logical Drive Usage Counts.
diff -Nru a/drivers/block/DAC960.h b/drivers/block/DAC960.h
--- a/drivers/block/DAC960.h	Mon Oct  7 11:25:36 2002
+++ b/drivers/block/DAC960.h	Mon Oct  7 11:25:36 2002
@@ -2360,7 +2360,7 @@
   boolean MonitoringAlertMode;
   boolean SuppressEnclosureMessages;
   Timer_T MonitoringTimer;
-  struct gendisk disks[DAC960_MaxLogicalDrives];
+  struct gendisk *disks[DAC960_MaxLogicalDrives];
   DAC960_Command_T *FreeCommands;
   unsigned char *CombinedStatusBuffer;
   unsigned char *CurrentStatusBuffer;
diff -Nru a/drivers/block/acsi.c b/drivers/block/acsi.c
--- a/drivers/block/acsi.c	Mon Oct  7 11:25:35 2002
+++ b/drivers/block/acsi.c	Mon Oct  7 11:25:35 2002
@@ -367,7 +367,6 @@
 static void acsi_prevent_removal( int target, int flag );
 static int acsi_change_blk_size( int target, int lun);
 static int acsi_mode_sense( int target, int lun, SENSE_DATA *sd );
-static void acsi_geninit(void);
 static int acsi_revalidate (kdev_t);
 
 /************************* End of Prototypes **************************/
@@ -974,12 +973,12 @@
 	dev = DEVICE_NR(CURRENT->rq_dev);
 	block = CURRENT->sector;
 	if (dev >= NDevices ||
-		block+CURRENT->nr_sectors >= get_capacity(acsi_gendisk + dev)) {
+		block+CURRENT->nr_sectors >= get_capacity(acsi_gendisk[dev])) {
 #ifdef DEBUG
 		printk( "ad%c: attempted access for blocks %d...%ld past end of device at block %ld.\n",
 		       dev+'a',
 		       block, block + CURRENT->nr_sectors - 1,
-		       get_capacity(acsi_gendisk + dev));
+		       get_capacity(acsi_gendisk[dev]));
 #endif
 		end_request(CURRENT, 0);
 		goto repeat;
@@ -1340,7 +1339,7 @@
 
 extern struct block_device_operations acsi_fops;
 
-static struct gendisk acsi_gendisk[MAX_DEV];
+static struct gendisk *acsi_gendisk[MAX_DEV];
 
 #define MAX_SCSI_DEVICE_CODE 10
 
@@ -1607,13 +1606,53 @@
 	revalidate:		acsi_revalidate,
 };
 
-static void acsi_geninit(void)
+#ifdef CONFIG_ATARI_SLM_MODULE
+/* call attach_slm() for each device that is a printer; needed for init of SLM
+ * driver as a module, since it's not yet present if acsi.c is inited and thus
+ * the bus gets scanned. */
+void acsi_attach_SLMs( int (*attach_func)( int, int ) )
+{
+	int i, n = 0;
+
+	for( i = 0; i < 8; ++i )
+		if (SLM_devices[i] >= 0)
+			n += (*attach_func)( i, SLM_devices[i] );
+	printk( KERN_INFO "Found %d SLM printer(s) total.\n", n );
+}
+#endif /* CONFIG_ATARI_SLM_MODULE */
+
+
+int acsi_init( void )
+
 {
+	int err = 0;
 	int i, target, lun;
 	struct acsi_info_struct *aip;
 #ifdef CONFIG_ATARI_SLM
 	int n_slm = 0;
 #endif
+	if (!MACH_IS_ATARI || !ATARIHW_PRESENT(ACSI))
+		return 0;
+	if (register_blkdev( MAJOR_NR, "ad", &acsi_fops )) {
+		printk( KERN_ERR "Unable to get major %d for ACSI\n", MAJOR_NR );
+		err = -EBUSY;
+		goto out1;
+	}
+	if (!(acsi_buffer =
+		  (char *)atari_stram_alloc(ACSI_BUFFER_SIZE, "acsi"))) {
+		err = -ENOMEM;
+		printk( KERN_ERR "Unable to get ACSI ST-Ram buffer.\n" );
+		goto out2;
+	}
+	phys_acsi_buffer = virt_to_phys( acsi_buffer );
+	STramMask = ATARIHW_PRESENT(EXTD_DMA) ? 0x00000000 : 0xff000000;
+	
+	blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), do_acsi_request, &acsi_lock);
+#ifdef CONFIG_ATARI_SLM
+	err = slm_init();
+#endif
+	if (err)
+		goto out3;
 
 	printk( KERN_INFO "Probing ACSI devices:\n" );
 	NDevices = 0;
@@ -1688,9 +1727,15 @@
 	printk( KERN_INFO "Found %d ACSI device(s) and %d SLM printer(s) total.\n",
 			NDevices, n_slm );
 #endif
-					 
+	err = -ENOMEM;
 	for( i = 0; i < NDevices; ++i ) {
-		struct gendisk *disk = acsi_gendisk + i;
+		acsi_gendisk[i] = alloc_disk();
+		if (!acsi_gendisk[i])
+			goto out4;
+	}
+
+	for( i = 0; i < NDevices; ++i ) {
+		struct gendisk *disk = acsi_gendisk[i];
 		sprintf(disk->disk_name, "ad%c", 'a'+i);
 		disk->major = MAJOR_NR;
 		disk->first_minor = i << 4;
@@ -1699,49 +1744,16 @@
 		set_capacity(disk, acsi_info[i].size);
 		add_disk(disk);
 	}
-}
-
-#ifdef CONFIG_ATARI_SLM_MODULE
-/* call attach_slm() for each device that is a printer; needed for init of SLM
- * driver as a module, since it's not yet present if acsi.c is inited and thus
- * the bus gets scanned. */
-void acsi_attach_SLMs( int (*attach_func)( int, int ) )
-{
-	int i, n = 0;
-
-	for( i = 0; i < 8; ++i )
-		if (SLM_devices[i] >= 0)
-			n += (*attach_func)( i, SLM_devices[i] );
-	printk( KERN_INFO "Found %d SLM printer(s) total.\n", n );
-}
-#endif /* CONFIG_ATARI_SLM_MODULE */
-
-
-int acsi_init( void )
-
-{
-	int err = 0;
-	if (!MACH_IS_ATARI || !ATARIHW_PRESENT(ACSI))
-		return 0;
-	if (register_blkdev( MAJOR_NR, "ad", &acsi_fops )) {
-		printk( KERN_ERR "Unable to get major %d for ACSI\n", MAJOR_NR );
-		return -EBUSY;
-	}
-	if (!(acsi_buffer =
-		  (char *)atari_stram_alloc(ACSI_BUFFER_SIZE, "acsi"))) {
-		printk( KERN_ERR "Unable to get ACSI ST-Ram buffer.\n" );
-		unregister_blkdev( MAJOR_NR, "ad" );
-		return -ENOMEM;
-	}
-	phys_acsi_buffer = virt_to_phys( acsi_buffer );
-	STramMask = ATARIHW_PRESENT(EXTD_DMA) ? 0x00000000 : 0xff000000;
-	
-	blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), do_acsi_request, &acsi_lock);
-#ifdef CONFIG_ATARI_SLM
-	err = slm_init();
-#endif
-	if (!err)
-		acsi_geninit();
+	return 0;
+out4:
+	while (i--)
+		put_disk(acsi_gendisk[i]);
+out3:
+	blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR));
+	atari_stram_free( acsi_buffer );
+out2:
+	unregister_blkdev( MAJOR_NR, "ad" );
+out1:
 	return err;
 }
 
@@ -1770,8 +1782,10 @@
 	if (unregister_blkdev( MAJOR_NR, "ad" ) != 0)
 		printk( KERN_ERR "acsi: cleanup_module failed\n");
 
-	for (i = 0; i < NDevices; i++)
-		del_gendisk(acsi_gendisk + i);
+	for (i = 0; i < NDevices; i++) {
+		del_gendisk(acsi_gendisk[i]);
+		put_disk(acsi_gendisk[i]);
+	}
 }
 #endif
 
@@ -1810,6 +1824,6 @@
 
 	ENABLE_IRQ();
 	stdma_release();
-	set_capacity(acsi_gendisk + unit, aip->size);
+	set_capacity(acsi_gendisk[unit], aip->size);
 	return 0;
 }
diff -Nru a/drivers/block/amiflop.c b/drivers/block/amiflop.c
--- a/drivers/block/amiflop.c	Mon Oct  7 11:25:35 2002
+++ b/drivers/block/amiflop.c	Mon Oct  7 11:25:35 2002
@@ -1643,7 +1643,7 @@
 	unit[drive].dtype=&data_types[system];
 	unit[drive].blocks=unit[drive].type->heads*unit[drive].type->tracks*
 		data_types[system].sects*unit[drive].type->sect_mult;
-	set_capacity(&unit[drive].disk, unit[drive].blocks);
+	set_capacity(unit[drive].gendisk, unit[drive].blocks);
 
 	printk(KERN_INFO "fd%d: accessing %s-disk with %s-layout\n",drive,
 	       unit[drive].type->name, data_types[system].name);
@@ -1731,25 +1731,31 @@
 	drives=0;
 	nomem=0;
 	for(drive=0;drive<FD_MAX_UNITS;drive++) {
+		struct gendisk *disk;
 		fd_probe(drive);
-		if (unit[drive].type->code != FD_NODRIVE) {
-			struct gendisk *disk = &unit[drive].disk;
-			drives++;
-			if ((unit[drive].trackbuf = kmalloc(FLOPPY_MAX_SECTORS * 512, GFP_KERNEL)) == NULL) {
-				printk("no mem for ");
-				unit[drive].type = &drive_types[num_dr_types - 1]; /* FD_NODRIVE */
-				drives--;
-				nomem = 1;
-			}
-			printk("fd%d ",drive);
-			disk->major = MAJOR_NR;
-			disk->first_minor = drive;
-			disk->minor_shift = 0;
-			disk->fops = &floppy_fops;
-			sprintf(disk->disk_name, "fd%d", drive);
-			set_capacity(disk, 880*2);
-			add_disk(disk);
+		if (unit[drive].type->code == FD_NODRIVE)
+			continue;
+		disk = alloc_disk();
+		if (!disk) {
+			unit[drive].type->code = FD_NODRIVE;
+			continue;
 		}
+		unit[drive].gendisk = disk;
+		drives++;
+		if ((unit[drive].trackbuf = kmalloc(FLOPPY_MAX_SECTORS * 512, GFP_KERNEL)) == NULL) {
+			printk("no mem for ");
+			unit[drive].type = &drive_types[num_dr_types - 1]; /* FD_NODRIVE */
+			drives--;
+			nomem = 1;
+		}
+		printk("fd%d ",drive);
+		disk->major = MAJOR_NR;
+		disk->first_minor = drive;
+		disk->minor_shift = 0;
+		disk->fops = &floppy_fops;
+		sprintf(disk->disk_name, "fd%d", drive);
+		set_capacity(disk, 880*2);
+		add_disk(disk);
 	}
 	if ((drives > 0) || (nomem == 0)) {
 		if (drives == 0)
@@ -1766,7 +1772,7 @@
 	int drive = minor & 3;
 	if (unit[drive].type->code == FD_NODRIVE)
 		return NULL;
-	return &unit[drive].disk;
+	return unit[drive].gendisk;
 }
 
 int __init amiga_floppy_init(void)
@@ -1875,7 +1881,8 @@
 
 	for( i = 0; i < FD_MAX_UNITS; i++) {
 		if (unit[i].type->code != FD_NODRIVE) {
-			del_gendisk(&unit[i].disk);
+			del_gendisk(unit[i].gendisk);
+			put_disk(unit[i].gendisk);
 			kfree(unit[i].trackbuf);
 		}
 	}
diff -Nru a/drivers/block/ataflop.c b/drivers/block/ataflop.c
--- a/drivers/block/ataflop.c	Mon Oct  7 11:25:26 2002
+++ b/drivers/block/ataflop.c	Mon Oct  7 11:25:26 2002
@@ -235,7 +235,7 @@
 	unsigned int wpstat;	/* current state of WP signal (for
 				   disk change detection) */
 	int flags;		/* flags */
-	struct gendisk disk;
+	struct gendisk *disk;
 } unit[FD_MAX_UNITS];
 
 #define	UD	unit[drive]
@@ -1093,7 +1093,7 @@
 			    if (SUDT[-1].blocks > ReqBlock) {
 				/* try another disk type */
 				SUDT--;
-				set_capacity(&unit[SelectedDrive].disk,
+				set_capacity(unit[SelectedDrive].disk,
 							SUDT->blocks);
 			    } else
 				Probing = 0;
@@ -1108,7 +1108,7 @@
 /* record not found, but not probing. Maybe stretch wrong ? Restart probing */
 			if (SUD.autoprobe) {
 				SUDT = disk_type + StartDiskType[DriveType];
-				set_capacity(&unit[SelectedDrive].disk,
+				set_capacity(unit[SelectedDrive].disk,
 							SUDT->blocks);
 				Probing = 1;
 			}
@@ -1470,7 +1470,7 @@
 		if (!UDT) {
 			Probing = 1;
 			UDT = disk_type + StartDiskType[DriveType];
-			set_capacity(&unit[drive].disk, UDT->blocks);
+			set_capacity(unit[drive].disk, UDT->blocks);
 			UD.autoprobe = 1;
 		}
 	} 
@@ -1488,7 +1488,7 @@
 		}
 		type = minor2disktype[type].index;
 		UDT = &disk_type[type];
-		set_capacity(&unit[drive].disk, UDT->blocks);
+		set_capacity(unit[drive].disk, UDT->blocks);
 		UD.autoprobe = 0;
 	}
 	
@@ -1635,7 +1635,7 @@
 				    printk (KERN_INFO "floppy%d: setting %s %p!\n",
 				        drive, dtp->name, dtp);
 				UDT = dtp;
-				set_capacity(&unit[drive].disk, UDT->blocks);
+				set_capacity(unit[drive].disk, UDT->blocks);
 
 				if (cmd == FDDEFPRM) {
 				  /* save settings as permanent default type */
@@ -1681,7 +1681,7 @@
 		}
 
 		UDT = dtp;
-		set_capacity(&unit[drive].disk, UDT->blocks);
+		set_capacity(unit[drive].disk, UDT->blocks);
 
 		return 0;
 	case FDMSGON:
@@ -1704,7 +1704,7 @@
 		UDT = NULL;
 		/* MSch: invalidate default_params */
 		default_params[drive].blocks  = 0;
-		set_capacity(&unit[drive].disk, MAX_DISK_SIZE * 2);
+		set_capacity(unit[drive].disk, MAX_DISK_SIZE * 2);
 	case FDFMTEND:
 	case FDFLUSH:
 		/* invalidate the buffer track to force a reread */
@@ -1928,7 +1928,7 @@
 	int type  = minor >> 2;
 	if (drive >= FD_MAX_UNITS || type > NUM_DISK_MINORS)
 		return NULL;
-	return &unit[drive].disk;
+	return unit[drive].disk;
 }
 
 int __init atari_floppy_init (void)
@@ -1948,6 +1948,12 @@
 		return -EBUSY;
 	}
 
+	for (i = 0; i < FD_MAX_UNITS; i++) {
+		unit[i].disk = alloc_disk();
+		if (!unit[i].disk)
+			goto Enomem;
+	}
+
 	if (UseTrackbuffer < 0)
 		/* not set by user -> use default: for now, we turn
 		   track buffering off for all Medusas, though it
@@ -1962,8 +1968,7 @@
 	DMABuffer = atari_stram_alloc(BUFFER_SIZE+512, "ataflop");
 	if (!DMABuffer) {
 		printk(KERN_ERR "atari_floppy_init: cannot get dma buffer\n");
-		unregister_blkdev(MAJOR_NR, "fd");
-		return -ENOMEM;
+		goto Enomem;
 	}
 	TrackBuffer = DMABuffer + 512;
 	PhysDMABuffer = virt_to_phys(DMABuffer);
@@ -1973,12 +1978,12 @@
 	for (i = 0; i < FD_MAX_UNITS; i++) {
 		unit[i].track = -1;
 		unit[i].flags = 0;
-		unit[i].disk.major = MAJOR_NR;
-		unit[i].disk.first_minor = i;
-		sprintf(unit[i].disk.disk_name, "fd%d", i);
-		unit[i].disk.fops = &floppy_fops;
-		set_capacity(&unit[i].disk, MAX_DISK_SIZE * 2);
-		add_disk(&unit[i].disk);
+		unit[i].disk->major = MAJOR_NR;
+		unit[i].disk->first_minor = i;
+		sprintf(unit[i].disk->disk_name, "fd%d", i);
+		unit[i].disk->fops = &floppy_fops;
+		set_capacity(unit[i].disk, MAX_DISK_SIZE * 2);
+		add_disk(unit[i].disk);
 	}
 
 	blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), do_fd_request, &ataflop_lock);
@@ -1990,6 +1995,11 @@
 	config_types();
 
 	return 0;
+Enomem:
+	while (i--)
+		put_disk(unit[i].disk);
+	unregister_blkdev(MAJOR_NR, "fd");
+	return -ENOMEM;
 }
 
 
@@ -2035,8 +2045,10 @@
 void cleanup_module (void)
 {
 	int i;
-	for (i = 0; i < FD_MAX_UNITS; i++)
-		del_gendisk(&unit[i].disk);
+	for (i = 0; i < FD_MAX_UNITS; i++) {
+		del_gendisk(unit[i].disk);
+		put_disk(unit[i].disk);
+	}
 	unregister_blkdev(MAJOR_NR, "fd");
 	blk_set_probe(MAJOR_NR, NULL);
 
diff -Nru a/drivers/block/cciss.c b/drivers/block/cciss.c
--- a/drivers/block/cciss.c	Mon Oct  7 11:25:39 2002
+++ b/drivers/block/cciss.c	Mon Oct  7 11:25:39 2002
@@ -703,7 +703,7 @@
 {
         int ctlr = major(dev) - MAJOR_NR;
 	int target = minor(dev) >> NWD_SHIFT;
-        struct gendisk *disk = &hba[ctlr]->gendisk[target];
+        struct gendisk *disk = hba[ctlr]->gendisk[target];
 	set_capacity(disk, hba[ctlr]->drv[target].nr_blocks);
 	return 0;
 }
@@ -739,7 +739,7 @@
 	spin_unlock_irqrestore(CCISS_LOCK(ctlr), flags);
 
 	for(i=0; i< NWD; i++) {
-		struct gendisk *disk = &hba[ctlr]->gendisk[i];
+		struct gendisk *disk = hba[ctlr]->gendisk[i];
 		if (disk->part)
 			del_gendisk(disk);
 	}
@@ -761,7 +761,7 @@
 
 	/* Loop through each real device */ 
 	for (i = 0; i < NWD; i++) {
-		struct gendisk *disk = &hba[ctlr]->gendisk[i];
+		struct gendisk *disk = hba[ctlr]->gendisk[i];
 		drive_info_struct *drv = &(hba[ctlr]->drv[i]);
 		if (!drv->nr_blocks)
 			continue;
@@ -776,7 +776,7 @@
 static int deregister_disk(int ctlr, int logvol)
 {
 	unsigned long flags;
-	struct gendisk *disk = &hba[ctlr]->gendisk[logvol];
+	struct gendisk *disk = hba[ctlr]->gendisk[logvol];
 	ctlr_info_t  *h = hba[ctlr];
 
 	if (!capable(CAP_SYS_RAWIO))
@@ -1231,7 +1231,7 @@
 	hba[ctlr]->drv[logvol].usage_count = 0;
 	++hba[ctlr]->num_luns;
 	/* setup partitions per disk */
-        disk = &hba[ctlr]->gendisk[logvol];
+        disk = hba[ctlr]->gendisk[logvol];
 	set_capacity(disk, hba[ctlr]->drv[logvol].nr_blocks);
 	add_disk(disk);
 	
@@ -2271,29 +2271,47 @@
 /* Returns -1 if no free entries are left.  */
 static int alloc_cciss_hba(void)
 {
-	int i;
-	for(i=0; i< MAX_CTLR; i++)
-	{
-		if (hba[i] == NULL)
-		{
-			hba[i] = kmalloc(sizeof(ctlr_info_t), GFP_KERNEL);
-			if(hba[i]==NULL)
-			{
-				printk(KERN_ERR "cciss: out of memory.\n");
-				return (-1);
-			}
-			return (i);
+	struct gendisk *disk[NWD];
+	int i, n;
+	for (n = 0; n < NWD; n++) {
+		disk[n] = disk_alloc();
+		if (!disk[n])
+			goto out;
+	}
+
+	for(i=0; i< MAX_CTLR; i++) {
+		if (!hba[i]) {
+			ctlr_info_t *p;
+			p = kmalloc(sizeof(ctlr_info_t), GFP_KERNEL);
+			if (!p)
+				goto Enomem;
+			memset(p, 0, sizeof(ctlr_info_t));
+			for (n = 0; n < NWD; n++)
+				p->gendisk[n] = disk[n];
+			hba[i] = p;
+			return i;
 		}
 	}
 	printk(KERN_WARNING "cciss: This driver supports a maximum"
 		" of 8 controllers.\n");
-	return(-1);
+	goto out;
+Enomem:
+	printk(KERN_ERR "cciss: out of memory.\n");
+out:
+	while (n--)
+		put_disk(disk[n]);
+	return -1;
 }
 
 static void free_hba(int i)
 {
-	kfree(hba[i]);
-	hba[i]=NULL;
+	ctlr_info_t *p = hba[i];
+	int n;
+
+	hba[i] = NULL;
+	for (n = 0; n < NWD; n++)
+		put_disk(p->gendisk[n]);
+	kfree(p);
 }
 
 /*
@@ -2315,7 +2333,6 @@
 	i = alloc_cciss_hba();
 	if( i < 0 ) 
 		return (-1);
-	memset(hba[i], 0, sizeof(ctlr_info_t));
 	if (cciss_pci_init(hba[i], pdev) != 0)
 	{
 		release_io_mem(hba[i]);
@@ -2425,7 +2442,7 @@
 
 	for(j=0; j<NWD; j++) {
 		drive_info_struct *drv = &(hba[i]->drv[j]);
-		struct gendisk *disk = hba[i]->gendisk + j;
+		struct gendisk *disk = hba[i]->gendisk[j];
 
 		sprintf(disk->disk_name, "cciss/c%dd%d", i, j);
 		disk->major = MAJOR_NR + i;
@@ -2482,7 +2499,7 @@
 	
 	/* remove it from the disk list */
 	for (j = 0; j < NWD; j++) {
-		struct gendisk *disk = &hba[i]->gendisk[j];
+		struct gendisk *disk = hba[i]->gendisk[j];
 		if (disk->part)
 			del_gendisk(disk);
 	}
diff -Nru a/drivers/block/cciss.h b/drivers/block/cciss.h
--- a/drivers/block/cciss.h	Mon Oct  7 11:25:25 2002
+++ b/drivers/block/cciss.h	Mon Oct  7 11:25:25 2002
@@ -81,7 +81,7 @@
 	int			nr_frees; 
 
 	// Disk structures we need to pass back
-	struct gendisk   gendisk[NWD];
+	struct gendisk   *gendisk[NWD];
 #ifdef CONFIG_CISS_SCSI_TAPE
 	void *scsi_ctlr; /* ptr to structure containing scsi related stuff */
 #endif
diff -Nru a/drivers/block/cpqarray.c b/drivers/block/cpqarray.c
--- a/drivers/block/cpqarray.c	Mon Oct  7 11:25:26 2002
+++ b/drivers/block/cpqarray.c	Mon Oct  7 11:25:26 2002
@@ -102,7 +102,7 @@
 	{ 0x40580E11, "Smart Array 431",	&smart4_access },
 };
 
-static struct gendisk ida_gendisk[MAX_CTLR * NWD];
+static struct gendisk *ida_gendisk[MAX_CTLR][NWD];
 
 static struct proc_dir_entry *proc_array;
 
@@ -115,7 +115,6 @@
 /* Debug Extra Paranoid... */
 #define DBGPX(s) do { } while(0)
 
-int cpqarray_init(void);
 static int cpqarray_pci_detect(void);
 static int cpqarray_pci_init(ctlr_info_t *c, struct pci_dev *pdev);
 static void *remap_pci_mem(ulong base, ulong size);
@@ -273,19 +272,9 @@
 }
 #endif /* CONFIG_PROC_FS */
 
-#ifdef MODULE
-
 MODULE_PARM(eisa, "1-8i");
 
-/* This is a bit of a hack... */
-int __init init_module(void)
-{
-	if (cpqarray_init() == 0) /* all the block dev numbers already used */
-		return -EIO;	  /* or no controllers were found */
-	return 0;
-}
-
-void cleanup_module(void)
+static void __exit cpqarray_exit(void)
 {
 	int i, j;
 	char buff[4]; 
@@ -312,21 +301,21 @@
 		kfree(hba[i]->cmd_pool_bits);
 
 		for (j = 0; j < NWD; j++) {
-			if (ida_gendisk[i*NWD+j].part)
-				del_gendisk(&ida_gendisk[i*NWD+j]);
+			if (ida_gendisk[i][j]->part)
+				del_gendisk(ida_gendisk[i][j]);
+			put_disk(ida_gendisk[i][j]);
 		}
 	}
 	devfs_find_and_unregister(NULL, "ida", 0, 0, 0, 0);
 	remove_proc_entry("cpqarray", proc_root_driver);
 }
-#endif /* MODULE */
 
 /*
  *  This is it.  Find all the controllers and register them.  I really hate
  *  stealing all these major device numbers.
  *  returns the number of block devices registered.
  */
-int __init cpqarray_init(void)
+static int __init cpqarray_init(void)
 {
 	request_queue_t *q;
 	int i,j;
@@ -336,7 +325,7 @@
 	cpqarray_eisa_detect();
 	
 	if (nr_ctlr == 0)
-		return(num_cntlrs_reg);
+		return -ENODEV;
 
 	printk(DRIVER_NAME "\n");
 	printk("Found %d controller(s)\n", nr_ctlr);
@@ -347,7 +336,7 @@
 	 * Find disks and fill in structs
 	 * Get an interrupt, set the Q depth and get into /proc
 	 */
-	for(i=0; i< nr_ctlr; i++) {
+	for(i=0; i < nr_ctlr; i++) {
 	  	/* If this successful it should insure that we are the only */
 		/* instance of the driver */	
 		if (register_blkdev(MAJOR_NR+i, hba[i]->devname, &ida_fops)) {
@@ -355,8 +344,6 @@
                                 MAJOR_NR+i);
                         continue;
                 }
-
-	
 		hba[i]->access.set_intr_mask(hba[i], 0);
 		if (request_irq(hba[i]->intr, do_ida_intr,
 			SA_INTERRUPT|SA_SHIRQ, hba[i]->devname, hba[i])) {
@@ -367,33 +354,18 @@
 			continue;
 		}
 		num_cntlrs_reg++;
+		for (j=0; j<NWD; j++) {
+			ida_gendisk[i][j] = disk_alloc();
+			if (!ida_gendisk[i][j])
+				goto Enomem2;
+		}
 		hba[i]->cmd_pool = (cmdlist_t *)pci_alloc_consistent(
 				hba[i]->pci_dev, NR_CMDS * sizeof(cmdlist_t), 
 				&(hba[i]->cmd_pool_dhandle));
 		hba[i]->cmd_pool_bits = kmalloc(((NR_CMDS+BITS_PER_LONG-1)/BITS_PER_LONG)*sizeof(unsigned long), GFP_KERNEL);
 		
-	if(hba[i]->cmd_pool_bits == NULL || hba[i]->cmd_pool == NULL)
-		{
-			nr_ctlr = i; 
-			if(hba[i]->cmd_pool_bits)
-				kfree(hba[i]->cmd_pool_bits);
-			if(hba[i]->cmd_pool)
-				pci_free_consistent(hba[i]->pci_dev, 
-					NR_CMDS * sizeof(cmdlist_t), 
-					hba[i]->cmd_pool, 
-					hba[i]->cmd_pool_dhandle);
-			free_irq(hba[i]->intr, hba[i]);
-			unregister_blkdev(MAJOR_NR+i, hba[i]->devname);
-			num_cntlrs_reg--;
-                	printk( KERN_ERR "cpqarray: out of memory");
-
-			/* If num_cntlrs_reg == 0, no controllers worked. 
-			 *	init_module will fail, so clean up global 
-			 *	memory that clean_module would do.
-			*/	
-                	return(num_cntlrs_reg);
-	
-		}
+		if (!hba[i]->cmd_pool_bits || !hba[i]->cmd_pool)
+			goto Enomem1;
 		memset(hba[i]->cmd_pool, 0, NR_CMDS * sizeof(cmdlist_t));
 		memset(hba[i]->cmd_pool_bits, 0, ((NR_CMDS+BITS_PER_LONG-1)/BITS_PER_LONG)*sizeof(unsigned long));
 		printk(KERN_INFO "cpqarray: Finding drives on %s", 
@@ -424,7 +396,7 @@
 		add_timer(&hba[i]->timer);
 
 		for(j=0; j<NWD; j++) {
-			struct gendisk *disk = ida_gendisk + i*NWD + j;
+			struct gendisk *disk = ida_gendisk[i][j];
 			drv_info_t *drv = &hba[i]->drv[j];
 			sprintf(disk->disk_name, "ida/c%dd%d", i, j);
 			disk->major = MAJOR_NR + i;
@@ -440,7 +412,31 @@
 		}
 	}
 	/* done ! */
-	return(num_cntlrs_reg);
+	return num_cntlrs_reg ? 0 : -ENODEV;
+
+Enomem1:
+	nr_ctlr = i; 
+	kfree(hba[i]->cmd_pool_bits);
+	if (hba[i]->cmd_pool)
+		pci_free_consistent(hba[i]->pci_dev, NR_CMDS*sizeof(cmdlist_t), 
+				    hba[i]->cmd_pool, hba[i]->cmd_pool_dhandle);
+Enomem2:
+	while (j--) {
+		put_disk(ida_gendisk[i][j]);
+		ida_gendisk[i][j] = NULL;
+	}
+	free_irq(hba[i]->intr, hba[i]);
+	unregister_blkdev(MAJOR_NR+i, hba[i]->devname);
+	num_cntlrs_reg--;
+	printk( KERN_ERR "cpqarray: out of memory");
+
+	if (!num_cntlrs_reg) {
+		devfs_find_and_unregister(NULL,"ida",0,0,0,0);
+		remove_proc_entry("cpqarray", proc_root_driver);
+		return -ENODEV;
+	}
+	return 0;
+}
 }
 
 /*
@@ -1434,7 +1430,7 @@
 	 * on this controller to zero.  We will reread all of this data
 	 */
 	for (i = 0; i < NWD; i++) {
-		struct gendisk *disk = ida_gendisk + ctlr*NWD + i;
+		struct gendisk *disk = ida_gendisk[ctlr][i];
 		if (disk->part)
 			del_gendisk(disk);
 	}
@@ -1450,7 +1446,7 @@
 	hba[ctlr]->access.set_intr_mask(hba[ctlr], FIFO_NOT_EMPTY);
 
 	for(i=0; i<NWD; i++) {
-		struct gendisk *disk = ida_gendisk + ctlr*NWD + i;
+		struct gendisk *disk = ida_gendisk[ctlr][i];
 		drv_info_t *drv = &hba[ctlr]->drv[i];
 		if (!drv->nr_blks)
 			continue;
@@ -1467,7 +1463,7 @@
 {
         int ctlr = major(dev) - MAJOR_NR;
 	int target = DEVICE_NR(dev);
-        struct gendisk *gdev = &ida_gendisk[ctlr*NWD+target];
+        struct gendisk *gdev = ida_gendisk[ctlr][target];
 	set_capacity(gdev, hba[ctlr]->drv[target].nr_blks);
 	return 0;
 }
@@ -1635,7 +1631,7 @@
 	     (log_index < id_ctlr_buf->nr_drvs)
 	     && (log_unit < NWD);
 	     log_unit++) {
-		struct gendisk *disk = ida_gendisk + ctlr * NWD + log_unit;
+		struct gendisk *disk = ida_gendisk[ctlr][log_unit];
 
 		size = sizeof(sense_log_drv_stat_t);
 
@@ -1724,3 +1720,6 @@
 	return;
 
 }
+
+module_init(cpqarray_init)
+module_exit(cpqarray_exit)
diff -Nru a/drivers/block/deadline-iosched.c b/drivers/block/deadline-iosched.c
--- a/drivers/block/deadline-iosched.c	Mon Oct  7 11:25:37 2002
+++ b/drivers/block/deadline-iosched.c	Mon Oct  7 11:25:37 2002
@@ -159,7 +159,6 @@
 	struct deadline_data *dd = q->elevator.elevator_data;
 	const int data_dir = bio_data_dir(bio);
 	struct list_head *entry, *sort_list;
-	struct deadline_rq *drq;
 	struct request *__rq;
 	int ret = ELEVATOR_NO_MERGE;
 
@@ -191,15 +190,21 @@
 	entry = sort_list = &dd->sort_list[data_dir];
 	while ((entry = entry->prev) != sort_list) {
 		__rq = list_entry_rq(entry);
-		drq = RQ_DATA(__rq);
 
 		BUG_ON(__rq->flags & REQ_STARTED);
 
 		if (!(__rq->flags & REQ_CMD))
 			continue;
 
-		if (!*insert && bio_rq_in_between(bio, __rq, sort_list))
+		/*
+		 * it's not necessary to break here, and in fact it could make
+		 * us loose a front merge. emperical evidence shows this to
+		 * be a big waste of cycles though, so quit scanning
+		 */
+		if (!*insert && bio_rq_in_between(bio, __rq, sort_list)) {
 			*insert = &__rq->queuelist;
+			break;
+		}
 
 		if (__rq->flags & REQ_BARRIER)
 			break;
@@ -583,7 +588,7 @@
 	return 0;
 }
 
-module_init(deadline_slab_setup);
+subsys_initcall(deadline_slab_setup);
 
 elevator_t iosched_deadline = {
 	.elevator_merge_fn = 		deadline_merge,
diff -Nru a/drivers/block/elevator.c b/drivers/block/elevator.c
--- a/drivers/block/elevator.c	Mon Oct  7 11:25:36 2002
+++ b/drivers/block/elevator.c	Mon Oct  7 11:25:36 2002
@@ -217,9 +217,11 @@
 /*
  * general block -> elevator interface starts here
  */
-int elevator_init(request_queue_t *q, elevator_t *e, elevator_t type)
+int elevator_init(request_queue_t *q, elevator_t *type)
 {
-	*e = type;
+	elevator_t *e = &q->elevator;
+
+	memcpy(e, type, sizeof(*e));
 
 	INIT_LIST_HEAD(&q->queue_head);
 	q->last_merge = NULL;
@@ -230,8 +232,10 @@
 	return 0;
 }
 
-void elevator_exit(request_queue_t *q, elevator_t *e)
+void elevator_exit(request_queue_t *q)
 {
+	elevator_t *e = &q->elevator;
+
 	if (e->elevator_exit_fn)
 		e->elevator_exit_fn(q, e);
 }
diff -Nru a/drivers/block/floppy.c b/drivers/block/floppy.c
--- a/drivers/block/floppy.c	Mon Oct  7 11:25:26 2002
+++ b/drivers/block/floppy.c	Mon Oct  7 11:25:26 2002
@@ -150,7 +150,7 @@
 #include <linux/fs.h>
 #include <linux/kernel.h>
 #include <linux/timer.h>
-#include <linux/tqueue.h>
+#include <linux/workqueue.h>
 #define FDPATCHES
 #include <linux/fdreg.h>
 
@@ -415,8 +415,7 @@
 static struct floppy_drive_struct drive_state[N_DRIVE];
 static struct floppy_write_errors write_errors[N_DRIVE];
 static struct timer_list motor_off_timer[N_DRIVE];
-static struct gendisk disks[N_DRIVE];
-static char names[N_DRIVE][4];
+static struct gendisk *disks[N_DRIVE];
 static struct floppy_raw_cmd *raw_cmd, default_raw_cmd;
 
 /*
@@ -1004,12 +1003,12 @@
 {
 }
 
-static struct tq_struct floppy_tq;
+static DECLARE_WORK(floppy_work, NULL, NULL);
 
 static void schedule_bh( void (*handler)(void*) )
 {
-	floppy_tq.routine = (void *)(void *) handler;
-	schedule_task(&floppy_tq);
+	PREPARE_WORK(&floppy_work, handler, NULL);
+	schedule_work(&floppy_work);
 }
 
 static struct timer_list fd_timer;
@@ -1017,7 +1016,7 @@
 static void cancel_activity(void)
 {
 	do_floppy = NULL;
-	floppy_tq.routine = (void *)(void *) empty;
+	PREPARE_WORK(&floppy_work, (void*)(void*)empty, NULL);
 	del_timer(&fd_timer);
 }
 
@@ -1886,8 +1885,8 @@
 	printk("fdc_busy=%lu\n", fdc_busy);
 	if (do_floppy)
 		printk("do_floppy=%p\n", do_floppy);
-	if (floppy_tq.sync)
-		printk("floppy_tq.routine=%p\n", floppy_tq.routine);
+	if (floppy_work.pending)
+		printk("floppy_work.func=%p\n", floppy_work.func);
 	if (timer_pending(&fd_timer))
 		printk("fd_timer.function=%p\n", fd_timer.function);
 	if (timer_pending(&fd_timeout)){
@@ -3772,7 +3771,7 @@
 	}
 
 	UDRS->fd_device = minor(inode->i_rdev);
-	set_capacity(&disks[drive], floppy_sizes[minor(inode->i_rdev)]);
+	set_capacity(disks[drive], floppy_sizes[minor(inode->i_rdev)]);
 	if (old_dev != -1 && old_dev != minor(inode->i_rdev)) {
 		if (buffer_drive == drive)
 			buffer_track = -1;
@@ -3952,7 +3951,7 @@
 			process_fd_request();
 		}
 	}
-	set_capacity(&disks[drive], floppy_sizes[minor(dev)]);
+	set_capacity(disks[drive], floppy_sizes[minor(dev)]);
 	return res;
 }
 
@@ -4230,26 +4229,34 @@
 	    !(allowed_drive_mask & (1 << drive)) ||
 	    fdc_state[FDC(drive)].version == FDC_NONE)
 		return NULL;
-	return &disks[drive];
+	return disks[drive];
 }
 
 int __init floppy_init(void)
 {
 	int i,unit,drive;
+	int err;
 
 	raw_cmd = NULL;
 
+	for (i=0; i<N_DRIVE; i++) {
+		disks[i] = alloc_disk();
+		if (!disks[i])
+			goto Enomem;
+	}
+
 	devfs_handle = devfs_mk_dir (NULL, "floppy", NULL);
 	if (register_blkdev(MAJOR_NR,"fd",&floppy_fops)) {
 		printk("Unable to get major %d for floppy\n",MAJOR_NR);
-		return -EBUSY;
+		err = -EBUSY;
+		goto out;
 	}
 
 	for (i=0; i<N_DRIVE; i++) {
-		disks[i].major = MAJOR_NR;
-		disks[i].first_minor = TOMINOR(i);
-		disks[i].fops = &floppy_fops;
-		sprintf(disks[i].disk_name, "fd%d", i);
+		disks[i]->major = MAJOR_NR;
+		disks[i]->first_minor = TOMINOR(i);
+		disks[i]->fops = &floppy_fops;
+		sprintf(disks[i]->disk_name, "fd%d", i);
 	}
 
 	blk_set_probe(MAJOR_NR, floppy_find);
@@ -4281,11 +4288,8 @@
 	use_virtual_dma = can_use_virtual_dma & 1;
 	fdc_state[0].address = FDC1;
 	if (fdc_state[0].address == -1) {
-		unregister_blkdev(MAJOR_NR,"fd");
-		del_timer(&fd_timeout);
-		blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR));
-		blk_set_probe(MAJOR_NR, NULL);
-		return -ENODEV;
+		err = -ENODEV;
+		goto out1;
 	}
 #if N_FDC > 1
 	fdc_state[1].address = FDC2;
@@ -4293,11 +4297,8 @@
 
 	fdc = 0; /* reset fdc in case of unexpected interrupt */
 	if (floppy_grab_irq_and_dma()){
-		del_timer(&fd_timeout);
-		blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR));
-		unregister_blkdev(MAJOR_NR,"fd");
-		blk_set_probe(MAJOR_NR, NULL);
-		return -EBUSY;
+		err = -EBUSY;
+		goto out1;
 	}
 
 	/* initialise drive state */
@@ -4352,15 +4353,13 @@
 	current_drive = 0;
 	floppy_release_irq_and_dma();
 	initialising=0;
-	if (have_no_fdc) 
-	{
+	if (have_no_fdc) {
 		DPRINT("no floppy controllers found\n");
-		flush_scheduled_tasks();
+		flush_scheduled_work();
 		if (usage_count)
 			floppy_release_irq_and_dma();
-		blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR));
-		unregister_blkdev(MAJOR_NR,"fd");
-		blk_set_probe(MAJOR_NR, NULL);
+		err = have_no_fdc;
+		goto out2;
 	}
 	
 	for (drive = 0; drive < N_DRIVE; drive++) {
@@ -4370,12 +4369,27 @@
 			continue;
 		if (fdc_state[FDC(drive)].version == FDC_NONE)
 			continue;
-		add_disk(disks + drive);
+		add_disk(disks[drive]);
 	}
 
 	platform_device_register(&floppy_device);
+	return 0;
 
-	return have_no_fdc;
+out1:
+	del_timer(&fd_timeout);
+out2:
+	unregister_blkdev(MAJOR_NR,"fd");
+	blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR));
+	blk_set_probe(MAJOR_NR, NULL);
+out:
+	for (i=0; i<N_DRIVE; i++)
+		put_disk(disks[i]);
+	return err;
+
+Enomem:
+	while (i--)
+		put_disk(disks[i]);
+	return -ENOMEM;
 }
 
 static spinlock_t floppy_usage_lock = SPIN_LOCK_UNLOCKED;
@@ -4511,8 +4525,8 @@
 		printk("floppy timer still active:%s\n", timeout_message);
 	if (timer_pending(&fd_timer))
 		printk("auxiliary floppy timer still active\n");
-	if (floppy_tq.sync)
-		printk("task queue still active\n");
+	if (floppy_work.pending)
+		printk("work still pending\n");
 #endif
 	old_fdc = fdc;
 	for (fdc = 0; fdc < N_FDC; fdc++)
@@ -4564,7 +4578,8 @@
 	for (drive = 0; drive < N_DRIVE; drive++) {
 		if ((allowed_drive_mask & (1 << drive)) &&
 		    fdc_state[FDC(drive)].version != FDC_NONE)
-			del_gendisk(disks + drive);
+			del_gendisk(disks[drive]);
+		put_disk(disks[drive]);
 	}
 
 	blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR));
diff -Nru a/drivers/block/genhd.c b/drivers/block/genhd.c
--- a/drivers/block/genhd.c	Mon Oct  7 11:25:35 2002
+++ b/drivers/block/genhd.c	Mon Oct  7 11:25:35 2002
@@ -205,7 +205,6 @@
 extern int blk_dev_init(void);
 extern int soc_probe(void);
 extern int atmdev_init(void);
-extern int cpqarray_init(void);
 
 struct device_class disk_devclass = {
 	.name		= "disk",
@@ -221,9 +220,6 @@
 #ifdef CONFIG_FC4_SOC
 	/* This has to be done before scsi_dev_init */
 	soc_probe();
-#endif
-#ifdef CONFIG_BLK_CPQ_DA
-	cpqarray_init();
 #endif
 #ifdef CONFIG_ATM
 	(void) atmdev_init();
diff -Nru a/drivers/block/ll_rw_blk.c b/drivers/block/ll_rw_blk.c
--- a/drivers/block/ll_rw_blk.c	Mon Oct  7 11:25:25 2002
+++ b/drivers/block/ll_rw_blk.c	Mon Oct  7 11:25:25 2002
@@ -58,6 +58,7 @@
 static int batch_requests;
 
 unsigned long blk_max_low_pfn, blk_max_pfn;
+atomic_t nr_iowait_tasks = ATOMIC_INIT(0);
 int blk_nohighio = 0;
 
 static struct congestion_state {
@@ -116,6 +117,27 @@
 		atomic_inc(&congestion_states[rw].nr_congested_queues);
 }
 
+/*
+ * This task is about to go to sleep on IO.  Increment nr_iowait_tasks so
+ * that process accounting knows that this is a task in IO wait state.
+ *
+ * But don't do that if it is a deliberate, throttling IO wait (this task
+ * has set its backing_dev_info: the queue against which it should throttle)
+ */
+void io_schedule(void)
+{
+	atomic_inc(&nr_iowait_tasks);
+	schedule();
+	atomic_dec(&nr_iowait_tasks);
+}
+
+void io_schedule_timeout(long timeout)
+{
+	atomic_inc(&nr_iowait_tasks);
+	schedule_timeout(timeout);
+	atomic_dec(&nr_iowait_tasks);
+}
+
 /**
  * bdev_get_queue: - return the queue that matches the given device
  * @bdev:    device
@@ -1123,7 +1145,7 @@
 	if (blk_queue_tagged(q))
 		blk_queue_free_tags(q);
 
-	elevator_exit(q, &q->elevator);
+	elevator_exit(q);
 
 	memset(q, 0, sizeof(*q));
 }
@@ -1205,7 +1227,7 @@
 	if (blk_init_free_list(q))
 		return -ENOMEM;
 
-	if ((ret = elevator_init(q, &q->elevator, iosched_deadline))) {
+	if ((ret = elevator_init(q, &iosched_deadline))) {
 		blk_cleanup_queue(q);
 		return ret;
 	}
@@ -1274,7 +1296,7 @@
 		prepare_to_wait_exclusive(&rl->wait, &wait,
 					TASK_UNINTERRUPTIBLE);
 		if (!rl->count)
-			schedule();
+			io_schedule();
 		finish_wait(&rl->wait, &wait);
 		spin_lock_irq(q->queue_lock);
 		rq = get_request(q, rw);
@@ -1497,7 +1519,7 @@
 	blk_run_queues();
 	prepare_to_wait(&cs->wqh, &wait, TASK_UNINTERRUPTIBLE);
 	if (atomic_read(&cs->nr_congested_queues) != 0)
-		schedule_timeout(timeout);
+		io_schedule_timeout(timeout);
 	finish_wait(&cs->wqh, &wait);
 }
 
@@ -1854,23 +1876,16 @@
  */
 int submit_bio(int rw, struct bio *bio)
 {
-	int count = bio_sectors(bio);
+	int count = bio_sectors(bio) >> 1;
 
-	/*
-	 * do some validity checks...
-	 */
 	BUG_ON(!bio->bi_end_io);
-
 	BIO_BUG_ON(!bio->bi_size);
 	BIO_BUG_ON(!bio->bi_io_vec);
-
 	bio->bi_rw = rw;
-
 	if (rw & WRITE)
-		kstat.pgpgout += count;
+		mod_page_state(pgpgout, count);
 	else
-		kstat.pgpgin += count;
-
+		mod_page_state(pgpgin, count);
 	generic_make_request(bio);
 	return 1;
 }
diff -Nru a/drivers/block/loop.c b/drivers/block/loop.c
--- a/drivers/block/loop.c	Mon Oct  7 11:25:35 2002
+++ b/drivers/block/loop.c	Mon Oct  7 11:25:35 2002
@@ -83,7 +83,7 @@
 
 static int max_loop = 8;
 static struct loop_device *loop_dev;
-static struct gendisk *disks;
+static struct gendisk **disks;
 static devfs_handle_t devfs_handle;      /*  For the directory */
 
 /*
@@ -159,9 +159,7 @@
 static void figure_loop_size(struct loop_device *lo)
 {
 	loff_t size = lo->lo_backing_file->f_dentry->d_inode->i_size;
-
-	set_capacity(disks + lo->lo_number,
-		     (size - lo->lo_offset) >> 9);
+	set_capacity(disks[lo->lo_number], (size - lo->lo_offset) >> 9);
 }
 
 static inline int lo_do_transfer(struct loop_device *lo, int cmd, char *rbuf,
@@ -809,7 +807,7 @@
 	memset(lo->lo_encrypt_key, 0, LO_KEY_SIZE);
 	memset(lo->lo_name, 0, LO_NAME_SIZE);
 	invalidate_bdev(bdev, 0);
-	set_capacity(disks + lo->lo_number, 0);
+	set_capacity(disks[lo->lo_number], 0);
 	filp->f_dentry->d_inode->i_mapping->gfp_mask = gfp;
 	lo->lo_state = Lo_unbound;
 	fput(filp);
@@ -1048,20 +1046,25 @@
 	if (!loop_dev)
 		return -ENOMEM;
 
-	disks = kmalloc(max_loop * sizeof(struct gendisk), GFP_KERNEL);
+	disks = kmalloc(max_loop * sizeof(struct gendisk *), GFP_KERNEL);
 	if (!disks)
 		goto out_mem;
 
 	for (i = 0; i < max_loop; i++) {
+		disks[i] = alloc_disk();
+		if (!disks[i])
+			goto out_mem2;
+	}
+
+	for (i = 0; i < max_loop; i++) {
 		struct loop_device *lo = &loop_dev[i];
-		struct gendisk *disk = disks + i;
+		struct gendisk *disk = disks[i];
 		memset(lo, 0, sizeof(struct loop_device));
 		init_MUTEX(&lo->lo_ctl_mutex);
 		init_MUTEX_LOCKED(&lo->lo_sem);
 		init_MUTEX_LOCKED(&lo->lo_bh_mutex);
 		lo->lo_number = i;
 		spin_lock_init(&lo->lo_lock);
-		memset(disk, 0, sizeof(struct gendisk));
 		disk->major = LOOP_MAJOR;
 		disk->first_minor = i;
 		disk->fops = &lo_fops;
@@ -1074,6 +1077,9 @@
 	printk(KERN_INFO "loop: loaded (max %d devices)\n", max_loop);
 	return 0;
 
+out_mem2:
+	while (i--)
+		put_disk(disks[i]);
 out_mem:
 	kfree(disks);
 	kfree(loop_dev);
@@ -1084,8 +1090,10 @@
 void loop_exit(void) 
 {
 	int i;
-	for (i = 0; i < max_loop; i++)
-		del_gendisk(disks + i);
+	for (i = 0; i < max_loop; i++) {
+		del_gendisk(disks[i]);
+		put_disk(disks[i]);
+	}
 	devfs_unregister(devfs_handle);
 	if (unregister_blkdev(MAJOR_NR, "loop"))
 		printk(KERN_WARNING "loop: cannot unregister blkdev\n");
diff -Nru a/drivers/block/nbd.c b/drivers/block/nbd.c
--- a/drivers/block/nbd.c	Mon Oct  7 11:25:27 2002
+++ b/drivers/block/nbd.c	Mon Oct  7 11:25:27 2002
@@ -444,15 +444,15 @@
 			temp >>= 1;
 		}
 		nbd_bytesizes[dev] &= ~(nbd_blksizes[dev]-1); 
-		set_capacity(&lo->disk, nbd_bytesizes[dev] >> 9);
+		set_capacity(lo->disk, nbd_bytesizes[dev] >> 9);
 		return 0;
 	case NBD_SET_SIZE:
 		nbd_bytesizes[dev] = arg & ~(nbd_blksizes[dev]-1); 
-		set_capacity(&lo->disk, nbd_bytesizes[dev] >> 9);
+		set_capacity(lo->disk, nbd_bytesizes[dev] >> 9);
 		return 0;
 	case NBD_SET_SIZE_BLOCKS:
 		nbd_bytesizes[dev] = ((u64) arg) << nbd_blksize_bits[dev]; 
-		set_capacity(&lo->disk, nbd_bytesizes[dev] >> 9);
+		set_capacity(lo->disk, nbd_bytesizes[dev] >> 9);
 		return 0;
 	case NBD_DO_IT:
 		if (!lo->file)
@@ -498,6 +498,7 @@
 
 static int __init nbd_init(void)
 {
+	int err = -ENOMEM;
 	int i;
 
 	if (sizeof(struct nbd_request) != 28) {
@@ -505,17 +506,25 @@
 		return -EIO;
 	}
 
+	for (i = 0; i < MAX_NBD; i++) {
+		struct gendisk *disk = alloc_disk();
+		if (!disk)
+			goto out;
+		nbd_dev[i].disk = disk;
+	}
+
 	if (register_blkdev(MAJOR_NR, "nbd", &nbd_fops)) {
 		printk("Unable to get major number %d for NBD\n",
 		       MAJOR_NR);
-		return -EIO;
+		err = -EIO;
+		goto out;
 	}
 #ifdef MODULE
 	printk("nbd: registered device at major %d\n", MAJOR_NR);
 #endif
 	blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), do_nbd_request, &nbd_lock);
 	for (i = 0; i < MAX_NBD; i++) {
-		struct gendisk *disk = &nbd_dev[i].disk;
+		struct gendisk *disk = nbd_dev[i].disk;
 		nbd_dev[i].refcnt = 0;
 		nbd_dev[i].file = NULL;
 		nbd_dev[i].magic = LO_MAGIC;
@@ -541,13 +550,19 @@
 			       &nbd_fops, NULL);
 
 	return 0;
+out:
+	while (i--)
+		put_disk(nbd_dev[i].disk);
+	return err;
 }
 
 static void __exit nbd_cleanup(void)
 {
 	int i;
-	for (i = 0; i < MAX_NBD; i++)
-		del_gendisk(&nbd_dev[i].disk);
+	for (i = 0; i < MAX_NBD; i++) {
+		del_gendisk(nbd_dev[i].disk);
+		put_disk(nbd_dev[i].disk);
+	}
 	devfs_unregister (devfs_handle);
 	blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR));
 
diff -Nru a/drivers/block/paride/pcd.c b/drivers/block/paride/pcd.c
--- a/drivers/block/paride/pcd.c	Mon Oct  7 11:25:33 2002
+++ b/drivers/block/paride/pcd.c	Mon Oct  7 11:25:34 2002
@@ -222,7 +222,7 @@
 	int present;		/* does this unit exist ? */
 	char *name;		/* pcd0, pcd1, etc */
 	struct cdrom_device_info info;	/* uniform cdrom interface */
-	struct gendisk disk;
+	struct gendisk *disk;
 };
 
 struct pcd_unit pcd[PCD_UNITS];
@@ -281,7 +281,10 @@
 
 	pcd_drive_count = 0;
 	for (unit = 0, cd = pcd; unit < PCD_UNITS; unit++, cd++) {
-		struct gendisk *disk = &cd->disk;
+		struct gendisk *disk = alloc_disk();
+		if (!disk)
+			continue;
+		cd->disk = disk;
 		cd->pi = &cd->pia;
 		cd->present = 0;
 		cd->last_sense = 0;
@@ -680,7 +683,7 @@
 		cd = pcd;
 		if (pi_init(cd->pi, 1, -1, -1, -1, -1, -1, pcd_buffer,
 			    PI_PCD, verbose, cd->name)) {
-			if (!pcd_probe(cd, -1, id)) {
+			if (!pcd_probe(cd, -1, id) && cd->disk) {
 				cd->present = 1;
 				k++;
 			} else
@@ -695,7 +698,7 @@
 				     conf[D_UNI], conf[D_PRO], conf[D_DLY],
 				     pcd_buffer, PI_PCD, verbose, cd->name)) 
 				continue;
-			if (!pcd_probe(cd, conf[D_SLV], id)) {
+			if (!pcd_probe(cd, conf[D_SLV], id) && cd->disk) {
 				cd->present = 1;
 				k++;
 			} else
@@ -706,6 +709,8 @@
 		return 0;
 
 	printk("%s: No CD-ROM drive found\n", name);
+	for (unit = 0, cd = pcd; unit < PCD_UNITS; unit++, cd++)
+		put_disk(cd->disk);
 	return -1;
 }
 
@@ -925,14 +930,15 @@
 
 	if (register_blkdev(MAJOR_NR, name, &pcd_bdops)) {
 		printk("pcd: unable to get major number %d\n", MAJOR_NR);
+		for (unit = 0, cd = pcd; unit < PCD_UNITS; unit++, cd++)
+			put_disk(cd->disk);
 		return -1;
 	}
 
 	for (unit = 0, cd = pcd; unit < PCD_UNITS; unit++, cd++) {
 		if (cd->present) {
-			struct gendisk *disk = &cd->disk;
 			register_cdrom(&cd->info);
-			add_disk(disk);
+			add_disk(cd->disk);
 		}
 	}
 
@@ -948,10 +954,11 @@
 
 	for (unit = 0, cd = pcd; unit < PCD_UNITS; unit++, cd++) {
 		if (cd->present) {
-			del_gendisk(&cd->disk);
+			del_gendisk(cd->disk);
 			pi_release(cd->pi);
 			unregister_cdrom(&cd->info);
 		}
+		put_disk(cd->disk);
 	}
 	unregister_blkdev(MAJOR_NR, name);
 }
diff -Nru a/drivers/block/paride/pd.c b/drivers/block/paride/pd.c
--- a/drivers/block/paride/pd.c	Mon Oct  7 11:25:36 2002
+++ b/drivers/block/paride/pd.c	Mon Oct  7 11:25:36 2002
@@ -276,7 +276,7 @@
 	int alt_geom;
 	int present;
 	char name[PD_NAMELEN];	/* pda, pdb, etc ... */
-	struct gendisk gd;
+	struct gendisk *gd;
 };
 
 struct pd_unit pd[PD_UNITS];
@@ -287,8 +287,6 @@
 static int pd_check_media(kdev_t dev);
 static void pd_eject(struct pd_unit *disk);
 
-/*  'unit' must be defined in all functions - either as a local or a param */
-
 static char pd_scratch[512];	/* scratch block buffer */
 
 /* the variables below are used mainly in the I/O request engine, which
@@ -368,11 +366,8 @@
 	 unsigned int cmd, unsigned long arg)
 {
 	struct hd_geometry *geo = (struct hd_geometry *) arg;
-	int err, unit = DEVICE_NR(inode->i_rdev);
-	struct pd_unit *disk = pd + unit;
-
-	if (!disk->present)
-		return -ENODEV;
+	struct hd_geometry g;
+	struct pd_unit *disk = pd + DEVICE_NR(inode->i_rdev);
 
 	switch (cmd) {
 	case CDROMEJECT:
@@ -380,23 +375,18 @@
 			pd_eject(disk);
 		return 0;
 	case HDIO_GETGEO:
-		if (!geo)
-			return -EINVAL;
-		err = verify_area(VERIFY_WRITE, geo, sizeof (*geo));
-		if (err)
-			return err;
-
 		if (disk->alt_geom) {
-			put_user(disk->capacity / (PD_LOG_HEADS * PD_LOG_SECTS),
-				 (short *) &geo->cylinders);
-			put_user(PD_LOG_HEADS, (char *) &geo->heads);
-			put_user(PD_LOG_SECTS, (char *) &geo->sectors);
+			g.heads = PD_LOG_HEADS;
+			g.sectors = PD_LOG_SECTS;
+			g.cylinders = disk->capacity / (g.heads * g.sectors);
 		} else {
-			put_user(disk->cylinders, (short *) &geo->cylinders);
-			put_user(disk->heads, (char *) &geo->heads);
-			put_user(disk->sectors, (char *) &geo->sectors);
+			g.heads = disk->heads;
+			g.sectors = disk->sectors;
+			g.cylinders = disk->cylinders;
 		}
-		put_user(get_start_sect(inode->i_bdev), (long *) &geo->start);
+		g.start = get_start_sect(inode->i_bdev);
+		if (copy_to_user(geo, &g, sizeof(struct hd_geometry)))
+			return -EFAULT;
 		return 0;
 	default:
 		return -EINVAL;
@@ -418,8 +408,6 @@
 {
 	int r, unit = DEVICE_NR(dev);
 	struct pd_unit *disk = pd + unit;
-	if (unit >= PD_UNITS || (!disk->present))
-		return -ENODEV;
 	if (!disk->removable)
 		return 0;
 	pd_media_check(disk);
@@ -432,12 +420,10 @@
 {
 	int unit = DEVICE_NR(dev);
 	struct pd_unit *disk = pd + unit;
-	if (unit >= PD_UNITS || !disk->present)
-		return -ENODEV;
 	if (pd_identify(disk))
-		set_capacity(&disk->gd, disk->capacity);
+		set_capacity(disk->gd, disk->capacity);
 	else
-		set_capacity(&disk->gd, 0);
+		set_capacity(disk->gd, 0);
 	return 0;
 }
 
@@ -717,13 +703,20 @@
 	}
 	for (unit = 0, disk = pd; unit < PD_UNITS; unit++, disk++) {
 		if (disk->present) {
-			strcpy(disk->gd.disk_name, disk->name);
-			disk->gd.minor_shift = PD_BITS;
-			disk->gd.fops = &pd_fops;
-			disk->gd.major = major;
-			disk->gd.first_minor = unit << PD_BITS;
-			set_capacity(&disk->gd, disk->capacity);
-			add_disk(&disk->gd);
+			struct gendisk *p = alloc_disk();
+			if (!p) {
+				disk->present = 0;
+				k--;
+				continue;
+			}
+			strcpy(p->disk_name, disk->name);
+			p->minor_shift = PD_BITS;
+			p->fops = &pd_fops;
+			p->major = major;
+			p->first_minor = unit << PD_BITS;
+			set_capacity(p, disk->capacity);
+			disk->gd = p;
+			add_disk(p);
 		}
 	}
 	if (k)
@@ -760,7 +753,7 @@
 	pd_run = pd_req->nr_sectors;
 	pd_count = pd_req->current_nr_sectors;
 	pd_current = pd + unit;
-	if (pd_block + pd_count > get_capacity(&pd_current->gd)) {
+	if (pd_block + pd_count > get_capacity(pd_current->gd)) {
 		end_request(pd_req, 0);
 		goto repeat;
 	}
@@ -942,7 +935,10 @@
 	unregister_blkdev(MAJOR_NR, name);
 	for (unit = 0, disk = pd; unit < PD_UNITS; unit++, disk++) {
 		if (disk->present) {
-			del_gendisk(&disk->gd);
+			struct gendisk *p = disk->gd;
+			disk->gd = NULL;
+			del_gendisk(p);
+			put_disk(p);
 			pi_release(disk->pi);
 		}
 	}
diff -Nru a/drivers/block/paride/pf.c b/drivers/block/paride/pf.c
--- a/drivers/block/paride/pf.c	Mon Oct  7 11:25:36 2002
+++ b/drivers/block/paride/pf.c	Mon Oct  7 11:25:36 2002
@@ -264,7 +264,7 @@
 	int access;		/* count of active opens ... */
 	int present;		/* device present ? */
 	char name[PF_NAMELEN];	/* pf0, pf1, ... */
-	struct gendisk disk;
+	struct gendisk *disk;
 };
 
 struct pf_unit units[PF_UNITS];
@@ -308,7 +308,10 @@
 
 	pf_drive_count = 0;
 	for (unit = 0, pf = units; unit < PF_UNITS; unit++, pf++) {
-		struct gendisk *disk = &pf->disk;
+		struct gendisk *disk = alloc_disk();
+		if (!disk)
+			continue;
+		pf->disk = disk;
 		pf->pi = &pf->pia;
 		pf->media_status = PF_NM;
 		pf->drive = (*drives[unit])[D_SLV];
@@ -355,11 +358,6 @@
 	struct pf_unit *pf = units + unit;
 	sector_t capacity;
 
-	if (unit >= PF_UNITS)
-		return -EINVAL;
-	if (!pf->present)
-		return -ENODEV;
-
 	if (cmd == CDROMEJECT) {
 		if (pf->access == 1) {
 			pf_eject(pf);
@@ -369,7 +367,7 @@
 	}
 	if (cmd != HDIO_GETGEO)
 		return -EINVAL;
-	capacity = get_capacity(&pf->disk);
+	capacity = get_capacity(pf->disk);
 	if (capacity < PF_FD_MAX) {
 		g.cylinders = capacity / (PF_FD_HDS * PF_FD_SPT);
 		g.heads = PF_FD_HDS;
@@ -635,10 +633,10 @@
 		pf->media_status = PF_NM;
 		return;
 	}
-	set_capacity(&pf->disk, xl(buf, 0) + 1);
+	set_capacity(pf->disk, xl(buf, 0) + 1);
 	bs = xl(buf, 4);
 	if (bs != 512) {
-		set_capacity(&pf->disk, 0);
+		set_capacity(pf->disk, 0);
 		if (verbose)
 			printk("%s: Drive %d, LUN %d,"
 			       " unsupported block size %d\n",
@@ -687,7 +685,7 @@
 	else {
 		if (pf->media_status == PF_RO)
 			printk(", RO");
-		printk(", %ld blocks\n", get_capacity(&pf->disk));
+		printk(", %ld blocks\n", get_capacity(pf->disk));
 	}
 	return 0;
 }
@@ -731,7 +729,7 @@
 	if (pf_drive_count == 0) {
 		if (pi_init(pf->pi, 1, -1, -1, -1, -1, -1, pf_scratch, PI_PF,
 			    verbose, pf->name)) {
-			if (!pf_probe(pf)) {
+			if (!pf_probe(pf) && pf->disk) {
 				pf->present = 1;
 				k++;
 			} else
@@ -746,7 +744,7 @@
 			if (pi_init(pf->pi, 0, conf[D_PRT], conf[D_MOD],
 				    conf[D_UNI], conf[D_PRO], conf[D_DLY],
 				    pf_scratch, PI_PF, verbose, pf->name)) {
-				if (!pf_probe(pf)) {
+				if (!pf_probe(pf) && pf->disk) {
 					pf->present = 1;
 					k++;
 				} else
@@ -757,6 +755,8 @@
 		return 0;
 
 	printk("%s: No ATAPI disk detected\n", name);
+	for (pf = units, unit = 0; unit < PF_UNITS; pf++, unit++)
+		put_disk(pf->disk);
 	return -1;
 }
 
@@ -805,7 +805,7 @@
 	pf_count = pf_req->current_nr_sectors;
 
 	if ((unit >= PF_UNITS) ||
-	    (pf_block + pf_count > get_capacity(&pf_current->disk))) {
+	    (pf_block + pf_count > get_capacity(pf_current->disk))) {
 		end_request(pf_req, 0);
 		goto repeat;
 	}
@@ -976,6 +976,8 @@
 
 	if (register_blkdev(MAJOR_NR, name, &pf_fops)) {
 		printk("pf_init: unable to get major number %d\n", major);
+		for (pf = units, unit = 0; unit < PF_UNITS; pf++, unit++)
+			put_disk(pf->disk);
 		return -1;
 	}
 	q = BLK_DEFAULT_QUEUE(MAJOR_NR);
@@ -984,7 +986,7 @@
 	blk_queue_max_hw_segments(q, cluster);
 
 	for (pf = units, unit = 0; unit < PF_UNITS; pf++, unit++) {
-		struct gendisk *disk = &pf->disk;
+		struct gendisk *disk = pf->disk;
 		if (!pf->present)
 			continue;
 		add_disk(disk);
@@ -1000,7 +1002,8 @@
 	for (pf = units, unit = 0; unit < PF_UNITS; pf++, unit++) {
 		if (!pf->present)
 			continue;
-		del_gendisk(&pf->disk);
+		del_gendisk(pf->disk);
+		put_disk(pf->disk);
 		pi_release(pf->pi);
 	}
 }
diff -Nru a/drivers/block/paride/pseudo.h b/drivers/block/paride/pseudo.h
--- a/drivers/block/paride/pseudo.h	Mon Oct  7 11:25:29 2002
+++ b/drivers/block/paride/pseudo.h	Mon Oct  7 11:25:29 2002
@@ -34,7 +34,7 @@
 
 #include <linux/sched.h>
 #include <linux/timer.h>
-#include <linux/tqueue.h>
+#include <linux/workqueue.h>
 
 static void ps_timer_int( unsigned long data);
 static void ps_tq_int( void *data);
@@ -50,7 +50,7 @@
 static spinlock_t ps_spinlock __attribute__((unused)) = SPIN_LOCK_UNLOCKED;
 
 static struct timer_list ps_timer = { function: ps_timer_int };
-static struct tq_struct ps_tq = { routine: ps_tq_int };
+static DECLARE_WORK(ps_tq, ps_tq_int, NULL);
 
 static void ps_set_intr( void (*continuation)(void), 
 			 int (*ready)(void),
@@ -71,7 +71,7 @@
                 disable_hlt();
 #endif
 		ps_tq_active = 1;
-                schedule_task(&ps_tq);
+                schedule_work(&ps_tq);
 	}
 
         if (!ps_timer_active) {
@@ -114,7 +114,7 @@
 #endif
 
         ps_tq_active = 1;
-	schedule_task(&ps_tq);
+	schedule_work(&ps_tq);
         spin_unlock_irqrestore(&ps_spinlock,flags);
 }
 
diff -Nru a/drivers/block/ps2esdi.c b/drivers/block/ps2esdi.c
--- a/drivers/block/ps2esdi.c	Mon Oct  7 11:25:25 2002
+++ b/drivers/block/ps2esdi.c	Mon Oct  7 11:25:25 2002
@@ -140,26 +140,12 @@
 
 static struct block_device_operations ps2esdi_fops =
 {
-	owner:		THIS_MODULE,
-	open:		ps2esdi_open,
-	ioctl:		ps2esdi_ioctl,
+	.owner		= THIS_MODULE,
+	.open		= ps2esdi_open,
+	.ioctl		= ps2esdi_ioctl,
 };
 
-static struct gendisk ps2esdi_gendisk[2] = {
-{
-	major:		MAJOR_NR,
-	disk_name:	"eda",
-	first_minor:	0,
-	minor_shift:	6,
-	fops:		&ps2esdi_fops,
-},{
-	major:		MAJOR_NR,
-	first_minor:	64,
-	disk_name:	"edb",
-	minor_shift:	6,
-	fops:		&ps2esdi_fops,
-}
-};
+static struct gendisk *ps2esdi_gendisk[2];
 
 /* initialization routine called by ll_rw_blk.c   */
 int __init ps2esdi_init(void)
@@ -233,8 +219,10 @@
 	free_irq(PS2ESDI_IRQ, &ps2esdi_gendisk);
 	unregister_blkdev(MAJOR_NR, "ed");
 	blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR));
-	for (i = 0; i < ps2esdi_drives; i++)
-		del_gendisk(ps2esdi_gendisk + i);
+	for (i = 0; i < ps2esdi_drives; i++) {
+		del_gendisk(ps2esdi_gendisk[i]);
+		put_disk(ps2esdi_gendisk[i]);
+	}
 }
 #endif /* MODULE */
 
@@ -431,14 +419,29 @@
 	}
 	blk_queue_max_sectors(BLK_DEFAULT_QUEUE(MAJOR_NR), 128);
 
+	error = -ENOMEM;
 	for (i = 0; i < ps2esdi_drives; i++) {
-		struct gendisk *disk = ps2esdi_gendisk + i;
+		struct gendisk *disk = alloc_disk();
+		if (!disk)
+			goto err_out4;
+		disk->major = MAJOR_NR;
+		disk->first_minor = i<<6;
+		sprintf(disk->disk_name, "ed%c", 'a'+i);
+		disk->minor_shift = 6;
+		disk->fops = &ps2esdi_fops;
+		ps2esdi_gendisk[i] = disk;
+	}
+
+	for (i = 0; i < ps2esdi_drives; i++) {
+		struct gendisk *disk = ps2esdi_gendisk[i];
 		set_capacity(disk, ps2esdi_info[i].head * ps2esdi_info[i].sect *
 				ps2esdi_info[i].cyl);
 		add_disk(disk);
 	}
 	return 0;
-
+err_out4:
+	while (i--)
+		put_disk(ps2esdi_gendisk[i]);
 err_out3:
 	release_region(io_base, 4);
 err_out2:
@@ -504,7 +507,7 @@
 	}			/* check for above 16Mb dmas */
 	else if ((unit < ps2esdi_drives) &&
 	    (CURRENT->sector + CURRENT->current_nr_sectors <=
-	     get_capacity(&ps2esdi_gendisk[unit])) &&
+	     get_capacity(ps2esdi_gendisk[unit])) &&
 	    	CURRENT->flags & REQ_CMD) {
 #if 0
 		printk("%s:got request. device : %d minor : %d command : %d  sector : %ld count : %ld\n",
@@ -533,7 +536,7 @@
 	/* is request is valid */ 
 	else {
 		printk("Grrr. error. ps2esdi_drives: %d, %lu %lu\n", ps2esdi_drives,
-		       CURRENT->sector, get_capacity(&ps2esdi_gendisk[unit]));
+		       CURRENT->sector, get_capacity(ps2esdi_gendisk[unit]));
 		end_request(CURRENT, FAIL);
 	}
 
diff -Nru a/drivers/block/rd.c b/drivers/block/rd.c
--- a/drivers/block/rd.c	Mon Oct  7 11:25:36 2002
+++ b/drivers/block/rd.c	Mon Oct  7 11:25:36 2002
@@ -77,7 +77,7 @@
  */
 
 static unsigned long rd_length[NUM_RAMDISKS];	/* Size of RAM disks in bytes   */
-static struct gendisk rd_disks[NUM_RAMDISKS];
+static struct gendisk *rd_disks[NUM_RAMDISKS];
 static devfs_handle_t devfs_handle;
 static struct block_device *rd_bdev[NUM_RAMDISKS];/* Protected device data */
 
@@ -213,7 +213,7 @@
 		kunmap(vec->bv_page);
 
 		if (rw == READ) {
-			flush_dcache_page(page);
+			flush_dcache_page(sbh->b_page);
 		} else {
 			SetPageDirty(page);
 		}
@@ -310,13 +310,7 @@
 #ifdef CONFIG_BLK_DEV_INITRD
 
 static struct block_device_operations rd_bd_op;
-static struct gendisk initrd_disk = {
-	.major = MAJOR_NR,
-	.first_minor = INITRD_MINOR,
-	.minor_shift = 0,
-	.fops = &rd_bd_op,	
-	.disk_name = "initrd"
-};
+static struct gendisk *initrd_disk;
 
 static ssize_t initrd_read(struct file *file, char *buf,
 			   size_t count, loff_t *ppos)
@@ -340,7 +334,7 @@
 	spin_lock(&initrd_users_lock);
 	if (!--initrd_users) {
 		spin_unlock(&initrd_users_lock);
-		del_gendisk(&initrd_disk);
+		del_gendisk(initrd_disk);
 		free_initrd_mem(initrd_start, initrd_end);
 		initrd_start = 0;
 	} else {
@@ -412,8 +406,12 @@
 			invalidate_bdev(bdev, 1);
 			blkdev_put(bdev, BDEV_FILE);
 		}
-		del_gendisk(rd_disks + i);
+		del_gendisk(rd_disks[i]);
+		put_disk(rd_disks[i]);
 	}
+#ifdef CONFIG_BLK_DEV_INITRD
+	put_disk(initrd_disk);
+#endif
 
 	devfs_unregister (devfs_handle);
 	unregister_blkdev( MAJOR_NR, "ramdisk" );
@@ -423,6 +421,7 @@
 static int __init rd_init (void)
 {
 	int i;
+	int err = -ENOMEM;
 
 	if (rd_blocksize > PAGE_SIZE || rd_blocksize < 512 ||
 	    (rd_blocksize & (rd_blocksize-1))) {
@@ -431,15 +430,32 @@
 		rd_blocksize = BLOCK_SIZE;
 	}
 
+#ifdef CONFIG_BLK_DEV_INITRD
+	initrd_disk = alloc_disk();
+	if (!initrd_disk)
+		return -ENOMEM;
+	initrd_disk->major = MAJOR_NR;
+	initrd_disk->first_minor = INITRD_MINOR;
+	initrd_disk->minor_shift = 0;
+	initrd_disk->fops = &rd_bd_op;	
+	sprintf(initrd_disk->disk_name, "initrd");
+#endif
+	for (i = 0; i < NUM_RAMDISKS; i++) {
+		rd_disks[i] = alloc_disk();
+		if (!rd_disks[i])
+			goto out;
+	}
+
 	if (register_blkdev(MAJOR_NR, "ramdisk", &rd_bd_op)) {
 		printk("RAMDISK: Could not get major %d", MAJOR_NR);
-		return -EIO;
+		err = -EIO;
+		goto out;
 	}
 
 	blk_queue_make_request(BLK_DEFAULT_QUEUE(MAJOR_NR), &rd_make_request);
 
 	for (i = 0; i < NUM_RAMDISKS; i++) {
-		struct gendisk *disk = rd_disks + i;
+		struct gendisk *disk = rd_disks[i];
 		/* rd_size is given in kB */
 		rd_length[i] = rd_size << 10;
 		disk->major = MAJOR_NR;
@@ -456,11 +472,12 @@
 			       &rd_bd_op, NULL);
 
 	for (i = 0; i < NUM_RAMDISKS; i++)
-		add_disk(rd_disks + i);
+		add_disk(rd_disks[i]);
 
 #ifdef CONFIG_BLK_DEV_INITRD
 	/* We ought to separate initrd operations here */
-	add_disk(&initrd_disk);
+	set_capacity(initrd_disk, (initrd_end-initrd_start+511)>>9);
+	add_disk(initrd_disk);
 	devfs_register(devfs_handle, "initrd", DEVFS_FL_DEFAULT, MAJOR_NR,
 			INITRD_MINOR, S_IFBLK | S_IRUSR, &rd_bd_op, NULL);
 #endif
@@ -471,6 +488,13 @@
 	       NUM_RAMDISKS, rd_size, rd_blocksize);
 
 	return 0;
+out:
+	while (i--)
+		put_disk(rd_disks[i]);
+#ifdef CONFIG_BLK_DEV_INITRD
+	put_disk(initrd_disk);
+#endif
+	return err;
 }
 
 module_init(rd_init);
diff -Nru a/drivers/block/swim3.c b/drivers/block/swim3.c
--- a/drivers/block/swim3.c	Mon Oct  7 11:25:31 2002
+++ b/drivers/block/swim3.c	Mon Oct  7 11:25:31 2002
@@ -38,7 +38,7 @@
 #include <linux/blk.h>
 #include <linux/devfs_fs_kernel.h>
 
-static struct gendisk disks[2];
+static struct gendisk *disks[2];
 
 #define MAX_FLOPPIES	2
 
@@ -1014,6 +1014,7 @@
 int swim3_init(void)
 {
 	struct device_node *swim;
+	int err = -ENOMEM;
 	int i;
 
 	floppy_devfs_handle = devfs_mk_dir(NULL, "floppy", NULL);
@@ -1032,26 +1033,37 @@
 		swim = swim->next;
 	}
 
-	if (floppy_count > 0)
-	{
-		if (register_blkdev(MAJOR_NR, "fd", &floppy_fops)) {
-			printk(KERN_ERR "Unable to get major %d for floppy\n",
-			       MAJOR_NR);
-			return -EBUSY;
-		}
-		blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), do_fd_request, &swim3_lock);
-		for (i = 0; i < floppy_count; i++) {
-			struct gendisk *disk = disks + i;
-			disk->major = MAJOR_NR;
-			disk->first_minor = i;
-			disk->fops = &floppy_fops;
-			sprintf(disk->disk_name, "fd%d", i);
-			set_capacity(disk, 2880);
-			add_disk(disk);
-		}
-	}
+	if (!floppy_count)
+		return -ENODEV;
 
+	for (i = 0; i < floppy_count; i++) {
+		disks[i] = alloc_disk();
+		if (!disks[i])
+			goto out;
+	}
+
+	if (register_blkdev(MAJOR_NR, "fd", &floppy_fops)) {
+		printk(KERN_ERR"Unable to get major %d for floppy\n", MAJOR_NR);
+		err = -EBUSY;
+		goto out;
+	}
+	blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), do_fd_request, &swim3_lock);
+	for (i = 0; i < floppy_count; i++) {
+		struct gendisk *disk = disks[i];
+		disk->major = MAJOR_NR;
+		disk->first_minor = i;
+		disk->fops = &floppy_fops;
+		sprintf(disk->disk_name, "fd%d", i);
+		set_capacity(disk, 2880);
+		add_disk(disk);
+	}
 	return 0;
+
+out:
+	while (i--)
+		put_disk(disks[i]);
+	/* shouldn't we do something with results of swim_add_device()? */
+	return err;
 }
 
 static int swim3_add_device(struct device_node *swim)
@@ -1117,8 +1129,6 @@
 */
 
 	init_timer(&fs->timeout);
-
-	do_floppy = NULL;
 
 	printk(KERN_INFO "fd%d: SWIM3 floppy controller %s\n", floppy_count,
 		mediabay ? "in media bay" : "");
diff -Nru a/drivers/block/swim_iop.c b/drivers/block/swim_iop.c
--- a/drivers/block/swim_iop.c	Mon Oct  7 11:25:25 2002
+++ b/drivers/block/swim_iop.c	Mon Oct  7 11:25:25 2002
@@ -82,9 +82,6 @@
 static int floppy_count;
 
 static struct floppy_state floppy_states[MAX_FLOPPIES];
-
-static struct gendisk disks[2];
-
 static spinlock_t swim_iop_lock = SPIN_LOCK_UNLOCKED;
 
 static char *drive_names[7] = {
@@ -190,9 +187,10 @@
 	}
 	printk("SWIM-IOP: detected %d installed drives.\n", floppy_count);
 
-	do_floppy = NULL;
 	for (i = 0; i < floppy_count; i++) {
-		struct gendisk *disk = disks + i;
+		struct gendisk *disk = alloc_disk();
+		if (!disk)
+			continue;
 		disk->major = MAJOR_NR;
 		disk->first_minor = i;
 		disk->fops = &floppy_fops;
diff -Nru a/drivers/block/umem.c b/drivers/block/umem.c
--- a/drivers/block/umem.c	Mon Oct  7 11:25:39 2002
+++ b/drivers/block/umem.c	Mon Oct  7 11:25:39 2002
@@ -161,7 +161,7 @@
 
 static int num_cards = 0;
 
-static struct gendisk mm_gendisk[MM_MAXCARDS];
+static struct gendisk *mm_gendisk[MM_MAXCARDS];
 
 static void check_batteries(struct cardinfo *card);
 
@@ -813,7 +813,7 @@
 static int mm_revalidate(kdev_t i_rdev)
 {
 	int card_number = DEVICE_NR(i_rdev);
-	set_capacity(mm_gendisk + card_number, cards[card_number].mm_size << 1);
+	set_capacity(mm_gendisk[card_number], cards[card_number].mm_size << 1);
 	return 0;
 }
 /*
@@ -1186,11 +1186,17 @@
 		printk(KERN_ERR "MM: Could not register block device\n");
 		return -EIO;
 	}
+
+	for (i = 0; i < num_cards; i++) {
+		mm_gendisk[i] = alloc_disk();
+		if (!mm_gendisk[i])
+			goto out;
+	}
 	devfs_handle = devfs_mk_dir(NULL, "umem", NULL);
 
 	blk_dev[MAJOR_NR].queue = mm_queue_proc;
 	for (i = 0; i < num_cards; i++) {
-		struct gendisk *disk = mm_gendisk + i;
+		struct gendisk *disk = mm_gendisk[i];
 		sprintf(disk->disk_name, "umem%c", 'a'+i);
 		spin_lock_init(&cards[i].lock);
 		disk->major = major_nr;
@@ -1205,6 +1211,12 @@
 	printk("MM: desc_per_page = %ld\n", DESC_PER_PAGE);
 /* printk("mm_init: Done. 10-19-01 9:00\n"); */
 	return 0;
+
+out:
+	unregister_blkdev(MAJOR_NR, "umem");
+	while (i--)
+		put_disk(mm_gendisk[i]);
+	return -ENOMEM;
 }
 /*
 -----------------------------------------------------------------------------------
@@ -1217,8 +1229,10 @@
 
 	del_battery_timer();
 
-	for (i=0; i < num_cards ; i++)
-		del_gendisk(mm_gendisk + i);
+	for (i=0; i < num_cards ; i++) {
+		del_gendisk(mm_gendisk[i]);
+		put_disk(mm_gendisk[i]);
+	}
 	if (devfs_handle)
 		devfs_unregister(devfs_handle);
 	devfs_handle = NULL;
diff -Nru a/drivers/block/xd.c b/drivers/block/xd.c
--- a/drivers/block/xd.c	Mon Oct  7 11:25:37 2002
+++ b/drivers/block/xd.c	Mon Oct  7 11:25:37 2002
@@ -58,6 +58,9 @@
 
 #include "xd.h"
 
+static void __init do_xd_setup (int *integers);
+static int xd[5] = { -1,-1,-1,-1, };
+
 #define XD_DONT_USE_DMA		0  /* Initial value. may be overriden using
 				      "nodma" module option */
 #define XD_INIT_DISK_DELAY	(30*HZ/1000)  /* 30 ms delay during disk initialization */
@@ -123,23 +126,7 @@
 
 static spinlock_t xd_lock = SPIN_LOCK_UNLOCKED;
 
-extern struct block_device_operations xd_fops;
-
-static struct gendisk xd_gendisk[2] = {
-{
-	.major =	MAJOR_NR,
-	.first_minor =	0,
-	.disk_name =	"xda",
-	.minor_shift =	6,
-	.fops =		&xd_fops,
-},{
-	.major =	MAJOR_NR,
-	.first_minor =	64,
-	.disk_name =	"xdb",
-	.minor_shift =	6,
-	.fops =		&xd_fops,
-}
-};
+static struct gendisk *xd_gendisk[2];
 
 static struct block_device_operations xd_fops = {
 	owner:		THIS_MODULE,
@@ -161,10 +148,19 @@
 static devfs_handle_t devfs_handle = NULL;
 
 /* xd_init: register the block device number and set up pointer tables */
-int __init xd_init (void)
+static int __init xd_init(void)
 {
 	u_char i,controller;
 	unsigned int address;
+	int err;
+
+#ifdef MODULE
+	for (i = 4; i > 0; i--)
+		if(((xd[i] = xd[i-1]) >= 0) && !count)
+			count = i;
+	if((xd[0] = count))
+		do_xd_setup(xd);
+#endif
 
 	init_timer (&xd_watchdog_int); xd_watchdog_int.function = xd_watchdog;
 
@@ -175,9 +171,10 @@
 		return -ENOMEM;
 	}
 
+	err = -EBUSY;
 	if (register_blkdev(MAJOR_NR,"xd",&xd_fops)) {
 		printk("xd: Unable to get major number %d\n",MAJOR_NR);
-		return -1;
+		goto out1;
 	}
 	devfs_handle = devfs_mk_dir (NULL, "xd", NULL);
 	blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), do_xd_request, &xd_lock);
@@ -188,7 +185,7 @@
 		if (!request_region(xd_iobase,4,"xd")) {
 			printk("xd: Ports at 0x%x are not available\n",
 				xd_iobase);
-			return -EBUSY;
+			goto out2;
 		}
 		if (controller)
 			xd_sigs[controller].init_controller(address);
@@ -203,31 +200,65 @@
 
 	}
 
+	err = -ENODEV;
 	if (!xd_drives)
-		return -ENODEV;
+		goto out3;
+
+	for (i = 0; i < xd_drives; i++) {
+		struct gendisk *disk = alloc_disk();
+		if (!disk)
+			goto Enomem;
+		disk->major = MAJOR_NR;
+		disk->first_minor = i<<6;
+		disk->minor_shift = 6;
+		sprintf(disk->disk_name, "xd%c", i+'a');
+		disk->fops = &xd_fops;
+		xd_gendisk[i] = disk;
+	}
 
+	err = -EBUSY;
 	if (request_irq(xd_irq,xd_interrupt_handler, 0, "XT hard disk", NULL)) {
 		printk("xd: unable to get IRQ%d\n",xd_irq);
-		return -EBUSY;
+		goto out4;
 	}
 
 	if (request_dma(xd_dma,"xd")) {
 		printk("xd: unable to get DMA%d\n",xd_dma);
-		free_irq(xd_irq, NULL);
-		return -EBUSY;
+		goto out5;
 	}
 
 	/* xd_maxsectors depends on controller - so set after detection */
 	blk_queue_max_sectors(BLK_DEFAULT_QUEUE(MAJOR_NR), xd_maxsectors);
 
 	for (i = 0; i < xd_drives; i++) {
-		struct gendisk *disk = xd_gendisk + i;
+		struct gendisk *disk = xd_gendisk[i];
 		set_capacity(disk, xd_info[i].heads * xd_info[i].cylinders *
 				xd_info[i].sectors);
 		add_disk(disk);
 	}
 
 	return 0;
+
+out5:
+	free_irq(xd_irq, NULL);
+out4:
+	for (i = 0; i < xd_drives; i++)
+		put_disk(xd_gendisk[i]);
+out3:
+	release_region(xd_iobase,4);
+out2:
+	blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR));
+	unregister_blkdev(MAJOR_NR, "xd");
+out1:
+	if (xd_dma_buffer)
+		xd_dma_mem_free((unsigned long)xd_dma_buffer,
+				xd_maxsectors * 0x200);
+	return err;
+Enomem:
+	err = -ENOMEM;
+	while (i--)
+		put_disk(xd_gendisk[i]);
+	goto out3;
 }
 
 /* xd_detect: scan the possible BIOS ROM locations for the signature strings */
@@ -282,7 +313,7 @@
 		if (unit < xd_drives
 		    && (CURRENT->flags & REQ_CMD)
 		    && CURRENT->sector + CURRENT->nr_sectors
-		         <= get_capacity(xd_gendisk + unit)) {
+		         <= get_capacity(xd_gendisk[unit])) {
 			block = CURRENT->sector;
 			count = CURRENT->nr_sectors;
 
@@ -980,7 +1011,7 @@
 }
 
 /* xd_setup: initialise controller from command line parameters */
-void __init do_xd_setup (int *integers)
+static void __init do_xd_setup (int *integers)
 {
 	switch (integers[0]) {
 		case 4: if (integers[4] < 0)
@@ -1023,7 +1054,6 @@
 
 
 #ifdef MODULE
-static int xd[5] = { -1,-1,-1,-1, };
 
 MODULE_PARM(xd, "1-4i");
 MODULE_PARM(xd_geo, "3-6i");
@@ -1031,44 +1061,16 @@
 
 MODULE_LICENSE("GPL");
 
-static void xd_done (void)
+void cleanup_module(void)
 {
 	int i;
-	for (i = 0; i < xd_drives; i++)
-		del_gendisk(xd_gendisk + i);
+	unregister_blkdev(MAJOR_NR, "xd");
+	for (i = 0; i < xd_drives; i++) {
+		del_gendisk(xd_gendisk[i]);
+		put_disk(xd_gendisk[i]);
+	}
 	blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR));
 	release_region(xd_iobase,4);
-}
-
-int init_module(void)
-{
-	int i,count = 0;
-	int error;
-
-	for (i = 4; i > 0; i--)
-		if(((xd[i] = xd[i-1]) >= 0) && !count)
-			count = i;
-	if((xd[0] = count))
-		do_xd_setup(xd);
-
-	error = xd_init();
-	if (error) return error;
-
-	printk(KERN_INFO "XD: Loaded as a module.\n");
-	if (!xd_drives) {
-		/* no drives detected - unload module */
-		unregister_blkdev(MAJOR_NR, "xd");
-		xd_done();
-		return (-1);
-	}
-        
-	return 0;
-}
-
-void cleanup_module(void)
-{
-	unregister_blkdev(MAJOR_NR, "xd");
-	xd_done();
 	devfs_unregister (devfs_handle);
 	if (xd_drives) {
 		free_irq(xd_irq, NULL);
@@ -1107,4 +1109,6 @@
 __setup ("xd_geo=", xd_manual_geo_init);
 
 #endif /* MODULE */
+
+module_init(xd_init)
 
diff -Nru a/drivers/block/z2ram.c b/drivers/block/z2ram.c
--- a/drivers/block/z2ram.c	Mon Oct  7 11:25:36 2002
+++ b/drivers/block/z2ram.c	Mon Oct  7 11:25:36 2002
@@ -71,13 +71,7 @@
 static spinlock_t z2ram_lock = SPIN_LOCK_UNLOCKED;
 
 static struct block_device_operations z2_fops;
-static struct gendisk z2ram_gendisk = {
-	.major = MAJOR_NR,
-	.first_minor = 0,
-	.minor_shift = 0,
-	.fops = &z2_fops,
-	.disk_name = "z2ram"
-};
+static struct gendisk *z2ram_gendisk;
 
 static void
 do_z2_request( request_queue_t * q )
@@ -320,7 +314,7 @@
 
 	current_device = device;
 	z2ram_size <<= Z2RAM_CHUNKSHIFT;
-	set_capacity(&z2ram_gendisk, z2ram_size >> 9;   
+	set_capacity(z2ram_gendisk, z2ram_size >> 9;   
     }
 
     return 0;
@@ -355,7 +349,7 @@
 {
 	if (minor > Z2MINOR_COUNT)
 		return NULL;
-	return &z2ram_gendisk;
+	return z2ram_gendisk;
 }
 
 int __init 
@@ -371,9 +365,19 @@
 	    MAJOR_NR );
 	return -EBUSY;
     }
+    z2ram_gendisk = alloc_disk();
+    if (!z2ram_gendisk) {
+	unregister_blkdev( MAJOR_NR, DEVICE_NAME );
+	return -ENOMEM;
+    }
+    z2ram_gendisk->major = MAJOR_NR;
+    z2ram_gendisk->first_minor = 0;
+    z2ram_gendisk->minor_shift = 0;
+    z2ram_gendisk->fops = &z2_fops;
+    sprintf(z2ram_gendisk->disk_name, "z2ram");
 
     blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), do_z2_request, &z2ram_lock);
-    add_disk(&z2ram_gendisk);
+    add_disk(z2ram_gendisk);
     blk_set_probe(MAJOR_NR, z2_find);
 
     return 0;
@@ -406,7 +410,8 @@
     if ( unregister_blkdev( MAJOR_NR, DEVICE_NAME ) != 0 )
 	printk( KERN_ERR DEVICE_NAME ": unregister of device failed\n");
 
-    del_gendisk(&z2ram_gendisk);
+    del_gendisk(z2ram_gendisk);
+    put_disk(z2ram_gendisk);
     blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR));
 
     if ( current_device != -1 )
diff -Nru a/drivers/bluetooth/Config.help b/drivers/bluetooth/Config.help
--- a/drivers/bluetooth/Config.help	Mon Oct  7 11:25:32 2002
+++ b/drivers/bluetooth/Config.help	Mon Oct  7 11:25:32 2002
@@ -12,11 +12,20 @@
 HCI UART (H4) protocol support 
 CONFIG_BLUEZ_HCIUART_H4
   UART (H4) is serial protocol for communication between Bluetooth 
-  device and host. This protocol is required for most UART based 
-  Bluetooth device (including PCMCIA and CF). 
+  device and host. This protocol is required for most Bluetooth devices 
+  with UART interface, including PCMCIA and CF cards. 
 
   Say Y here to compile support for HCI UART (H4) protocol.
 
+HCI BCSP protocol support 
+CONFIG_BLUEZ_HCIUART_BCSP
+  BCSP (BlueCore Serial Protocol) is serial protocol for communication 
+  between Bluetooth device and host. This protocol is required for non
+  USB Bluetooth devices based on CSR BlueCore chip, including PCMCIA and 
+  CF cards.
+
+  Say Y here to compile support for HCI BCSP protocol.
+
 HCI USB driver
 CONFIG_BLUEZ_HCIUSB
   Bluetooth HCI USB driver.
@@ -25,15 +34,6 @@
 
   Say Y here to compile support for Bluetooth USB devices into the
   kernel or say M to compile it as module (hci_usb.o).
-
-HCI USB firmware download support
-CONFIG_BLUEZ_USB_FW_LOAD
-  Firmware download support for Bluetooth USB devices.
-  This support is required for devices like Broadcom BCM2033.
-
-  HCI USB driver uses external firmware downloader program provided 
-  in BlueFW package.
-  For more information, see <http://bluez.sf.net/>.
 
 HCI USB zero packet support
 CONFIG_BLUEZ_USB_ZERO_PACKET
diff -Nru a/drivers/bluetooth/Config.in b/drivers/bluetooth/Config.in
--- a/drivers/bluetooth/Config.in	Mon Oct  7 11:25:35 2002
+++ b/drivers/bluetooth/Config.in	Mon Oct  7 11:25:35 2002
@@ -3,13 +3,13 @@
 
 dep_tristate 'HCI USB driver' CONFIG_BLUEZ_HCIUSB $CONFIG_BLUEZ $CONFIG_USB
 if [ "$CONFIG_BLUEZ_HCIUSB" != "n" ]; then
-   bool '  Firmware download support'  CONFIG_BLUEZ_USB_FW_LOAD
    bool '  USB zero packet support'  CONFIG_BLUEZ_USB_ZERO_PACKET
 fi
 
 dep_tristate 'HCI UART driver' CONFIG_BLUEZ_HCIUART $CONFIG_BLUEZ
 if [ "$CONFIG_BLUEZ_HCIUART" != "n" ]; then
    bool '  UART (H4) protocol support' CONFIG_BLUEZ_HCIUART_H4
+   bool '  BCSP protocol support' CONFIG_BLUEZ_HCIUART_BCSP
 fi
 
 dep_tristate 'HCI DTL1 (PC Card) driver' CONFIG_BLUEZ_HCIDTL1 $CONFIG_PCMCIA $CONFIG_BLUEZ
diff -Nru a/drivers/bluetooth/Makefile b/drivers/bluetooth/Makefile
--- a/drivers/bluetooth/Makefile	Mon Oct  7 11:25:39 2002
+++ b/drivers/bluetooth/Makefile	Mon Oct  7 11:25:39 2002
@@ -1,5 +1,5 @@
 #
-# Makefile for Bluetooth HCI device drivers.
+# Makefile for the Linux Bluetooth HCI device drivers.
 #
 
 obj-$(CONFIG_BLUEZ_HCIUSB)	+= hci_usb.o
@@ -11,6 +11,7 @@
 
 hci_uart-y				:= hci_ldisc.o
 hci_uart-$(CONFIG_BLUEZ_HCIUART_H4)	+= hci_h4.o
+hci_uart-$(CONFIG_BLUEZ_HCIUART_BCSP)	+= hci_bcsp.o
 hci_uart-objs				:= $(hci_uart-y)
 
 include $(TOPDIR)/Rules.make
diff -Nru a/drivers/bluetooth/hci_bcsp.c b/drivers/bluetooth/hci_bcsp.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/bluetooth/hci_bcsp.c	Mon Oct  7 11:25:41 2002
@@ -0,0 +1,710 @@
+/* 
+   BlueCore Serial Protocol (BCSP) for Linux Bluetooth stack (BlueZ).
+   Copyright 2002 by Fabrizio Gennari <fabrizio.gennari@philips.com>
+
+   Based on
+       hci_h4.c  by Maxim Krasnyansky <maxk@qualcomm.com>
+       ABCSP     by Carl Orsborn <cjo@csr.com>
+
+   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;
+
+   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+   IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
+   CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES 
+   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 
+   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 
+   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+   ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, 
+   COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS 
+   SOFTWARE IS DISCLAIMED.
+*/
+
+/*
+ * $Id: hci_bcsp.c,v 1.2 2002/09/26 05:05:14 maxk Exp $
+ */
+
+#define VERSION "0.1"
+
+#include <linux/config.h>
+#include <linux/module.h>
+
+#include <linux/version.h>
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/poll.h>
+
+#include <linux/slab.h>
+#include <linux/tty.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/signal.h>
+#include <linux/ioctl.h>
+#include <linux/skbuff.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+#include "hci_uart.h"
+#include "hci_bcsp.h"
+
+#ifndef HCI_UART_DEBUG
+#undef  BT_DBG
+#define BT_DBG( A... )
+#undef  BT_DMP
+#define BT_DMP( A... )
+#endif
+
+/* ---- BCSP CRC calculation ---- */
+
+/* Table for calculating CRC for polynomial 0x1021, LSB processed first,
+initial value 0xffff, bits shifted in reverse order. */
+
+static const u16 crc_table[] = {
+	0x0000, 0x1081, 0x2102, 0x3183,
+	0x4204, 0x5285, 0x6306, 0x7387,
+	0x8408, 0x9489, 0xa50a, 0xb58b,
+	0xc60c, 0xd68d, 0xe70e, 0xf78f
+};
+
+/* Initialise the crc calculator */
+#define BCSP_CRC_INIT(x) x = 0xffff
+
+/*
+   Update crc with next data byte
+
+   Implementation note
+        The data byte is treated as two nibbles.  The crc is generated
+        in reverse, i.e., bits are fed into the register from the top.
+*/
+static void bcsp_crc_update(u16 *crc, u8 d)
+{
+	u16 reg = *crc;
+
+	reg = (reg >> 4) ^ crc_table[(reg ^ d) & 0x000f];
+	reg = (reg >> 4) ^ crc_table[(reg ^ (d >> 4)) & 0x000f];
+
+	*crc = reg;
+}
+
+/*
+   Get reverse of generated crc
+
+   Implementation note
+        The crc generator (bcsp_crc_init() and bcsp_crc_update())
+        creates a reversed crc, so it needs to be swapped back before
+        being passed on.
+*/
+static u16 bcsp_crc_reverse(u16 crc)
+{
+	u16 b, rev;
+
+	for (b = 0, rev = 0; b < 16; b++) {
+		rev = rev << 1;
+		rev |= (crc & 1);
+		crc = crc >> 1;
+	}
+	return (rev);
+}
+
+/* ---- BCSP core ---- */
+
+static void bcsp_slip_msgdelim(struct sk_buff *skb)
+{
+	const char pkt_delim = 0xc0;
+	memcpy(skb_put(skb, 1), &pkt_delim, 1);
+}
+
+static void bcsp_slip_one_byte(struct sk_buff *skb, u8 c)
+{
+	const char esc_c0[2] = { 0xdb, 0xdc };
+	const char esc_db[2] = { 0xdb, 0xdd };
+
+	switch (c) {
+	case 0xc0:
+		memcpy(skb_put(skb, 2), &esc_c0, 2);
+		break;
+	case 0xdb:
+		memcpy(skb_put(skb, 2), &esc_db, 2);
+		break;
+	default:
+		memcpy(skb_put(skb, 1), &c, 1);
+	}
+}
+
+static int bcsp_enqueue(struct hci_uart *hu, struct sk_buff *skb)
+{
+	struct bcsp_struct *bcsp = hu->priv;
+
+	if (skb->len > 0xFFF) {
+		BT_ERR("Packet too long");
+		kfree_skb(skb);
+		return 0;
+	}
+
+	switch (skb->pkt_type) {
+	case HCI_ACLDATA_PKT:
+	case HCI_COMMAND_PKT:
+		skb_queue_tail(&bcsp->rel, skb);
+		break;
+
+	case HCI_SCODATA_PKT:
+		skb_queue_tail(&bcsp->unrel, skb);
+		break;
+		
+	default:
+		BT_ERR("Unknown packet type");
+		kfree_skb(skb);
+		break;
+	}
+	return 0;
+}
+
+static struct sk_buff *bcsp_prepare_pkt(struct bcsp_struct *bcsp, u8 *data,
+		int len, int pkt_type)
+{
+	struct sk_buff *nskb;
+	u8  hdr[4], chan;
+	int rel, i;
+
+#ifdef CONFIG_BLUEZ_HCIUART_BCSP_TXCRC
+	u16 BCSP_CRC_INIT(bcsp_txmsg_crc);
+#endif
+
+	switch (pkt_type) {
+	case HCI_ACLDATA_PKT:
+		chan = 6;	/* BCSP ACL channel */
+		rel = 1;	/* reliable channel */
+		break;
+	case HCI_COMMAND_PKT:
+		chan = 5;	/* BCSP cmd/evt channel */
+		rel = 1;	/* reliable channel */
+		break;
+	case HCI_SCODATA_PKT:
+		chan = 7;	/* BCSP SCO channel */
+		rel = 0;	/* unreliable channel */
+		break;
+	case BCSP_LE_PKT:
+		chan = 1;	/* BCSP LE channel */
+		rel = 0;	/* unreliable channel */
+		break;
+	case BCSP_ACK_PKT:
+		chan = 0;	/* BCSP internal channel */
+		rel = 0;	/* unreliable channel */
+		break;
+	default:
+		BT_ERR("Unknown packet type");
+		return NULL;
+	}
+
+	/* Max len of packet: (original len +4(bcsp hdr) +2(crc))*2
+	   (because bytes 0xc0 and 0xdb are escaped, worst case is
+	   when the packet is all made of 0xc0 and 0xdb :) )
+	   + 2 (0xc0 delimiters at start and end). */
+
+	nskb = alloc_skb((len + 6) * 2 + 2, GFP_ATOMIC);
+	if (!nskb)
+		return NULL;
+
+	nskb->pkt_type = pkt_type;
+
+	bcsp_slip_msgdelim(nskb);
+
+	hdr[0] = bcsp->rxseq_txack << 3;
+	bcsp->txack_req = 0;
+	BT_DBG("We request packet no %u to card", bcsp->rxseq_txack);
+
+	if (rel) {
+		hdr[0] |= 0x80 + bcsp->msgq_txseq;
+		BT_DBG("Sending packet with seqno %u", bcsp->msgq_txseq);
+		bcsp->msgq_txseq = ++(bcsp->msgq_txseq) & 0x07;
+	}
+#ifdef  CONFIG_BLUEZ_HCIUART_BCSP_TXCRC
+	hdr[0] |= 0x40;
+#endif
+
+	hdr[1]  = (len << 4) & 0xFF;
+	hdr[1] |= chan;
+	hdr[2]  = len >> 4;
+	hdr[3]  = ~(hdr[0] + hdr[1] + hdr[2]);
+
+	/* Put BCSP header */
+	for (i = 0; i < 4; i++) {
+		bcsp_slip_one_byte(nskb, hdr[i]);
+#ifdef  CONFIG_BLUEZ_HCIUART_BCSP_TXCRC
+		bcsp_crc_update(&bcsp_txmsg_crc, hdr[i]);
+#endif
+	}
+
+	/* Put payload */
+	for (i = 0; i < len; i++) {
+		bcsp_slip_one_byte(nskb, data[i]);
+#ifdef  CONFIG_BLUEZ_HCIUART_BCSP_TXCRC
+		bcsp_crc_update(&bcsp_txmsg_crc, data[i]);
+#endif
+	}
+
+#ifdef CONFIG_BLUEZ_HCIUART_BCSP_TXCRC
+	/* Put CRC */
+	bcsp_txmsg_crc = bcsp_crc_reverse(bcsp_txmsg_crc);
+	bcsp_slip_one_byte(nskb, (u8) ((bcsp_txmsg_crc >> 8) & 0x00ff));
+	bcsp_slip_one_byte(nskb, (u8) (bcsp_txmsg_crc & 0x00ff));
+#endif
+
+	bcsp_slip_msgdelim(nskb);
+	return nskb;
+}
+
+/* This is a rewrite of pkt_avail in ABCSP */
+static struct sk_buff *bcsp_dequeue(struct hci_uart *hu)
+{
+	struct bcsp_struct *bcsp = (struct bcsp_struct *) hu->priv;
+	unsigned long flags;
+	struct sk_buff *skb;
+	
+	/* First of all, check for unreliable messages in the queue,
+	   since they have priority */
+
+	if ((skb = skb_dequeue(&bcsp->unrel)) != NULL) {
+		struct sk_buff *nskb = bcsp_prepare_pkt(bcsp, skb->data, skb->len, skb->pkt_type);
+		if (nskb) {
+			kfree_skb(skb);
+			return nskb;
+		} else {
+			skb_queue_head(&bcsp->unrel, skb);
+			BT_ERR("Could not dequeue pkt because alloc_skb failed");
+		}
+	}
+
+	/* Now, try to send a reliable pkt. We can only send a
+	   reliable packet if the number of packets sent but not yet ack'ed
+	   is < than the winsize */
+
+	spin_lock_irqsave(&bcsp->unack.lock, flags);
+
+	if (bcsp->unack.qlen < BCSP_TXWINSIZE && (skb = skb_dequeue(&bcsp->rel)) != NULL) {
+		struct sk_buff *nskb = bcsp_prepare_pkt(bcsp, skb->data, skb->len, skb->pkt_type);
+		if (nskb) {
+			__skb_queue_tail(&bcsp->unack, skb);
+			mod_timer(&bcsp->tbcsp, jiffies + HZ / 4);
+			spin_unlock_irqrestore(&bcsp->unack.lock, flags);
+			return nskb;
+		} else {
+			skb_queue_head(&bcsp->rel, skb);
+			BT_ERR("Could not dequeue pkt because alloc_skb failed");
+		}
+	}
+
+	spin_unlock_irqrestore(&bcsp->unack.lock, flags);
+
+
+	/* We could not send a reliable packet, either because there are
+	   none or because there are too many unack'ed pkts. Did we receive
+	   any packets we have not acknowledged yet ? */
+
+	if (bcsp->txack_req) {
+		/* if so, craft an empty ACK pkt and send it on BCSP unreliable
+		   channel 0 */
+		struct sk_buff *nskb = bcsp_prepare_pkt(bcsp, NULL, 0, BCSP_ACK_PKT);
+		return nskb;
+	}
+
+	/* We have nothing to send */
+	return NULL;
+}
+
+static int bcsp_flush(struct hci_uart *hu)
+{
+	BT_DBG("hu %p", hu);
+	return 0;
+}
+
+/* Remove ack'ed packets */
+static void bcsp_pkt_cull(struct bcsp_struct *bcsp)
+{
+	unsigned long flags;
+	struct sk_buff *skb;
+	int i, pkts_to_be_removed;
+	u8 seqno;
+
+	spin_lock_irqsave(&bcsp->unack.lock, flags);
+
+	pkts_to_be_removed = bcsp->unack.qlen;
+	seqno = bcsp->msgq_txseq;
+
+	while (pkts_to_be_removed) {
+		if (bcsp->rxack == seqno)
+			break;
+		pkts_to_be_removed--;
+		seqno = (seqno - 1) & 0x07;
+	}
+
+	if (bcsp->rxack != seqno)
+		BT_ERR("Peer acked invalid packet");
+
+	BT_DBG("Removing %u pkts out of %u, up to seqno %u",
+	       pkts_to_be_removed, bcsp->unack.qlen, (seqno - 1) & 0x07);
+
+	for (i = 0, skb = ((struct sk_buff *) &bcsp->unack)->next; i < pkts_to_be_removed
+			&& skb != (struct sk_buff *) &bcsp->unack; i++) {
+		struct sk_buff *nskb;
+
+		nskb = skb->next;
+		__skb_unlink(skb, &bcsp->unack);
+		kfree_skb(skb);
+		skb = nskb;
+	}
+	if (bcsp->unack.qlen == 0)
+		del_timer(&bcsp->tbcsp);
+	spin_unlock_irqrestore(&bcsp->unack.lock, flags);
+
+	if (i != pkts_to_be_removed)
+		BT_ERR("Removed only %u out of %u pkts", i, pkts_to_be_removed);
+}
+
+/* Handle BCSP link-establishment packets. When we
+   detect a "sync" packet, symptom that the BT module has reset,
+   we do nothing :) (yet) */
+static void bcsp_handle_le_pkt(struct hci_uart *hu)
+{
+	struct bcsp_struct *bcsp = hu->priv;
+	u8 conf_pkt[4]     = { 0xad, 0xef, 0xac, 0xed };
+	u8 conf_rsp_pkt[4] = { 0xde, 0xad, 0xd0, 0xd0 };
+	u8 sync_pkt[4]     = { 0xda, 0xdc, 0xed, 0xed };
+
+	/* spot "conf" pkts and reply with a "conf rsp" pkt */
+	if (bcsp->rx_skb->data[1] >> 4 == 4 && bcsp->rx_skb->data[2] == 0 &&
+			!memcmp(&bcsp->rx_skb->data[4], conf_pkt, 4)) {
+		struct sk_buff *nskb = alloc_skb(4, GFP_ATOMIC);
+
+		BT_DBG("Found a LE conf pkt");
+		if (!nskb)
+			return;
+		memcpy(skb_put(nskb, 4), conf_rsp_pkt, 4);
+		nskb->pkt_type = BCSP_LE_PKT;
+
+		skb_queue_head(&bcsp->unrel, nskb);
+		hci_uart_tx_wakeup(hu);
+	}
+	/* Spot "sync" pkts. If we find one...disaster! */
+	else if (bcsp->rx_skb->data[1] >> 4 == 4 && bcsp->rx_skb->data[2] == 0 &&
+			!memcmp(&bcsp->rx_skb->data[4], sync_pkt, 4)) {
+		BT_ERR("Found a LE sync pkt, card has reset");
+	}
+}
+
+static inline void bcsp_unslip_one_byte(struct bcsp_struct *bcsp, unsigned char byte)
+{
+	const u8 c0 = 0xc0, db = 0xdb;
+
+	switch (bcsp->rx_esc_state) {
+	case BCSP_ESCSTATE_NOESC:
+		switch (byte) {
+		case 0xdb:
+			bcsp->rx_esc_state = BCSP_ESCSTATE_ESC;
+			break;
+		default:
+			memcpy(skb_put(bcsp->rx_skb, 1), &byte, 1);
+			if ((bcsp->rx_skb-> data[0] & 0x40) != 0 && 
+					bcsp->rx_state != BCSP_W4_CRC)
+				bcsp_crc_update(&bcsp->message_crc, byte);
+			bcsp->rx_count--;
+		}
+		break;
+
+	case BCSP_ESCSTATE_ESC:
+		switch (byte) {
+		case 0xdc:
+			memcpy(skb_put(bcsp->rx_skb, 1), &c0, 1);
+			if ((bcsp->rx_skb-> data[0] & 0x40) != 0 && 
+					bcsp->rx_state != BCSP_W4_CRC)
+				bcsp_crc_update(&bcsp-> message_crc, 0xc0);
+			bcsp->rx_esc_state = BCSP_ESCSTATE_NOESC;
+			bcsp->rx_count--;
+			break;
+
+		case 0xdd:
+			memcpy(skb_put(bcsp->rx_skb, 1), &db, 1);
+			if ((bcsp->rx_skb-> data[0] & 0x40) != 0 && 
+					bcsp->rx_state != BCSP_W4_CRC) 
+				bcsp_crc_update(&bcsp-> message_crc, 0xdb);
+			bcsp->rx_esc_state = BCSP_ESCSTATE_NOESC;
+			bcsp->rx_count--;
+			break;
+
+		default:
+			BT_ERR ("Invalid byte %02x after esc byte", byte);
+			kfree_skb(bcsp->rx_skb);
+			bcsp->rx_skb = NULL;
+			bcsp->rx_state = BCSP_W4_PKT_DELIMITER;
+			bcsp->rx_count = 0;
+		}
+	}
+}
+
+static inline void bcsp_complete_rx_pkt(struct hci_uart *hu)
+{
+	struct bcsp_struct *bcsp = hu->priv;
+	int pass_up;
+
+	if (bcsp->rx_skb->data[0] & 0x80) {	/* reliable pkt */
+		BT_DBG("Received seqno %u from card", bcsp->rxseq_txack);
+		bcsp->rxseq_txack++;
+		bcsp->rxseq_txack %= 0x8;
+		bcsp->txack_req    = 1;
+
+		/* If needed, transmit an ack pkt */
+		hci_uart_tx_wakeup(hu);
+	}
+
+	bcsp->rxack = (bcsp->rx_skb->data[0] >> 3) & 0x07;
+	BT_DBG("Request for pkt %u from card", bcsp->rxack);
+
+	bcsp_pkt_cull(bcsp);
+	if ((bcsp->rx_skb->data[1] & 0x0f) == 6 &&
+			bcsp->rx_skb->data[0] & 0x80) {
+		bcsp->rx_skb->pkt_type = HCI_ACLDATA_PKT;
+		pass_up = 1;
+	} else if ((bcsp->rx_skb->data[1] & 0x0f) == 5 &&
+			bcsp->rx_skb->data[0] & 0x80) {
+		bcsp->rx_skb->pkt_type = HCI_EVENT_PKT;
+		pass_up = 1;
+	} else if ((bcsp->rx_skb->data[1] & 0x0f) == 7) {
+		bcsp->rx_skb->pkt_type = HCI_SCODATA_PKT;
+		pass_up = 1;
+	} else if ((bcsp->rx_skb->data[1] & 0x0f) == 1 &&
+			!(bcsp->rx_skb->data[0] & 0x80)) {
+		bcsp_handle_le_pkt(hu);
+		pass_up = 0;
+	} else
+		pass_up = 0;
+
+	if (!pass_up) {
+		if ((bcsp->rx_skb->data[1] & 0x0f) != 0 &&
+	    		(bcsp->rx_skb->data[1] & 0x0f) != 1) {
+			BT_ERR ("Packet for unknown channel (%u %s)",
+				bcsp->rx_skb->data[1] & 0x0f,
+				bcsp->rx_skb->data[0] & 0x80 ? 
+				"reliable" : "unreliable");
+		}
+		kfree_skb(bcsp->rx_skb);
+	} else {
+		/* Pull out BCSP hdr */
+		skb_pull(bcsp->rx_skb, 4);
+
+		hci_recv_frame(bcsp->rx_skb);
+	}
+	bcsp->rx_state = BCSP_W4_PKT_DELIMITER;
+	bcsp->rx_skb = NULL;
+}
+
+/* Recv data */
+static int bcsp_recv(struct hci_uart *hu, void *data, int count)
+{
+	struct bcsp_struct *bcsp = hu->priv;
+	register unsigned char *ptr;
+
+	BT_DBG("hu %p count %d rx_state %ld rx_count %ld", 
+		hu, count, bcsp->rx_state, bcsp->rx_count);
+
+	ptr = data;
+	while (count) {
+		if (bcsp->rx_count) {
+			if (*ptr == 0xc0) {
+				BT_ERR("Short BCSP packet");
+				kfree_skb(bcsp->rx_skb);
+				bcsp->rx_state = BCSP_W4_PKT_START;
+				bcsp->rx_count = 0;
+			} else
+				bcsp_unslip_one_byte(bcsp, *ptr);
+
+			ptr++; count--;
+			continue;
+		}
+
+		switch (bcsp->rx_state) {
+		case BCSP_W4_BCSP_HDR:
+			if ((0xff & (u8) ~ (bcsp->rx_skb->data[0] + bcsp->rx_skb->data[1] +
+					bcsp->rx_skb->data[2])) != bcsp->rx_skb->data[3]) {
+				BT_ERR("Error in BCSP hdr checksum");
+				kfree_skb(bcsp->rx_skb);
+				bcsp->rx_state = BCSP_W4_PKT_DELIMITER;
+				bcsp->rx_count = 0;
+				continue;
+			}
+			if (bcsp->rx_skb->data[0] & 0x80	/* reliable pkt */
+			    		&& (bcsp->rx_skb->data[0] & 0x07) != bcsp->rxseq_txack) {
+				BT_ERR ("Out-of-order packet arrived, got %u expected %u",
+					bcsp->rx_skb->data[0] & 0x07, bcsp->rxseq_txack);
+
+				kfree_skb(bcsp->rx_skb);
+				bcsp->rx_state = BCSP_W4_PKT_DELIMITER;
+				bcsp->rx_count = 0;
+				continue;
+			}
+			bcsp->rx_state = BCSP_W4_DATA;
+			bcsp->rx_count = (bcsp->rx_skb->data[1] >> 4) + 
+					(bcsp->rx_skb->data[2] << 4);	/* May be 0 */
+			continue;
+
+		case BCSP_W4_DATA:
+			if (bcsp->rx_skb->data[0] & 0x40) {	/* pkt with crc */
+				bcsp->rx_state = BCSP_W4_CRC;
+				bcsp->rx_count = 2;
+			} else
+				bcsp_complete_rx_pkt(hu);
+			continue;
+
+		case BCSP_W4_CRC:
+			if (bcsp_crc_reverse(bcsp->message_crc) !=
+					(bcsp->rx_skb->data[bcsp->rx_skb->len - 2] << 8) +
+					bcsp->rx_skb->data[bcsp->rx_skb->len - 1]) {
+
+				BT_ERR ("Checksum failed: computed %04x received %04x",
+					bcsp_crc_reverse(bcsp->message_crc),
+				     	(bcsp->rx_skb-> data[bcsp->rx_skb->len - 2] << 8) +
+				     	bcsp->rx_skb->data[bcsp->rx_skb->len - 1]);
+
+				kfree_skb(bcsp->rx_skb);
+				bcsp->rx_state = BCSP_W4_PKT_DELIMITER;
+				bcsp->rx_count = 0;
+				continue;
+			}
+			skb_trim(bcsp->rx_skb, bcsp->rx_skb->len - 2);
+			bcsp_complete_rx_pkt(hu);
+			continue;
+
+		case BCSP_W4_PKT_DELIMITER:
+			switch (*ptr) {
+			case 0xc0:
+				bcsp->rx_state = BCSP_W4_PKT_START;
+				break;
+			default:
+				/*BT_ERR("Ignoring byte %02x", *ptr);*/
+				break;
+			}
+			ptr++; count--;
+			break;
+
+		case BCSP_W4_PKT_START:
+			switch (*ptr) {
+			case 0xc0:
+				ptr++; count--;
+				break;
+
+			default:
+				bcsp->rx_state = BCSP_W4_BCSP_HDR;
+				bcsp->rx_count = 4;
+				bcsp->rx_esc_state = BCSP_ESCSTATE_NOESC;
+				BCSP_CRC_INIT(bcsp->message_crc);
+				
+				/* Do not increment ptr or decrement count
+				 * Allocate packet. Max len of a BCSP pkt= 
+				 * 0xFFF (payload) +4 (header) +2 (crc) */
+
+				bcsp->rx_skb = bluez_skb_alloc(0x1005, GFP_ATOMIC);
+				if (!bcsp->rx_skb) {
+					BT_ERR("Can't allocate mem for new packet");
+					bcsp->rx_state = BCSP_W4_PKT_DELIMITER;
+					bcsp->rx_count = 0;
+					return 0;
+				}
+				bcsp->rx_skb->dev = (void *) &hu->hdev;
+				break;
+			}
+			break;
+		}
+	}
+	return count;
+}
+
+	/* Arrange to retransmit all messages in the relq. */
+static void bcsp_timed_event(unsigned long arg)
+{
+	struct hci_uart *hu = (struct hci_uart *) arg;
+	struct bcsp_struct *bcsp = (struct bcsp_struct *) hu->priv;
+	struct sk_buff *skb;
+	unsigned long flags;
+
+	BT_ERR("Timeout, retransmitting %u pkts", bcsp->unack.qlen);
+	spin_lock_irqsave(&bcsp->unack.lock, flags);
+
+	while ((skb = __skb_dequeue_tail(&bcsp->unack)) != NULL) {
+		bcsp->msgq_txseq = (bcsp->msgq_txseq - 1) & 0x07;
+		skb_queue_head(&bcsp->rel, skb);
+	}
+
+	spin_unlock_irqrestore(&bcsp->unack.lock, flags);
+
+	hci_uart_tx_wakeup(hu);
+}
+
+static int bcsp_open(struct hci_uart *hu)
+{
+	struct bcsp_struct *bcsp;
+
+	BT_DBG("hu %p", hu);
+
+	bcsp = kmalloc(sizeof(*bcsp), GFP_ATOMIC);
+	if (!bcsp)
+		return -ENOMEM;
+	memset(bcsp, 0, sizeof(*bcsp));
+
+	hu->priv = bcsp;
+	skb_queue_head_init(&bcsp->unack);
+	skb_queue_head_init(&bcsp->rel);
+	skb_queue_head_init(&bcsp->unrel);
+
+	init_timer(&bcsp->tbcsp);
+	bcsp->tbcsp.function = bcsp_timed_event;
+	bcsp->tbcsp.data     = (u_long) hu;
+
+	bcsp->rx_state = BCSP_W4_PKT_DELIMITER;
+
+	return 0;
+}
+
+static int bcsp_close(struct hci_uart *hu)
+{
+	struct bcsp_struct *bcsp = hu->priv;
+	hu->priv = NULL;
+
+	BT_DBG("hu %p", hu);
+
+	skb_queue_purge(&bcsp->unack);
+	skb_queue_purge(&bcsp->rel);
+	skb_queue_purge(&bcsp->unrel);
+	del_timer(&bcsp->tbcsp);
+
+	kfree(bcsp);
+	return 0;
+}
+
+static struct hci_uart_proto bcsp = {
+	.id      = HCI_UART_BCSP,
+	.open    = bcsp_open,
+	.close   = bcsp_close,
+	.enqueue = bcsp_enqueue,
+	.dequeue = bcsp_dequeue,
+	.recv    = bcsp_recv,
+	.flush   = bcsp_flush
+};
+
+int bcsp_init(void)
+{
+	return hci_uart_register_proto(&bcsp);
+}
+
+int bcsp_deinit(void)
+{
+	return hci_uart_unregister_proto(&bcsp);
+}
diff -Nru a/drivers/bluetooth/hci_bcsp.h b/drivers/bluetooth/hci_bcsp.h
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/bluetooth/hci_bcsp.h	Mon Oct  7 11:25:41 2002
@@ -0,0 +1,70 @@
+/* 
+   BlueCore Serial Protocol (BCSP) for Linux Bluetooth stack (BlueZ).
+   Copyright 2002 by Fabrizio Gennari <fabrizio.gennari@philips.com>
+
+   Based on
+       hci_h4.c  by Maxim Krasnyansky <maxk@qualcomm.com>
+       ABCSP     by Carl Orsborn <cjo@csr.com>
+
+   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;
+
+   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+   IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
+   CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES 
+   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 
+   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 
+   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+   ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, 
+   COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS 
+   SOFTWARE IS DISCLAIMED.
+*/
+
+/* 
+ * $Id: hci_bcsp.h,v 1.2 2002/09/26 05:05:14 maxk Exp $
+ */
+
+#ifndef __HCI_BCSP_H__
+#define __HCI_BCSP_H__
+
+#define BCSP_TXWINSIZE  4
+
+#define BCSP_ACK_PKT    0x05
+#define BCSP_LE_PKT     0x06
+
+struct bcsp_struct {
+	struct sk_buff_head unack;	/* Unack'ed packets queue */
+	struct sk_buff_head rel;	/* Reliable packets queue */
+	struct sk_buff_head unrel;	/* Unreliable packets queue */
+
+	unsigned long rx_count;
+	struct  sk_buff *rx_skb;
+	u8      rxseq_txack;		/* rxseq == txack. */
+	u8      rxack;			/* Last packet sent by us that the peer ack'ed */
+	struct  timer_list tbcsp;
+	
+	enum {
+		BCSP_W4_PKT_DELIMITER,
+		BCSP_W4_PKT_START,
+		BCSP_W4_BCSP_HDR,
+		BCSP_W4_DATA,
+		BCSP_W4_CRC
+	} rx_state;
+
+	enum {
+		BCSP_ESCSTATE_NOESC,
+		BCSP_ESCSTATE_ESC
+	} rx_esc_state;
+
+	u16     message_crc;
+	u8      txack_req;		/* Do we need to send ack's to the peer? */
+
+	/* Reliable packet sequence number - used to assign seq to each rel pkt. */
+	u8      msgq_txseq;
+};
+
+#endif	/* __HCI_BCSP_H__ */
diff -Nru a/drivers/bluetooth/hci_h4.c b/drivers/bluetooth/hci_h4.c
--- a/drivers/bluetooth/hci_h4.c	Mon Oct  7 11:25:35 2002
+++ b/drivers/bluetooth/hci_h4.c	Mon Oct  7 11:25:35 2002
@@ -25,9 +25,9 @@
 /*
  * BlueZ HCI UART(H4) protocol.
  *
- * $Id: hci_h4.c,v 1.2 2002/04/17 17:37:20 maxk Exp $    
+ * $Id: hci_h4.c,v 1.3 2002/09/09 01:17:32 maxk Exp $    
  */
-#define VERSION "1.1"
+#define VERSION "1.2"
 
 #include <linux/config.h>
 #include <linux/module.h>
@@ -64,63 +64,61 @@
 #endif
 
 /* Initialize protocol */
-static int h4_open(struct n_hci *n_hci)
+static int h4_open(struct hci_uart *hu)
 {
 	struct h4_struct *h4;
 	
-	BT_DBG("n_hci %p", n_hci);
+	BT_DBG("hu %p", hu);
 	
 	h4 = kmalloc(sizeof(*h4), GFP_ATOMIC);
 	if (!h4)
 		return -ENOMEM;
 	memset(h4, 0, sizeof(*h4));
 
-	n_hci->priv = h4;
+	skb_queue_head_init(&h4->txq);
+
+	hu->priv = h4;
 	return 0;
 }
 
 /* Flush protocol data */
-static int h4_flush(struct n_hci *n_hci)
+static int h4_flush(struct hci_uart *hu)
 {
-	BT_DBG("n_hci %p", n_hci);
+	struct h4_struct *h4 = hu->priv;
+
+	BT_DBG("hu %p", hu);
+	skb_queue_purge(&h4->txq);
 	return 0;
 }
 
 /* Close protocol */
-static int h4_close(struct n_hci *n_hci)
+static int h4_close(struct hci_uart *hu)
 {
-	struct h4_struct *h4 = n_hci->priv;
-	n_hci->priv = NULL;
+	struct h4_struct *h4 = hu->priv;
+	hu->priv = NULL;
 
-	BT_DBG("n_hci %p", n_hci);
+	BT_DBG("hu %p", hu);
 
+	skb_queue_purge(&h4->txq);
 	if (h4->rx_skb)
 		kfree_skb(h4->rx_skb);
 
+	hu->priv = NULL;
 	kfree(h4);
 	return 0;
 }
 
-/* Send data */
-static int h4_send(struct n_hci *n_hci, void *data, int len)
+/* Enqueue frame for transmittion (padding, crc, etc) */
+static int h4_enqueue(struct hci_uart *hu, struct sk_buff *skb)
 {
-	struct tty_struct *tty = n_hci->tty;
-	
-	BT_DBG("n_hci %p len %d", n_hci, len);
-
-	/* Send frame to TTY driver */
-	tty->flags |= (1 << TTY_DO_WRITE_WAKEUP);
-	return tty->driver.write(tty, 0, data, len);
-}
+	struct h4_struct *h4 = hu->priv;
 
-/* Init frame before queueing (padding, crc, etc) */
-static struct sk_buff* h4_preq(struct n_hci *n_hci, struct sk_buff *skb)
-{
-	BT_DBG("n_hci %p skb %p", n_hci, skb);
+	BT_DBG("hu %p skb %p", hu, skb);
 
 	/* Prepend skb with frame type */
 	memcpy(skb_push(skb, 1), &skb->pkt_type, 1);
-	return skb;
+	skb_queue_tail(&h4->txq, skb);
+	return 0;
 }
 
 static inline int h4_check_data_len(struct h4_struct *h4, int len)
@@ -132,7 +130,7 @@
 		BT_DMP(h4->rx_skb->data, h4->rx_skb->len);
 		hci_recv_frame(h4->rx_skb);
 	} else if (len > room) {
-		BT_ERR("Data length is to large");
+		BT_ERR("Data length is too large");
 		kfree_skb(h4->rx_skb);
 	} else {
 		h4->rx_state = H4_W4_DATA;
@@ -147,16 +145,17 @@
 }
 
 /* Recv data */
-static int h4_recv(struct n_hci *n_hci, void *data, int count)
+static int h4_recv(struct hci_uart *hu, void *data, int count)
 {
-	struct h4_struct *h4 = n_hci->priv;
+	struct h4_struct *h4 = hu->priv;
 	register char *ptr;
 	hci_event_hdr *eh;
 	hci_acl_hdr   *ah;
 	hci_sco_hdr   *sh;
 	register int len, type, dlen;
 
-	BT_DBG("n_hci %p count %d rx_state %ld rx_count %ld", n_hci, count, h4->rx_state, h4->rx_count);
+	BT_DBG("hu %p count %d rx_state %ld rx_count %ld", 
+			hu, count, h4->rx_state, h4->rx_count);
 
 	ptr = data;
 	while (count) {
@@ -204,7 +203,7 @@
 
 				h4_check_data_len(h4, sh->dlen);
 				continue;
-			};
+			}
 		}
 
 		/* H4_W4_PACKET_TYPE */
@@ -232,7 +231,7 @@
 
 		default:
 			BT_ERR("Unknown HCI packet type %2.2x", (__u8)*ptr);
-			n_hci->hdev.stat.err_rx++;
+			hu->hdev.stat.err_rx++;
 			ptr++; count--;
 			continue;
 		};
@@ -246,20 +245,26 @@
 			h4->rx_count = 0;
 			return 0;
 		}
-		h4->rx_skb->dev = (void *) &n_hci->hdev;
+		h4->rx_skb->dev = (void *) &hu->hdev;
 		h4->rx_skb->pkt_type = type;
 	}
 	return count;
 }
 
+static struct sk_buff *h4_dequeue(struct hci_uart *hu)
+{
+	struct h4_struct *h4 = hu->priv;
+	return skb_dequeue(&h4->txq);
+}
+
 static struct hci_uart_proto h4p = {
-	.id    = HCI_UART_H4,
-	.open  = h4_open,
-	.close = h4_close,
-	.send  = h4_send,
-	.recv  = h4_recv,
-	.preq  = h4_preq,
-	.flush = h4_flush,
+	.id      = HCI_UART_H4,
+	.open    = h4_open,
+	.close   = h4_close,
+	.recv    = h4_recv,
+	.enqueue = h4_enqueue,
+	.dequeue = h4_dequeue,
+	.flush   = h4_flush,
 };
 	      
 int h4_init(void)
diff -Nru a/drivers/bluetooth/hci_h4.h b/drivers/bluetooth/hci_h4.h
--- a/drivers/bluetooth/hci_h4.h	Mon Oct  7 11:25:30 2002
+++ b/drivers/bluetooth/hci_h4.h	Mon Oct  7 11:25:30 2002
@@ -23,7 +23,7 @@
 */
 
 /*
- * $Id: hci_h4.h,v 1.1.1.1 2002/03/08 21:03:15 maxk Exp $
+ * $Id: hci_h4.h,v 1.2 2002/09/09 01:17:32 maxk Exp $
  */
 
 #ifdef __KERNEL__
@@ -31,6 +31,7 @@
 	unsigned long rx_state;
 	unsigned long rx_count;
 	struct sk_buff *rx_skb;
+	struct sk_buff_head txq;
 };
 
 /* H4 receiver States */
diff -Nru a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c
--- a/drivers/bluetooth/hci_ldisc.c	Mon Oct  7 11:25:36 2002
+++ b/drivers/bluetooth/hci_ldisc.c	Mon Oct  7 11:25:36 2002
@@ -25,14 +25,15 @@
 /*
  * BlueZ HCI UART driver.
  *
- * $Id: hci_ldisc.c,v 1.2 2002/04/17 17:37:20 maxk Exp $    
+ * $Id: hci_ldisc.c,v 1.5 2002/10/02 18:37:20 maxk Exp $    
  */
-#define VERSION "2.0"
+#define VERSION "2.1"
 
 #include <linux/config.h>
 #include <linux/module.h>
 
 #include <linux/version.h>
+#include <linux/config.h>
 #include <linux/kernel.h>
 #include <linux/init.h>
 #include <linux/sched.h>
@@ -87,16 +88,86 @@
 	return 0;
 }
 
-static struct hci_uart_proto *n_hci_get_proto(unsigned int id)
+static struct hci_uart_proto *hci_uart_get_proto(unsigned int id)
 {
 	if (id >= HCI_UART_MAX_PROTO)
 		return NULL;
 	return hup[id];
 }
 
+static inline void hci_uart_tx_complete(struct hci_uart *hu, int pkt_type)
+{
+	struct hci_dev *hdev = &hu->hdev;
+	
+	/* Update HCI stat counters */
+	switch (pkt_type) {
+	case HCI_COMMAND_PKT:
+		hdev->stat.cmd_tx++;
+		break;
+
+	case HCI_ACLDATA_PKT:
+		hdev->stat.acl_tx++;
+		break;
+
+	case HCI_SCODATA_PKT:
+		hdev->stat.cmd_tx++;
+		break;
+	}
+}
+
+static inline struct sk_buff *hci_uart_dequeue(struct hci_uart *hu)
+{
+	struct sk_buff *skb = hu->tx_skb;
+	if (!skb)
+		skb = hu->proto->dequeue(hu);
+	else
+		hu->tx_skb = NULL;
+	return skb;
+}
+
+int hci_uart_tx_wakeup(struct hci_uart *hu)
+{
+	struct tty_struct *tty = hu->tty;
+	struct hci_dev *hdev = &hu->hdev;
+	struct sk_buff *skb;
+	
+	if (test_and_set_bit(HCI_UART_SENDING, &hu->tx_state)) {
+		set_bit(HCI_UART_TX_WAKEUP, &hu->tx_state);
+		return 0;
+	}
+
+	BT_DBG("");
+
+restart:
+	clear_bit(HCI_UART_TX_WAKEUP, &hu->tx_state);
+
+	while ((skb = hci_uart_dequeue(hu))) {
+		int len;
+	
+		set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
+		len = tty->driver.write(tty, 0, skb->data, skb->len);
+		hdev->stat.byte_tx += len;
+
+		skb_pull(skb, len);
+		if (skb->len) {
+			hu->tx_skb = skb;
+			break;
+		}
+	
+		hci_uart_tx_complete(hu, skb->pkt_type);
+		kfree_skb(skb);
+	} 
+	
+	if (test_bit(HCI_UART_TX_WAKEUP, &hu->tx_state))
+		goto restart;
+
+	clear_bit(HCI_UART_SENDING, &hu->tx_state);
+	return 0;
+}
+
 /* ------- Interface to HCI layer ------ */
 /* Initialize device */
-static int n_hci_open(struct hci_dev *hdev)
+static int hci_uart_open(struct hci_dev *hdev)
 {
 	BT_DBG("%s %p", hdev->name, hdev);
 
@@ -107,15 +178,16 @@
 }
 
 /* Reset device */
-static int n_hci_flush(struct hci_dev *hdev)
+static int hci_uart_flush(struct hci_dev *hdev)
 {
-	struct n_hci *n_hci  = (struct n_hci *) hdev->driver_data;
-	struct tty_struct *tty = n_hci->tty;
+	struct hci_uart *hu  = (struct hci_uart *) hdev->driver_data;
+	struct tty_struct *tty = hu->tty;
 
 	BT_DBG("hdev %p tty %p", hdev, tty);
 
-	/* Drop TX queue */
-	skb_queue_purge(&n_hci->txq);
+	if (hu->tx_skb) {
+		kfree_skb(hu->tx_skb); hu->tx_skb = NULL;
+	}
 
 	/* Flush any pending characters in the driver and discipline. */
 	if (tty->ldisc.flush_buffer)
@@ -124,80 +196,30 @@
 	if (tty->driver.flush_buffer)
 		tty->driver.flush_buffer(tty);
 
-	if (n_hci->proto->flush)
-		n_hci->proto->flush(n_hci);
+	if (test_bit(HCI_UART_PROTO_SET, &hu->flags))
+		hu->proto->flush(hu);
 
 	return 0;
 }
 
 /* Close device */
-static int n_hci_close(struct hci_dev *hdev)
+static int hci_uart_close(struct hci_dev *hdev)
 {
 	BT_DBG("hdev %p", hdev);
 
 	if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
 		return 0;
 
-	n_hci_flush(hdev);
-	return 0;
-}
-
-static int n_hci_tx_wakeup(struct n_hci *n_hci)
-{
-	struct hci_dev *hdev = &n_hci->hdev;
-	
-	if (test_and_set_bit(N_HCI_SENDING, &n_hci->tx_state)) {
-		set_bit(N_HCI_TX_WAKEUP, &n_hci->tx_state);
-		return 0;
-	}
-
-	BT_DBG("");
-	do {
-		register struct sk_buff *skb;
-		register int len;
-
-		clear_bit(N_HCI_TX_WAKEUP, &n_hci->tx_state);
-
-		if (!(skb = skb_dequeue(&n_hci->txq)))
-			break;
-
-		len = n_hci->proto->send(n_hci, skb->data, skb->len);
-		n_hci->hdev.stat.byte_tx += len;
-
-		if (len == skb->len) {
-			/* Complete frame was sent */
-
-			switch (skb->pkt_type) {
-			case HCI_COMMAND_PKT:
-				hdev->stat.cmd_tx++;
-				break;
-
-			case HCI_ACLDATA_PKT:
-				hdev->stat.acl_tx++;
-				break;
-
-			case HCI_SCODATA_PKT:
-				hdev->stat.cmd_tx++;
-				break;
-			};
-
-			kfree_skb(skb);
-		} else {
-			/* Subtract sent part and requeue  */
-			skb_pull(skb, len);
-			skb_queue_head(&n_hci->txq, skb);
-		}
-	} while (test_bit(N_HCI_TX_WAKEUP, &n_hci->tx_state));
-	clear_bit(N_HCI_SENDING, &n_hci->tx_state);
+	hci_uart_flush(hdev);
 	return 0;
 }
 
 /* Send frames from HCI layer */
-static int n_hci_send_frame(struct sk_buff *skb)
+static int hci_uart_send_frame(struct sk_buff *skb)
 {
 	struct hci_dev* hdev = (struct hci_dev *) skb->dev;
 	struct tty_struct *tty;
-	struct n_hci *n_hci;
+	struct hci_uart *hu;
 
 	if (!hdev) {
 		BT_ERR("Frame for uknown device (hdev=NULL)");
@@ -207,66 +229,60 @@
 	if (!test_bit(HCI_RUNNING, &hdev->flags))
 		return -EBUSY;
 
-	n_hci = (struct n_hci *) hdev->driver_data;
-	tty = n_hci->tty;
+	hu = (struct hci_uart *) hdev->driver_data;
+	tty = hu->tty;
 
 	BT_DBG("%s: type %d len %d", hdev->name, skb->pkt_type, skb->len);
 
-	if (n_hci->proto->preq) {
-		skb = n_hci->proto->preq(n_hci, skb);
-		if (!skb)
-			return 0;
-	}
-	
-	skb_queue_tail(&n_hci->txq, skb);
-	n_hci_tx_wakeup(n_hci);
+	hu->proto->enqueue(hu, skb);
+
+	hci_uart_tx_wakeup(hu);
 	return 0;
 }
 
-static void n_hci_destruct(struct hci_dev *hdev)
+static void hci_uart_destruct(struct hci_dev *hdev)
 {
-	struct n_hci *n_hci;
+	struct hci_uart *hu;
 
 	if (!hdev) return;
 
 	BT_DBG("%s", hdev->name);
 
-	n_hci = (struct n_hci *) hdev->driver_data;
-	kfree(n_hci);
+	hu = (struct hci_uart *) hdev->driver_data;
+	kfree(hu);
 
 	MOD_DEC_USE_COUNT;
 }
 
 /* ------ LDISC part ------ */
-/* n_hci_tty_open
+/* hci_uart_tty_open
  * 
- *     Called when line discipline changed to N_HCI.
+ *     Called when line discipline changed to HCI_UART.
  *
  * Arguments:
  *     tty    pointer to tty info structure
  * Return Value:    
  *     0 if success, otherwise error code
  */
-static int n_hci_tty_open(struct tty_struct *tty)
+static int hci_uart_tty_open(struct tty_struct *tty)
 {
-	struct n_hci *n_hci = (void *)tty->disc_data;
+	struct hci_uart *hu = (void *) tty->disc_data;
 
 	BT_DBG("tty %p", tty);
 
-	if (n_hci)
+	if (hu)
 		return -EEXIST;
 
-	if (!(n_hci = kmalloc(sizeof(struct n_hci), GFP_KERNEL))) {
+	if (!(hu = kmalloc(sizeof(struct hci_uart), GFP_KERNEL))) {
 		BT_ERR("Can't allocate controll structure");
 		return -ENFILE;
 	}
-	memset(n_hci, 0, sizeof(struct n_hci));
+	memset(hu, 0, sizeof(struct hci_uart));
 
-	tty->disc_data = n_hci;
-	n_hci->tty = tty;
+	tty->disc_data = hu;
+	hu->tty = tty;
 
-	spin_lock_init(&n_hci->rx_lock);
-	skb_queue_head_init(&n_hci->txq);
+	spin_lock_init(&hu->rx_lock);
 
 	/* Flush any pending characters in the driver and line discipline */
 	if (tty->ldisc.flush_buffer)
@@ -279,34 +295,34 @@
 	return 0;
 }
 
-/* n_hci_tty_close()
+/* hci_uart_tty_close()
  *
  *    Called when the line discipline is changed to something
  *    else, the tty is closed, or the tty detects a hangup.
  */
-static void n_hci_tty_close(struct tty_struct *tty)
+static void hci_uart_tty_close(struct tty_struct *tty)
 {
-	struct n_hci *n_hci = (void *)tty->disc_data;
+	struct hci_uart *hu = (void *)tty->disc_data;
 
 	BT_DBG("tty %p", tty);
 
 	/* Detach from the tty */
 	tty->disc_data = NULL;
 
-	if (n_hci) {
-		struct hci_dev *hdev = &n_hci->hdev;
-		n_hci_close(hdev);
+	if (hu) {
+		struct hci_dev *hdev = &hu->hdev;
+		hci_uart_close(hdev);
 
-		if (test_and_clear_bit(N_HCI_PROTO_SET, &n_hci->flags)) {
-			n_hci->proto->close(n_hci);
+		if (test_and_clear_bit(HCI_UART_PROTO_SET, &hu->flags)) {
+			hu->proto->close(hu);
 			hci_unregister_dev(hdev);
 		}
-				
+
 		MOD_DEC_USE_COUNT;
 	}
 }
 
-/* n_hci_tty_wakeup()
+/* hci_uart_tty_wakeup()
  *
  *    Callback for transmit wakeup. Called when low level
  *    device driver can accept more send data.
@@ -314,24 +330,24 @@
  * Arguments:        tty    pointer to associated tty instance data
  * Return Value:    None
  */
-static void n_hci_tty_wakeup( struct tty_struct *tty )
+static void hci_uart_tty_wakeup(struct tty_struct *tty)
 {
-	struct n_hci *n_hci = (void *)tty->disc_data;
+	struct hci_uart *hu = (void *)tty->disc_data;
 
 	BT_DBG("");
 
-	if (!n_hci)
+	if (!hu)
 		return;
 
-	tty->flags &= ~(1 << TTY_DO_WRITE_WAKEUP);
+	clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
 
-	if (tty != n_hci->tty)
+	if (tty != hu->tty)
 		return;
 
-	n_hci_tx_wakeup(n_hci);
+	hci_uart_tx_wakeup(hu);
 }
 
-/* n_hci_tty_room()
+/* hci_uart_tty_room()
  * 
  *    Callback function from tty driver. Return the amount of 
  *    space left in the receiver's buffer to decide if remote
@@ -340,12 +356,12 @@
  * Arguments:        tty    pointer to associated tty instance data
  * Return Value:    number of bytes left in receive buffer
  */
-static int n_hci_tty_room (struct tty_struct *tty)
+static int hci_uart_tty_room (struct tty_struct *tty)
 {
 	return 65536;
 }
 
-/* n_hci_tty_receive()
+/* hci_uart_tty_receive()
  * 
  *     Called by tty low level driver when receive data is
  *     available.
@@ -357,42 +373,42 @@
  *     
  * Return Value:    None
  */
-static void n_hci_tty_receive(struct tty_struct *tty, const __u8 * data, char *flags, int count)
+static void hci_uart_tty_receive(struct tty_struct *tty, const __u8 *data, char *flags, int count)
 {
-	struct n_hci *n_hci = (void *)tty->disc_data;
+	struct hci_uart *hu = (void *)tty->disc_data;
 	
-	if (!n_hci || tty != n_hci->tty)
+	if (!hu || tty != hu->tty)
 		return;
 
-	if (!test_bit(N_HCI_PROTO_SET, &n_hci->flags))
+	if (!test_bit(HCI_UART_PROTO_SET, &hu->flags))
 		return;
 	
-	spin_lock(&n_hci->rx_lock);
-	n_hci->proto->recv(n_hci, (void *) data, count);
-	n_hci->hdev.stat.byte_rx += count;
-	spin_unlock(&n_hci->rx_lock);
+	spin_lock(&hu->rx_lock);
+	hu->proto->recv(hu, (void *) data, count);
+	hu->hdev.stat.byte_rx += count;
+	spin_unlock(&hu->rx_lock);
 
 	if (test_and_clear_bit(TTY_THROTTLED,&tty->flags) && tty->driver.unthrottle)
 		tty->driver.unthrottle(tty);
 }
 
-static int n_hci_register_dev(struct n_hci *n_hci)
+static int hci_uart_register_dev(struct hci_uart *hu)
 {
 	struct hci_dev *hdev;
 
 	BT_DBG("");
 
 	/* Initialize and register HCI device */
-	hdev = &n_hci->hdev;
+	hdev = &hu->hdev;
 
 	hdev->type = HCI_UART;
-	hdev->driver_data = n_hci;
+	hdev->driver_data = hu;
 
-	hdev->open  = n_hci_open;
-	hdev->close = n_hci_close;
-	hdev->flush = n_hci_flush;
-	hdev->send  = n_hci_send_frame;
-	hdev->destruct = n_hci_destruct;
+	hdev->open  = hci_uart_open;
+	hdev->close = hci_uart_close;
+	hdev->flush = hci_uart_flush;
+	hdev->send  = hci_uart_send_frame;
+	hdev->destruct = hci_uart_destruct;
 
 	if (hci_register_dev(hdev) < 0) {
 		BT_ERR("Can't register HCI device %s", hdev->name);
@@ -402,30 +418,30 @@
 	return 0;
 }
 
-static int n_hci_set_proto(struct n_hci *n_hci, int id)
+static int hci_uart_set_proto(struct hci_uart *hu, int id)
 {
 	struct hci_uart_proto *p;
 	int err;	
 	
-	p = n_hci_get_proto(id);
+	p = hci_uart_get_proto(id);
 	if (!p)
 		return -EPROTONOSUPPORT;
 
-	err = p->open(n_hci);
+	err = p->open(hu);
 	if (err)
 		return err;
 
-	n_hci->proto = p;
+	hu->proto = p;
 
-	err = n_hci_register_dev(n_hci);
+	err = hci_uart_register_dev(hu);
 	if (err) {
-		p->close(n_hci);
+		p->close(hu);
 		return err;
 	}
 	return 0;
 }
 
-/* n_hci_tty_ioctl()
+/* hci_uart_tty_ioctl()
  *
  *    Process IOCTL system call for the tty device.
  *
@@ -438,24 +454,24 @@
  *
  * Return Value:    Command dependent
  */
-static int n_hci_tty_ioctl(struct tty_struct *tty, struct file * file,
+static int hci_uart_tty_ioctl(struct tty_struct *tty, struct file * file,
                             unsigned int cmd, unsigned long arg)
 {
-	struct n_hci *n_hci = (void *)tty->disc_data;
+	struct hci_uart *hu = (void *)tty->disc_data;
 	int err = 0;
 
 	BT_DBG("");
 
 	/* Verify the status of the device */
-	if (!n_hci)
+	if (!hu)
 		return -EBADF;
 
 	switch (cmd) {
 	case HCIUARTSETPROTO:
-		if (!test_and_set_bit(N_HCI_PROTO_SET, &n_hci->flags)) {
-			err = n_hci_set_proto(n_hci, arg);
+		if (!test_and_set_bit(HCI_UART_PROTO_SET, &hu->flags)) {
+			err = hci_uart_set_proto(hu, arg);
 			if (err) {
-				clear_bit(N_HCI_PROTO_SET, &n_hci->flags);
+				clear_bit(HCI_UART_PROTO_SET, &hu->flags);
 				return err;
 			}
 			tty->low_latency = 1;
@@ -463,8 +479,8 @@
 			return -EBUSY;
 
 	case HCIUARTGETPROTO:
-		if (test_bit(N_HCI_PROTO_SET, &n_hci->flags))
-			return n_hci->proto->id;
+		if (test_bit(HCI_UART_PROTO_SET, &hu->flags))
+			return hu->proto->id;
 		return -EUNATCH;
 		
 	default:
@@ -478,15 +494,15 @@
 /*
  * We don't provide read/write/poll interface for user space.
  */
-static ssize_t n_hci_tty_read(struct tty_struct *tty, struct file *file, unsigned char *buf, size_t nr)
+static ssize_t hci_uart_tty_read(struct tty_struct *tty, struct file *file, unsigned char *buf, size_t nr)
 {
 	return 0;
 }
-static ssize_t n_hci_tty_write(struct tty_struct *tty, struct file *file, const unsigned char *data, size_t count)
+static ssize_t hci_uart_tty_write(struct tty_struct *tty, struct file *file, const unsigned char *data, size_t count)
 {
 	return 0;
 }
-static unsigned int n_hci_tty_poll(struct tty_struct *tty, struct file *filp, poll_table *wait)
+static unsigned int hci_uart_tty_poll(struct tty_struct *tty, struct file *filp, poll_table *wait)
 {
 	return 0;
 }
@@ -495,10 +511,14 @@
 int h4_init(void);
 int h4_deinit(void);
 #endif
+#ifdef CONFIG_BLUEZ_HCIUART_BCSP
+int bcsp_init(void);
+int bcsp_deinit(void);
+#endif
 
-int __init n_hci_init(void)
+int __init hci_uart_init(void)
 {
-	static struct tty_ldisc n_hci_ldisc;
+	static struct tty_ldisc hci_uart_ldisc;
 	int err;
 
 	BT_INFO("BlueZ HCI UART driver ver %s Copyright (C) 2000,2001 Qualcomm Inc", 
@@ -507,20 +527,20 @@
 
 	/* Register the tty discipline */
 
-	memset(&n_hci_ldisc, 0, sizeof (n_hci_ldisc));
-	n_hci_ldisc.magic       = TTY_LDISC_MAGIC;
-	n_hci_ldisc.name        = "n_hci";
-	n_hci_ldisc.open        = n_hci_tty_open;
-	n_hci_ldisc.close       = n_hci_tty_close;
-	n_hci_ldisc.read        = n_hci_tty_read;
-	n_hci_ldisc.write       = n_hci_tty_write;
-	n_hci_ldisc.ioctl       = n_hci_tty_ioctl;
-	n_hci_ldisc.poll        = n_hci_tty_poll;
-	n_hci_ldisc.receive_room= n_hci_tty_room;
-	n_hci_ldisc.receive_buf = n_hci_tty_receive;
-	n_hci_ldisc.write_wakeup= n_hci_tty_wakeup;
+	memset(&hci_uart_ldisc, 0, sizeof (hci_uart_ldisc));
+	hci_uart_ldisc.magic       = TTY_LDISC_MAGIC;
+	hci_uart_ldisc.name        = "n_hci";
+	hci_uart_ldisc.open        = hci_uart_tty_open;
+	hci_uart_ldisc.close       = hci_uart_tty_close;
+	hci_uart_ldisc.read        = hci_uart_tty_read;
+	hci_uart_ldisc.write       = hci_uart_tty_write;
+	hci_uart_ldisc.ioctl       = hci_uart_tty_ioctl;
+	hci_uart_ldisc.poll        = hci_uart_tty_poll;
+	hci_uart_ldisc.receive_room= hci_uart_tty_room;
+	hci_uart_ldisc.receive_buf = hci_uart_tty_receive;
+	hci_uart_ldisc.write_wakeup= hci_uart_tty_wakeup;
 
-	if ((err = tty_register_ldisc(N_HCI, &n_hci_ldisc))) {
+	if ((err = tty_register_ldisc(N_HCI, &hci_uart_ldisc))) {
 		BT_ERR("Can't register HCI line discipline (%d)", err);
 		return err;
 	}
@@ -528,25 +548,31 @@
 #ifdef CONFIG_BLUEZ_HCIUART_H4
 	h4_init();
 #endif
+#ifdef CONFIG_BLUEZ_HCIUART_BCSP
+	bcsp_init();
+#endif
 	
 	return 0;
 }
 
-void n_hci_cleanup(void)
+void hci_uart_cleanup(void)
 {
 	int err;
 
 #ifdef CONFIG_BLUEZ_HCIUART_H4
 	h4_deinit();
 #endif
+#ifdef CONFIG_BLUEZ_HCIUART_BCSP
+	bcsp_deinit();
+#endif
 
 	/* Release tty registration of line discipline */
 	if ((err = tty_register_ldisc(N_HCI, NULL)))
 		BT_ERR("Can't unregister HCI line discipline (%d)", err);
 }
 
-module_init(n_hci_init);
-module_exit(n_hci_cleanup);
+module_init(hci_uart_init);
+module_exit(hci_uart_cleanup);
 
 MODULE_AUTHOR("Maxim Krasnyansky <maxk@qualcomm.com>");
 MODULE_DESCRIPTION("BlueZ HCI UART driver ver " VERSION);
diff -Nru a/drivers/bluetooth/hci_uart.h b/drivers/bluetooth/hci_uart.h
--- a/drivers/bluetooth/hci_uart.h	Mon Oct  7 11:25:25 2002
+++ b/drivers/bluetooth/hci_uart.h	Mon Oct  7 11:25:25 2002
@@ -23,10 +23,10 @@
 */
 
 /*
- * $Id: hci_uart.h,v 1.1.1.1 2002/03/08 21:03:15 maxk Exp $
+ * $Id: hci_uart.h,v 1.2 2002/09/09 01:17:32 maxk Exp $
  */
 
-#ifndef N_HCI
+#ifndef N_HCI 
 #define N_HCI	15
 #endif
 
@@ -42,19 +42,19 @@
 #define HCI_UART_NCSP	2
 
 #ifdef __KERNEL__
-struct n_hci;
+struct hci_uart;
 
 struct hci_uart_proto {
 	unsigned int id;
-	int (*open)(struct n_hci *n_hci);
-	int (*recv)(struct n_hci *n_hci, void *data, int len);
-	int (*send)(struct n_hci *n_hci, void *data, int len);
-	int (*close)(struct n_hci *n_hci);
-	int (*flush)(struct n_hci *n_hci);
-	struct sk_buff* (*preq)(struct n_hci *n_hci, struct sk_buff *skb);
+	int (*open)(struct hci_uart *hu);
+	int (*close)(struct hci_uart *hu);
+	int (*flush)(struct hci_uart *hu);
+	int (*recv)(struct hci_uart *hu, void *data, int len);
+	int (*enqueue)(struct hci_uart *hu, struct sk_buff *skb);
+	struct sk_buff *(*dequeue)(struct hci_uart *hu);
 };
 
-struct n_hci {
+struct hci_uart {
 	struct tty_struct  *tty;
 	struct hci_dev     hdev;
 	unsigned long      flags;
@@ -62,19 +62,20 @@
 	struct hci_uart_proto *proto;
 	void               *priv;
 	
-	struct sk_buff_head txq;
-	unsigned long       tx_state;
-	spinlock_t          rx_lock;
+	struct sk_buff     *tx_skb;
+	unsigned long      tx_state;
+	spinlock_t         rx_lock;
 };
 
-/* N_HCI flag bits */
-#define N_HCI_PROTO_SET		0x00
+/* HCI_UART flag bits */
+#define HCI_UART_PROTO_SET		0x00
 
 /* TX states  */
-#define N_HCI_SENDING		1
-#define N_HCI_TX_WAKEUP		2
+#define HCI_UART_SENDING		1
+#define HCI_UART_TX_WAKEUP		2
 
 int hci_uart_register_proto(struct hci_uart_proto *p);
 int hci_uart_unregister_proto(struct hci_uart_proto *p);
+int hci_uart_tx_wakeup(struct hci_uart *hu);
 
 #endif /* __KERNEL__ */
diff -Nru a/drivers/bluetooth/hci_usb.c b/drivers/bluetooth/hci_usb.c
--- a/drivers/bluetooth/hci_usb.c	Mon Oct  7 11:25:39 2002
+++ b/drivers/bluetooth/hci_usb.c	Mon Oct  7 11:25:39 2002
@@ -28,9 +28,9 @@
  *    Copyright (c) 2000 Greg Kroah-Hartman        <greg@kroah.com>
  *    Copyright (c) 2000 Mark Douglas Corner       <mcorner@umich.edu>
  *
- * $Id: hci_usb.c,v 1.6 2002/04/17 17:37:20 maxk Exp $    
+ * $Id: hci_usb.c,v 1.8 2002/07/18 17:23:09 maxk Exp $    
  */
-#define VERSION "2.0"
+#define VERSION "2.1"
 
 #include <linux/config.h>
 #include <linux/module.h>
@@ -73,7 +73,7 @@
 
 static struct usb_driver hci_usb_driver; 
 
-static struct usb_device_id usb_bluetooth_ids [] = {
+static struct usb_device_id bluetooth_ids[] = {
 	/* Generic Bluetooth USB device */
 	{ USB_DEVICE_INFO(HCI_DEV_CLASS, HCI_DEV_SUBCLASS, HCI_DEV_PROTOCOL) },
 
@@ -83,7 +83,14 @@
 	{ }	/* Terminating entry */
 };
 
-MODULE_DEVICE_TABLE (usb, usb_bluetooth_ids);
+MODULE_DEVICE_TABLE (usb, bluetooth_ids);
+
+static struct usb_device_id ignore_ids[] = {
+	/* Broadcom BCM2033 without firmware */
+	{ USB_DEVICE(0x0a5c, 0x2033) },
+
+	{ }	/* Terminating entry */
+};
 
 static void hci_usb_interrupt(struct urb *urb);
 static void hci_usb_rx_complete(struct urb *urb);
@@ -193,22 +200,26 @@
 {
 	struct hci_usb *husb = (struct hci_usb *) hdev->driver_data;
 	int i, err;
-	long flags;
+	unsigned long flags;
 
 	BT_DBG("%s", hdev->name);
 
 	if (test_and_set_bit(HCI_RUNNING, &hdev->flags))
 		return 0;
 
+	MOD_INC_USE_COUNT;
+
 	write_lock_irqsave(&husb->completion_lock, flags);
 
 	err = hci_usb_enable_intr(husb);
 	if (!err) {
-		for (i = 0; i < HCI_MAX_BULK_TX; i++)
+		for (i = 0; i < HCI_MAX_BULK_RX; i++)
 			hci_usb_rx_submit(husb, NULL);
-	} else 
+	} else {
 		clear_bit(HCI_RUNNING, &hdev->flags);
-		
+		MOD_DEC_USE_COUNT;
+	}
+
 	write_unlock_irqrestore(&husb->completion_lock, flags);
 	return err;
 }
@@ -246,7 +257,7 @@
 static int hci_usb_close(struct hci_dev *hdev)
 {
 	struct hci_usb *husb = (struct hci_usb *) hdev->driver_data;
-	long flags;
+	unsigned long flags;
 	
 	if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
 		return 0;
@@ -260,6 +271,8 @@
 	hci_usb_flush(hdev);
 
 	write_unlock_irqrestore(&husb->completion_lock, flags);
+
+	MOD_DEC_USE_COUNT;
 	return 0;
 }
 
@@ -587,78 +600,11 @@
 
 	husb = (struct hci_usb *) hdev->driver_data;
 	kfree(husb);
-
-	MOD_DEC_USE_COUNT;
-}
-
-#ifdef CONFIG_BLUEZ_USB_FW_LOAD
-
-/* Support for user mode Bluetooth USB firmware loader */
-
-#define FW_LOADER "/sbin/bluefw"
-static int errno;
-
-static int hci_usb_fw_exec(void *dev)
-{
-	char *envp[] = { "HOME=/", "TERM=linux", 
-			 "PATH=/sbin:/usr/sbin:/bin:/usr/bin", NULL };
-	char *argv[] = { FW_LOADER, dev, NULL };
-	int err;
-
-	err = exec_usermodehelper(FW_LOADER, argv, envp);
-	if (err)
-		BT_ERR("failed to exec %s %s", FW_LOADER, (char *)dev);
-	return err;
-}
-
-static int hci_usb_fw_load(struct usb_device *udev)
-{
-	sigset_t tmpsig;
-	char dev[16];
-	pid_t pid;
-	int result;
-
-	/* Check if root fs is mounted */
-	if (!current->fs->root) {
-		BT_ERR("root fs not mounted");
-		return -EPERM;
-	}
-
-	sprintf(dev, "%3.3d/%3.3d", udev->bus->busnum, udev->devnum);
-
-	pid = kernel_thread(hci_usb_fw_exec, (void *)dev, 0);
-	if (pid < 0) {
-		BT_ERR("fork failed, errno %d\n", -pid);
-		return pid;
-	}
-
-	/* Block signals, everything but SIGKILL/SIGSTOP */
-	spin_lock_irq(&current->sig->siglock);
-	tmpsig = current->blocked;
-	siginitsetinv(&current->blocked, sigmask(SIGKILL) | sigmask(SIGSTOP));
-	recalc_sigpending();
-	spin_unlock_irq(&current->sig->siglock);
-
-	result = waitpid(pid, NULL, __WCLONE);
-
-	/* Allow signals again */
-	spin_lock_irq(&current->sig->siglock);
-	current->blocked = tmpsig;
-	recalc_sigpending();
-	spin_unlock_irq(&current->sig->siglock);
-
-	if (result != pid) {
-		BT_ERR("waitpid failed pid %d errno %d\n", pid, -result);
-		return -result;
-	}
-	return 0;
 }
 
-#endif /* CONFIG_BLUEZ_USB_FW_LOAD */
-
-static int hci_usb_probe(struct usb_interface *intf, const struct usb_device_id *id)
+int hci_usb_probe(struct usb_interface *intf, const struct usb_device_id *id)
 {
-	struct usb_device *udev = interface_to_usbdev(intf);
+	struct usb_device *udev = interface_to_usbdev(intf);	
 	struct usb_endpoint_descriptor *bulk_out_ep[HCI_MAX_IFACE_NUM];
 	struct usb_endpoint_descriptor *isoc_out_ep[HCI_MAX_IFACE_NUM];
 	struct usb_endpoint_descriptor *bulk_in_ep[HCI_MAX_IFACE_NUM];
@@ -673,15 +619,13 @@
 
 	BT_DBG("intf %p", intf);
 
+	/* Check our black list */
+	if (usb_match_id(intf, ignore_ids))
+		return -EIO;
+
 	/* Check number of endpoints */
 	if (intf->altsetting[0].bNumEndpoints < 3)
-		return -ENODEV;
-
-	MOD_INC_USE_COUNT;
-
-#ifdef CONFIG_BLUEZ_USB_FW_LOAD
-	hci_usb_fw_load(udev);
-#endif
+		return -EIO;
 
 	memset(bulk_out_ep, 0, sizeof(bulk_out_ep));
 	memset(isoc_out_ep, 0, sizeof(isoc_out_ep));
@@ -802,7 +746,6 @@
 	kfree(husb);
 
 done:
-	MOD_DEC_USE_COUNT;
 	return -EIO;
 }
 
@@ -811,9 +754,9 @@
 	struct hci_usb *husb = dev_get_drvdata(&intf->dev);
 	struct hci_dev *hdev;
 
-	dev_set_drvdata(&intf->dev, NULL);
 	if (!husb)
 		return;
+	dev_set_drvdata(&intf->dev, NULL);
 
 	hdev = &husb->hdev;
 	BT_DBG("%s", hdev->name);
@@ -828,10 +771,10 @@
 }
 
 static struct usb_driver hci_usb_driver = {
-	.name           = "hci_usb",
-	.probe          = hci_usb_probe,
-	.disconnect     = hci_usb_disconnect,
-	.id_table       = usb_bluetooth_ids,
+	.name       = "hci_usb",
+	.probe      = hci_usb_probe,
+	.disconnect = hci_usb_disconnect,
+	.id_table   = bluetooth_ids
 };
 
 int hci_usb_init(void)
diff -Nru a/drivers/bluetooth/hci_usb.h b/drivers/bluetooth/hci_usb.h
--- a/drivers/bluetooth/hci_usb.h	Mon Oct  7 11:25:36 2002
+++ b/drivers/bluetooth/hci_usb.h	Mon Oct  7 11:25:36 2002
@@ -55,8 +55,8 @@
 
 	__u8			intr_ep;
 	__u8			intr_interval;
-	struct urb *		intr_urb;
-	struct sk_buff *	intr_skb;
+	struct urb		*intr_urb;
+	struct sk_buff		*intr_skb;
 
 	rwlock_t		completion_lock;
 	
diff -Nru a/drivers/cdrom/aztcd.c b/drivers/cdrom/aztcd.c
--- a/drivers/cdrom/aztcd.c	Mon Oct  7 11:25:39 2002
+++ b/drivers/cdrom/aztcd.c	Mon Oct  7 11:25:39 2002
@@ -1690,13 +1690,7 @@
 	return 0;
 }
 
-static struct gendisk azt_disk = {
-	.major = MAJOR_NR,
-	.first_minor = 0,
-	.minor_shift = 0,
-	.fops = &azt_fops,
-	.disk_name = "aztcd"
-};
+static struct gendisk *azt_disk;
 
 /*
  * Test for presence of drive and initialize it.  Called at boot time.
@@ -1844,6 +1838,7 @@
 				for (count = 0; count < AZT_TIMEOUT;
 				     count++)
 					barrier();	/* Stop gcc 2.96 being smart */
+				/* use udelay(), damnit -- AV */
 
 				if ((st = getAztStatus()) == -1) {
 					printk(KERN_WARNING "aztcd: Drive Status"
@@ -1913,21 +1908,33 @@
 	}
 	devfs_register(NULL, "aztcd", DEVFS_FL_DEFAULT, MAJOR_NR, 0,
 		       S_IFBLK | S_IRUGO | S_IWUGO, &azt_fops, NULL);
+	azt_disk = alloc_disk();
+	if (!azt_disk)
+		goto err_out2;
 	if (register_blkdev(MAJOR_NR, "aztcd", &azt_fops) != 0) {
 		printk(KERN_WARNING "aztcd: Unable to get major %d for Aztech"
 		       " CD-ROM\n", MAJOR_NR);
 		ret = -EIO;
-		goto err_out;
+		goto err_out3;
 	}
 	blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), do_aztcd_request, &aztSpin);
 	blk_queue_hardsect_size(BLK_DEFAULT_QUEUE(MAJOR_NR), 2048);
-	add_disk(&azt_disk);
+	azt_disk->major = MAJOR_NR;
+	azt_disk->first_minor = 0;
+	azt_disk->minor_shift = 0;
+	azt_disk->fops = &azt_fops;
+	sprintf(azt_disk->disk_name, "aztcd");
+	add_disk(azt_disk);
 
 	azt_invalidate_buffers();
 	aztPresent = 1;
 	aztCloseDoor();
 	return (0);
- err_out:
+err_out3:
+	put_disk(azt_disk);
+err_out2:
+	devfs_find_and_unregister(NULL, "aztcd", 0, 0, DEVFS_SPECIAL_BLK, 0);
+err_out:
 	if ((azt_port == 0x1f0) || (azt_port == 0x170)) {
 		SWITCH_IDE_MASTER;
 		release_region(azt_port, 8);	/*IDE-interface */
@@ -1940,7 +1947,8 @@
 static void __exit aztcd_exit(void)
 {
 	devfs_find_and_unregister(NULL, "aztcd", 0, 0, DEVFS_SPECIAL_BLK, 0);
-	del_gendisk(&azt_disk);
+	del_gendisk(azt_disk);
+	put_disk(azt_disk);
 	if ((unregister_blkdev(MAJOR_NR, "aztcd") == -EINVAL)) {
 		printk("What's that: can't unregister aztcd\n");
 		return;
diff -Nru a/drivers/cdrom/cdu31a.c b/drivers/cdrom/cdu31a.c
--- a/drivers/cdrom/cdu31a.c	Mon Oct  7 11:25:26 2002
+++ b/drivers/cdrom/cdu31a.c	Mon Oct  7 11:25:26 2002
@@ -3188,14 +3188,7 @@
 	.name		= "cdu31a"
 };
 
-static struct gendisk scd_gendisk = {
-	.major		= MAJOR_NR,
-	.first_minor	= 0,
-	.minor_shift	= 0,
-	.disk_name	= "cdu31a",
-	.fops		= &scd_bdops,
-	.flags		= GENHD_FL_CD,
-};
+static struct gendisk *scd_gendisk;
 
 /* The different types of disc loading mechanisms supported */
 static char *load_mech[] __initdata =
@@ -3299,7 +3292,8 @@
 int __init cdu31a_init(void)
 {
 	struct s_sony_drive_config drive_config;
-	struct gendisk *disk = &scd_gendisk;
+	struct gendisk *disk;
+	int deficiency = 0;
 	unsigned int res_size;
 	char msg[255];
 	char buf[40];
@@ -3360,115 +3354,119 @@
 		}
 	}
 
-	if (drive_found) {
-		int deficiency = 0;
-
-		if (!request_region(cdu31a_port, 4, "cdu31a"))
-			goto errout3;
-
-		if (register_blkdev(MAJOR_NR, "cdu31a", &scd_bdops)) {
-			printk("Unable to get major %d for CDU-31a\n",
-			       MAJOR_NR);
-			goto errout2;
-		}
-
-		if (SONY_HWC_DOUBLE_SPEED(drive_config)) {
-			is_double_speed = 1;
-		}
-
-		tmp_irq = cdu31a_irq;	/* Need IRQ 0 because we can't sleep here. */
-		cdu31a_irq = 0;
+	if (!drive_found)
+		goto errout3;
 
-		set_drive_params(sony_speed);
+	if (!request_region(cdu31a_port, 4, "cdu31a"))
+		goto errout3;
 
-		cdu31a_irq = tmp_irq;
+	if (register_blkdev(MAJOR_NR, "cdu31a", &scd_bdops)) {
+		printk("Unable to get major %d for CDU-31a\n",
+		       MAJOR_NR);
+		goto errout2;
+	}
 
-		if (cdu31a_irq > 0) {
-			if (request_irq
-			    (cdu31a_irq, cdu31a_interrupt, SA_INTERRUPT,
-			     "cdu31a", NULL)) {
-				printk
-				    ("Unable to grab IRQ%d for the CDU31A driver\n",
-				     cdu31a_irq);
-				cdu31a_irq = 0;
-			}
+	disk = alloc_disk();
+	if (!disk)
+		goto errout1;
+	disk->major = MAJOR_NR;
+	disk->first_minor = 0;
+	disk->minor_shift = 0;
+	sprintf(disk->disk_name, "cdu31a");
+	disk->fops = &scd_bdops;
+	disk->flags = GENHD_FL_CD;
+
+	if (SONY_HWC_DOUBLE_SPEED(drive_config))
+		is_double_speed = 1;
+
+	tmp_irq = cdu31a_irq;	/* Need IRQ 0 because we can't sleep here. */
+	cdu31a_irq = 0;
+
+	set_drive_params(sony_speed);
+
+	cdu31a_irq = tmp_irq;
+
+	if (cdu31a_irq > 0) {
+		if (request_irq
+		    (cdu31a_irq, cdu31a_interrupt, SA_INTERRUPT,
+		     "cdu31a", NULL)) {
+			printk
+			    ("Unable to grab IRQ%d for the CDU31A driver\n",
+			     cdu31a_irq);
+			cdu31a_irq = 0;
 		}
+	}
 
-		sprintf(msg, "Sony I/F CDROM : %8.8s %16.16s %8.8s\n",
-			drive_config.vendor_id,
-			drive_config.product_id,
-			drive_config.product_rev_level);
-		sprintf(buf, "  Capabilities: %s",
-			load_mech[SONY_HWC_GET_LOAD_MECH(drive_config)]);
+	sprintf(msg, "Sony I/F CDROM : %8.8s %16.16s %8.8s\n",
+		drive_config.vendor_id,
+		drive_config.product_id,
+		drive_config.product_rev_level);
+	sprintf(buf, "  Capabilities: %s",
+		load_mech[SONY_HWC_GET_LOAD_MECH(drive_config)]);
+	strcat(msg, buf);
+	if (SONY_HWC_AUDIO_PLAYBACK(drive_config))
+		strcat(msg, ", audio");
+	else
+		deficiency |= CDC_PLAY_AUDIO;
+	if (SONY_HWC_EJECT(drive_config))
+		strcat(msg, ", eject");
+	else
+		deficiency |= CDC_OPEN_TRAY;
+	if (SONY_HWC_LED_SUPPORT(drive_config))
+		strcat(msg, ", LED");
+	if (SONY_HWC_ELECTRIC_VOLUME(drive_config))
+		strcat(msg, ", elec. Vol");
+	if (SONY_HWC_ELECTRIC_VOLUME_CTL(drive_config))
+		strcat(msg, ", sep. Vol");
+	if (is_double_speed)
+		strcat(msg, ", double speed");
+	else
+		deficiency |= CDC_SELECT_SPEED;
+	if (cdu31a_irq > 0) {
+		sprintf(buf, ", irq %d", cdu31a_irq);
 		strcat(msg, buf);
-		if (SONY_HWC_AUDIO_PLAYBACK(drive_config)) {
-			strcat(msg, ", audio");
-		} else
-			deficiency |= CDC_PLAY_AUDIO;
-		if (SONY_HWC_EJECT(drive_config)) {
-			strcat(msg, ", eject");
-		} else
-			deficiency |= CDC_OPEN_TRAY;
-		if (SONY_HWC_LED_SUPPORT(drive_config)) {
-			strcat(msg, ", LED");
-		}
-		if (SONY_HWC_ELECTRIC_VOLUME(drive_config)) {
-			strcat(msg, ", elec. Vol");
-		}
-		if (SONY_HWC_ELECTRIC_VOLUME_CTL(drive_config)) {
-			strcat(msg, ", sep. Vol");
-		}
-		if (is_double_speed) {
-			strcat(msg, ", double speed");
-		} else
-			deficiency |= CDC_SELECT_SPEED;
-		if (cdu31a_irq > 0) {
-			sprintf(buf, ", irq %d", cdu31a_irq);
-			strcat(msg, buf);
-		}
-		strcat(msg, "\n");
-
-		is_a_cdu31a =
-		    strcmp("CD-ROM CDU31A", drive_config.product_id) == 0;
-
-		blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR),
-			       do_cdu31a_request,
-			       &cdu31a_lock);
-
-		init_timer(&cdu31a_abort_timer);
-		cdu31a_abort_timer.function = handle_abort_timeout;
-
-		scd_info.dev = mk_kdev(MAJOR_NR, 0);
-		scd_info.mask = deficiency;
-		if (register_cdrom(&scd_info))
-			goto errout0;
-		add_disk(disk);
 	}
+	strcat(msg, "\n");
+
+	is_a_cdu31a =
+	    strcmp("CD-ROM CDU31A", drive_config.product_id) == 0;
 
+	blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR),
+		       do_cdu31a_request,
+		       &cdu31a_lock);
+
+	init_timer(&cdu31a_abort_timer);
+	cdu31a_abort_timer.function = handle_abort_timeout;
+
+	scd_info.dev = mk_kdev(MAJOR_NR, 0);
+	scd_info.mask = deficiency;
+	scd_gendisk = disk;
+	if (register_cdrom(&scd_info))
+		goto errout0;
+	add_disk(disk);
 
 	disk_changed = 1;
+	return (0);
 
-	if (drive_found) {
-		return (0);
-	} else {
-		goto errout3;
-	}
-      errout0:
+errout0:
 	printk("Unable to register CDU-31a with Uniform cdrom driver\n");
 	blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR));
+	put_disk(disk);
+errout1:
 	if (unregister_blkdev(MAJOR_NR, "cdu31a")) {
 		printk("Can't unregister block device for cdu31a\n");
 	}
-      errout2:
+errout2:
 	release_region(cdu31a_port, 4);
-      errout3:
+errout3:
 	return -EIO;
 }
 
 
 void __exit cdu31a_exit(void)
 {
-	del_gendisk(&scd_gendisk);
+	del_gendisk(scd_gendisk);
+	put_disk(scd_gendisk);
 	if (unregister_cdrom(&scd_info)) {
 		printk
 		    ("Can't unregister cdu31a from Uniform cdrom driver\n");
diff -Nru a/drivers/cdrom/cm206.c b/drivers/cdrom/cm206.c
--- a/drivers/cdrom/cm206.c	Mon Oct  7 11:25:38 2002
+++ b/drivers/cdrom/cm206.c	Mon Oct  7 11:25:38 2002
@@ -1357,14 +1357,7 @@
 	.name		= "cm206",
 };
 
-static struct gendisk cm206_gendisk = {
-	.major		= MAJOR_NR,
-	.first_minor	= 0,
-	.minor_shift	= 0,
-	.disk_name	= "cm206",
-	.fops		= &cm206_bdops,
-	.flags		= GENHD_FL_CD,
-};
+static struct gendisk *cm206_gendisk;
 
 /* This function probes for the adapter card. It returns the base
    address if it has found the adapter card. One can specify a base 
@@ -1419,7 +1412,7 @@
 {
 	uch e = 0;
 	long int size = sizeof(struct cm206_struct);
-	struct gendisk *disk = &cm206_gendisk;
+	struct gendisk *disk;
 
 	printk(KERN_INFO "cm206 cdrom driver " REVISION);
 	cm206_base = probe_base_port(auto_probe ? 0 : cm206_base);
@@ -1477,6 +1470,16 @@
 		printk(KERN_INFO "Cannot register for major %d!\n", MAJOR_NR);
 		goto out_blkdev;
 	}
+	disk = alloc_disk();
+	if (!disk)
+		goto out_disk;
+	disk->major = MAJOR_NR;
+	disk->first_minor = 0;
+	disk->minor_shift = 0;
+	sprintf(disk->disk_name, "cm206");
+	disk->fops = &cm206_bdops;
+	disk->flags = GENHD_FL_CD;
+	cm206_gendisk = disk;
 	cm206_info.dev = mk_kdev(MAJOR_NR, 0);
 	if (register_cdrom(&cm206_info) != 0) {
 		printk(KERN_INFO "Cannot register for cdrom %d!\n", MAJOR_NR);
@@ -1498,6 +1501,8 @@
 	return 0;
 
 out_cdrom:
+	put_disk(disk);
+out_disk:
 	unregister_blkdev(MAJOR_NR, "cm206");
 out_blkdev:
 	free_irq(cm206_irq, NULL);
@@ -1536,7 +1541,8 @@
 
 void __exit cm206_exit(void)
 {
-	del_gendisk(&cm206_gendisk);
+	del_gendisk(cm206_gendisk);
+	put_disk(cm206_gendisk);
 	if (unregister_cdrom(&cm206_info)) {
 		printk("Can't unregister cdrom cm206\n");
 		return;
diff -Nru a/drivers/cdrom/gscd.c b/drivers/cdrom/gscd.c
--- a/drivers/cdrom/gscd.c	Mon Oct  7 11:25:25 2002
+++ b/drivers/cdrom/gscd.c	Mon Oct  7 11:25:25 2002
@@ -898,20 +898,15 @@
 }
 #endif
 
-static struct gendisk gscd_disk = {
-	.major = MAJOR_NR,
-	.first_minor = 0,
-	.minor_shift = 0,
-	.fops = &gscd_fops,
-	.disk_name = "gscd"
-};
+static struct gendisk *gscd_disk;
 
 static void __exit gscd_exit(void)
 {
 	CLEAR_TIMER;
 
 	devfs_find_and_unregister(NULL, "gscd", 0, 0, DEVFS_SPECIAL_BLK, 0);
-	del_gendisk(&gscd_disk);
+	del_gendisk(gscd_disk);
+	put_disk(gscd_disk);
 	if ((unregister_blkdev(MAJOR_NR, "gscd") == -EINVAL)) {
 		printk("What's that: can't unregister GoldStar-module\n");
 		return;
@@ -977,11 +972,20 @@
 		i++;
 	}
 
+	gscd_disk = alloc_disk();
+	if (!gscd_disk)
+		goto err_out1;
+	gscd_disk->major = MAJOR_NR;
+	gscd_disk->first_minor = 0;
+	gscd_disk->minor_shift = 0;
+	gscd_disk->fops = &gscd_fops;
+	sprintf(gscd_disk->disk_name, "gscd");
+
 	if (register_blkdev(MAJOR_NR, "gscd", &gscd_fops) != 0) {
 		printk(KERN_WARNING "GSCD: Unable to get major %d for GoldStar "
 		       "CD-ROM\n", MAJOR_NR);
 		ret = -EIO;
-		goto err_out1;
+		goto err_out2;
 	}
 	devfs_register(NULL, "gscd", DEVFS_FL_DEFAULT, MAJOR_NR, 0,
 		       S_IFBLK | S_IRUGO | S_IWUGO, &gscd_fops, NULL);
@@ -991,10 +995,13 @@
 	disk_state = 0;
 	gscdPresent = 1;
 
-	add_disk(&gscd_disk);
+	add_disk(gscd_disk);
 
 	printk(KERN_INFO "GSCD: GoldStar CD-ROM Drive found.\n");
 	return 0;
+
+err_out2:
+	put_disk(gscd_disk);
 err_out1:
 	release_region(gscd_port, GSCD_IO_EXTENT);
 	return ret;
diff -Nru a/drivers/cdrom/mcd.c b/drivers/cdrom/mcd.c
--- a/drivers/cdrom/mcd.c	Mon Oct  7 11:25:36 2002
+++ b/drivers/cdrom/mcd.c	Mon Oct  7 11:25:36 2002
@@ -221,14 +221,7 @@
 	.name		= "mcd",
 };
 
-static struct gendisk mcd_gendisk = {
-	.major		= MAJOR_NR,
-	.first_minor	= 0,
-	.minor_shift	= 0,
-	.disk_name	= "mcd",
-	.fops		= &mcd_bdops,
-	.flags		= GENHD_FL_CD,
-};
+static struct gendisk *mcd_gendisk;
 
 #ifndef MODULE
 static int __init mcd_setup(char *str)
@@ -1038,18 +1031,23 @@
 
 int __init mcd_init(void)
 {
-	struct gendisk *disk = &mcd_gendisk;
+	struct gendisk *disk = alloc_disk();
 	int count;
 	unsigned char result[3];
 	char msg[80];
 
+	if (!disk) {
+		printk(KERN_INFO "mcd: can't allocated disk.\n");
+		return -ENOMEM;
+	}
 	if (mcd_port <= 0 || mcd_irq <= 0) {
 		printk(KERN_INFO "mcd: not probing.\n");
+		put_disk(disk);
 		return -EIO;
 	}
-
 	if (register_blkdev(MAJOR_NR, "mcd", &mcd_bdops) != 0) {
 		printk(KERN_ERR "mcd: Unable to get major %d for Mitsumi CD-ROM\n", MAJOR_NR);
+		put_disk(disk);
 		return -EIO;
 	}
 	if (!request_region(mcd_port, 4, "mcd")) {
@@ -1124,6 +1122,13 @@
 	mcd_invalidate_buffers();
 	mcdPresent = 1;
 
+	disk->major = MAJOR_NR;
+	disk->first_minor = 0;
+	disk->minor_shift = 0;
+	sprintf(disk->disk_name, "mcd");
+	disk->fops = &mcd_bdops;
+	disk->flags = GENHD_FL_CD;
+	mcd_gendisk = disk;
 	mcd_info.dev = mk_kdev(MAJOR_NR, 0);
 
 	if (register_cdrom(&mcd_info) != 0) {
@@ -1141,6 +1146,7 @@
 out_region:
 	unregister_blkdev(MAJOR_NR, "mcd");
 	blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR));
+	put_disk(disk);
 	return -EIO;
 }
 
@@ -1506,7 +1512,8 @@
 
 void __exit mcd_exit(void)
 {
-	del_gendisk(&mcd_gendisk);
+	del_gendisk(mcd_gendisk);
+	put_disk(mcd_gendisk);
 	if (unregister_cdrom(&mcd_info)) {
 		printk(KERN_WARNING "Can't unregister cdrom mcd\n");
 		return;
diff -Nru a/drivers/cdrom/mcdx.c b/drivers/cdrom/mcdx.c
--- a/drivers/cdrom/mcdx.c	Mon Oct  7 11:25:35 2002
+++ b/drivers/cdrom/mcdx.c	Mon Oct  7 11:25:35 2002
@@ -206,7 +206,7 @@
 	int status;		/* last operation's error / status */
 	int readerrs;		/* # of blocks read w/o error */
 	struct cdrom_device_info info;
-	struct gendisk disk;
+	struct gendisk *disk;
 };
 
 
@@ -1021,11 +1021,12 @@
 		struct s_drive_stuff *stuffp = mcdx_stuffp[i];
 		if (!stuffp)
 			continue;
-		del_gendisk(&stuffp->disk);
+		del_gendisk(stuffp->disk);
 		if (unregister_cdrom(&stuffp->info)) {
 			printk(KERN_WARNING "Can't unregister cdrom mcdx\n");
-			return;
+			continue;
 		}
+		put_disk(stuffp->disk);
 		release_region((unsigned long) stuffp->wreg_data,
 			       MCDX_IO_SIZE);
 		free_irq(stuffp->irq, NULL);
@@ -1075,6 +1076,13 @@
 		return 1;
 	}
 
+	disk = alloc_disk();
+	if (!disk) {
+		xwarn("init() malloc failed\n");
+		kfree(stuffp);
+		return 1;
+	}
+
 	xtrace(INIT, "init() got %d bytes for drive stuff @ %p\n",
 	       sizeof(*stuffp), stuffp);
 
@@ -1106,6 +1114,7 @@
 		      stuffp->wreg_data + MCDX_IO_SIZE - 1);
 		xtrace(MALLOC, "init() free stuffp @ %p\n", stuffp);
 		kfree(stuffp);
+		put_disk(disk);
 		xtrace(INIT, "init() continue at next drive\n");
 		return 0;	/* next drive */
 	}
@@ -1124,6 +1133,7 @@
 		      MCDX, stuffp->wreg_data, stuffp->irq);
 		xtrace(MALLOC, "init() free stuffp @ %p\n", stuffp);
 		kfree(stuffp);
+		put_disk(disk);
 		xtrace(INIT, "init() continue at next drive\n");
 		return 0;
 	}
@@ -1154,6 +1164,7 @@
 		xwarn("%s=0x%3p,%d: Init failed. No Mitsumi CD-ROM?.\n",
 		      MCDX, stuffp->wreg_data, stuffp->irq);
 		kfree(stuffp);
+		put_disk(disk);
 		return 0;	/* next drive */
 	}
 
@@ -1164,6 +1175,7 @@
 		xwarn("%s=0x%3p,%d: Init failed. Can't get major %d.\n",
 		      MCDX, stuffp->wreg_data, stuffp->irq, MAJOR_NR);
 		kfree(stuffp);
+		put_disk(disk);
 		return 1;
 	}
 
@@ -1180,6 +1192,7 @@
 		stuffp->irq = 0;
 		blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR));
 		kfree(stuffp);
+		put_disk(disk);
 		return 0;
 	}
 
@@ -1206,13 +1219,13 @@
 	stuffp->info.handle = stuffp;
 	sprintf(stuffp->info.name, "mcdx%d", drive);
 	stuffp->info.dev = mk_kdev(MAJOR_NR, drive);
-	disk = &stuffp->disk;
 	disk->major = MAJOR_NR;
 	disk->first_minor = drive;
 	disk->minor_shift = 0;
 	strcpy(disk->disk_name, stuffp->info.name);
 	disk->fops = &mcdx_bdops;
 	disk->flags = GENHD_FL_CD;
+	stuffp->disk = disk;
 
 	sprintf(msg, " mcdx: Mitsumi CD-ROM installed at 0x%3p, irq %d."
 		" (Firmware version %c %x)\n",
@@ -1225,6 +1238,7 @@
 			       MCDX_IO_SIZE);
 		free_irq(stuffp->irq, NULL);
 		kfree(stuffp);
+		put_disk(disk);
 		if (unregister_blkdev(MAJOR_NR, "mcdx") != 0)
 			xwarn("cleanup() unregister_blkdev() failed\n");
 		blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR));
diff -Nru a/drivers/cdrom/optcd.c b/drivers/cdrom/optcd.c
--- a/drivers/cdrom/optcd.c	Mon Oct  7 11:25:27 2002
+++ b/drivers/cdrom/optcd.c	Mon Oct  7 11:25:27 2002
@@ -1997,13 +1997,7 @@
 
 #endif /* MODULE */
 
-static struct gendisk optcd_disk = {
-	.major = MAJOR_NR,
-	.first_minor = 0,
-	.minor_shift = 0,
-	.fops = &opt_fops,
-	.disk_name = "optcd"
-};
+static struct gendisk *optcd_disk;
 
 /* Test for presence of drive and initialize it. Called at boot time
    or during module initialisation. */
@@ -2016,20 +2010,33 @@
 			"optcd: no Optics Storage CDROM Initialization\n");
 		return -EIO;
 	}
+	optcd_disk = alloc_disk();
+	if (!optcd_disk) {
+		printk(KERN_ERR "optcd: can't allocate disk\n");
+		return -ENOMEM;
+	}
+	optcd_disk->major = MAJOR_NR;
+	optcd_disk->first_minor = 0;
+	optcd_disk->minor_shift = 0;
+	optcd_disk->fops = &opt_fops;
+	sprintf(optcd_disk->disk_name, "optcd");
 	if (!request_region(optcd_port, 4, "optcd")) {
 		printk(KERN_ERR "optcd: conflict, I/O port 0x%x already used\n",
 			optcd_port);
+		put_disk(optcd_disk);
 		return -EIO;
 	}
 
 	if (!reset_drive()) {
 		printk(KERN_ERR "optcd: drive at 0x%x not ready\n", optcd_port);
 		release_region(optcd_port, 4);
+		put_disk(optcd_disk);
 		return -EIO;
 	}
 	if (!version_ok()) {
 		printk(KERN_ERR "optcd: unknown drive detected; aborting\n");
 		release_region(optcd_port, 4);
+		put_disk(optcd_disk);
 		return -EIO;
 	}
 	status = exec_cmd(COMINITDOUBLE);
@@ -2037,11 +2044,13 @@
 		printk(KERN_ERR "optcd: cannot init double speed mode\n");
 		release_region(optcd_port, 4);
 		DEBUG((DEBUG_VFS, "exec_cmd COMINITDOUBLE: %02x", -status));
+		put_disk(optcd_disk);
 		return -EIO;
 	}
 	if (register_blkdev(MAJOR_NR, "optcd", &opt_fops) != 0) {
 		printk(KERN_ERR "optcd: unable to get major %d\n", MAJOR_NR);
 		release_region(optcd_port, 4);
+		put_disk(optcd_disk);
 		return -EIO;
 	}
 	devfs_register (NULL, "optcd", DEVFS_FL_DEFAULT, MAJOR_NR, 0,
@@ -2049,7 +2058,7 @@
 	blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), do_optcd_request,
 		       &optcd_lock);
 	blk_queue_hardsect_size(BLK_DEFAULT_QUEUE(MAJOR_NR), 2048);
-	add_disk(&optcd_disk);
+	add_disk(optcd_disk);
 
 	printk(KERN_INFO "optcd: DOLPHIN 8000 AT CDROM at 0x%x\n", optcd_port);
 	return 0;
@@ -2059,7 +2068,8 @@
 static void __exit optcd_exit(void)
 {
 	devfs_find_and_unregister(NULL, "optcd", 0, 0, DEVFS_SPECIAL_BLK, 0);
-	del_gendisk(&optcd_disk);
+	del_gendisk(optcd_disk);
+	put_disk(optcd_disk);
 	if (unregister_blkdev(MAJOR_NR, "optcd") == -EINVAL) {
 		printk(KERN_ERR "optcd: what's that: can't unregister\n");
 		return;
diff -Nru a/drivers/cdrom/sbpcd.c b/drivers/cdrom/sbpcd.c
--- a/drivers/cdrom/sbpcd.c	Mon Oct  7 11:25:26 2002
+++ b/drivers/cdrom/sbpcd.c	Mon Oct  7 11:25:26 2002
@@ -722,7 +722,7 @@
 	u_char mode_xb_8;
 	u_char delay;
 	struct cdrom_device_info *sbpcd_infop;
-	struct gendisk disk;
+	struct gendisk *disk;
 } D_S[NR_SBPCD];
 
 static struct sbpcd_drive *current_drive = D_S;
@@ -5609,6 +5609,7 @@
 
 static devfs_handle_t devfs_handle;
 
+/* FIXME: cleanups after failed allocations are too ugly for words */
 #ifdef MODULE
 int __init __sbpcd_init(void)
 #else
@@ -5830,7 +5831,7 @@
 		sbpcd_infop->dev = mk_kdev(MAJOR_NR, j);
 		sbpcd_infop->handle = p;
 		p->sbpcd_infop = sbpcd_infop;
-		disk = &p->disk;
+		disk = alloc_disk();
 		disk->major = MAJOR_NR;
 		disk->first_minor = j;
 		disk->minor_shift = 0;
@@ -5839,6 +5840,7 @@
 		disk->flags = GENHD_FL_CD;
 		sprintf(nbuff, "c0t%d", p->drv_id);
 		disk->de = devfs_mk_dir(devfs_handle, nbuff, NULL);
+		p->disk = disk;
 		if (register_cdrom(sbpcd_infop))
 		{
 			printk(" sbpcd: Unable to register with Uniform CD-ROm driver\n");
@@ -5869,7 +5871,8 @@
 	for (j=0;j<NR_SBPCD;j++)
 	{
 		if (D_S[j].drv_id==-1) continue;
-		del_gendisk(&D_S[j].disk);
+		del_gendisk(D_S[j].disk);
+		put_disk(D_S[j].disk);
 		vfree(D_S[j].sbp_buf);
 		if (D_S[j].sbp_audsiz>0) vfree(D_S[j].aud_buf);
 		devfs_unregister(D_S[j].disk.de);
diff -Nru a/drivers/cdrom/sjcd.c b/drivers/cdrom/sjcd.c
--- a/drivers/cdrom/sjcd.c	Mon Oct  7 11:25:36 2002
+++ b/drivers/cdrom/sjcd.c	Mon Oct  7 11:25:36 2002
@@ -159,8 +159,6 @@
 
 #define CLEAR_TIMER del_timer( &sjcd_delay_timer )
 
-static int sjcd_cleanup(void);
-
 /*
  * Set up device, i.e., use command line data to set
  * base address.
@@ -1664,14 +1662,7 @@
 	unsigned char major, minor;
 } sjcd_version;
 
-static struct gendisk sjcd_disk =
-{
-	.major = MAJOR_NR,
-	.first_minor = 0,
-	.minor_shift = 0,
-	.fops = &sjcd_fops,
-	.disk_name = "sjcd"
-};
+static struct gendisk *sjcd_disk;
 
 /*
  * Test for presence of drive and initialize it. Called at boot time.
@@ -1698,12 +1689,22 @@
 	blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), do_sjcd_request, &sjcd_lock);
 	blk_queue_hardsect_size(BLK_DEFAULT_QUEUE(MAJOR_NR), 2048);
 
+	sjcd_disk = alloc_disk();
+	if (!sjcd_disk) {
+		printk(KERN_ERR "SJCD: can't allocate disk");
+		goto out1;
+	}
+	sjcd_disk->major = MAJOR_NR,
+	sjcd_disk->first_minor = 0,
+	sjcd_disk->minor_shift = 0,
+	sjcd_disk->fops = &sjcd_fops,
+	sprintf(sjcd_disk->disk_name, "sjcd");
+
 	if (check_region(sjcd_base, 4)) {
 		printk
 		    ("SJCD: Init failed, I/O port (%X) is already in use\n",
 		     sjcd_base);
-		sjcd_cleanup();
-		return (-EIO);
+		goto out2;
 	}
 
 	/*
@@ -1725,8 +1726,7 @@
 	}
 	if (i == 0 || sjcd_command_failed) {
 		printk(" reset failed, no drive found.\n");
-		sjcd_cleanup();
-		return (-EIO);
+		goto out3;
 	} else
 		printk("\n");
 
@@ -1748,8 +1748,7 @@
 	}
 	if (i == 0 || sjcd_command_failed) {
 		printk(" get version failed, no drive found.\n");
-		sjcd_cleanup();
-		return (-EIO);
+		goto out3;
 	}
 
 	if (sjcd_load_response(&sjcd_version, sizeof(sjcd_version)) == 0) {
@@ -1757,8 +1756,7 @@
 		       (int) sjcd_version.minor);
 	} else {
 		printk(" read version failed, no drive found.\n");
-		sjcd_cleanup();
-		return (-EIO);
+		goto out3;
 	}
 
 	/*
@@ -1781,8 +1779,7 @@
 		}
 		if (i == 0 || sjcd_command_failed) {
 			printk(" get status failed, no drive found.\n");
-			sjcd_cleanup();
-			return (-EIO);
+			goto out3;
 		} else
 			printk("\n");
 	}
@@ -1790,33 +1787,32 @@
 	printk(KERN_INFO "SJCD: Status: port=0x%x.\n", sjcd_base);
 	devfs_register(NULL, "sjcd", DEVFS_FL_DEFAULT, MAJOR_NR, 0,
 		       S_IFBLK | S_IRUGO | S_IWUGO, &sjcd_fops, NULL);
-	add_disk(&sjcd_disk);
+	add_disk(sjcd_disk);
 
 	sjcd_present++;
 	return (0);
-}
-
-static int sjcd_cleanup(void)
-{
+out3:
+	release_region(sjcd_base, 4);
+	blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR));
+out2:
+	put_disk(sjcd_disk);
+out1:
 	if ((unregister_blkdev(MAJOR_NR, "sjcd") == -EINVAL))
 		printk("SJCD: cannot unregister device.\n");
-	else {
-		release_region(sjcd_base, 4);
-		blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR));
-	}
-
-	return (0);
+	return (-EIO);
 }
 
-
 static void __exit sjcd_exit(void)
 {
 	devfs_find_and_unregister(NULL, "sjcd", 0, 0, DEVFS_SPECIAL_BLK, 0);
-	del_gendisk(&sjcd_disk);
-	if (sjcd_cleanup())
-		printk("SJCD: module: cannot be removed.\n");
-	else
-		printk(KERN_INFO "SJCD: module: removed.\n");
+	del_gendisk(sjcd_disk);
+	put_disk(sjcd_disk);
+	release_region(sjcd_base, 4);
+	blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR));
+	if ((unregister_blkdev(MAJOR_NR, "sjcd") == -EINVAL))
+		printk("SJCD: cannot unregister device.\n");
+	printk(KERN_INFO "SJCD: module: removed.\n");
+	return (0);
 }
 
 module_init(sjcd_init);
diff -Nru a/drivers/cdrom/sonycd535.c b/drivers/cdrom/sonycd535.c
--- a/drivers/cdrom/sonycd535.c	Mon Oct  7 11:25:29 2002
+++ b/drivers/cdrom/sonycd535.c	Mon Oct  7 11:25:29 2002
@@ -1454,14 +1454,7 @@
 	.check_media_change	= cdu535_check_media_change,
 };
 
-static struct gendisk cdu_disk =
-{
-	.major = MAJOR_NR,
-	.first_minor = 0,
-	.minor_shift = 0,
-	.fops = &cdu_fops,
-	.disk_name = "cdu",
-};
+static struct gendisk *cdu_disk;
 
 /*
  * Initialize the driver.
@@ -1477,6 +1470,7 @@
 	int  tmp_irq;
 	int  i;
 	devfs_handle_t sony_devfs_handle;
+	int err;
 
 	/* Setting the base I/O address to 0 will disable it. */
 	if ((sony535_cd_base_io == 0xffff)||(sony535_cd_base_io == 0))
@@ -1518,145 +1512,142 @@
 		sony_sleep();
 	}
 
-	if (got_result && (check_drive_status() != TIME_OUT)) {
-		/* CD-ROM drive responded --  get the drive configuration */
-		cmd_buff[0] = SONY535_INQUIRY;
-		if (do_sony_cmd(cmd_buff, 1, status,
-						(Byte *)&drive_config, 28, 1) == 0) {
-			/* was able to get the configuration,
-			 * set drive mode as rest of init
-			 */
+	if (!got_result || check_drive_status() == TIME_OUT)
+		goto Enodev;
+
+	/* CD-ROM drive responded --  get the drive configuration */
+	cmd_buff[0] = SONY535_INQUIRY;
+	if (do_sony_cmd(cmd_buff, 1, status, (Byte *)&drive_config, 28, 1) != 0)
+		goto Enodev;
+
+	/* was able to get the configuration,
+	 * set drive mode as rest of init
+	 */
 #if DEBUG > 0
-			/* 0x50 == CADDY_NOT_INSERTED | NOT_SPINNING */
-			if ( (status[0] & 0x7f) != 0 && (status[0] & 0x7f) != 0x50 )
-				printk(CDU535_MESSAGE_NAME
-						"Inquiry command returned status = 0x%x\n", status[0]);
+	/* 0x50 == CADDY_NOT_INSERTED | NOT_SPINNING */
+	if ( (status[0] & 0x7f) != 0 && (status[0] & 0x7f) != 0x50 )
+		printk(CDU535_MESSAGE_NAME
+				"Inquiry command returned status = 0x%x\n", status[0]);
 #endif
-			/* now ready to use interrupts, if available */
-			sony535_irq_used = tmp_irq;
+	/* now ready to use interrupts, if available */
+	sony535_irq_used = tmp_irq;
+
+	/* A negative sony535_irq_used will attempt an autoirq. */
+	if (sony535_irq_used < 0) {
+		unsigned long irq_mask, delay;
+
+		irq_mask = probe_irq_on();
+		enable_interrupts();
+		outb(0, read_status_reg);	/* does a reset? */
+		delay = jiffies + HZ/10;
+		while (time_before(jiffies, delay)) ;
 
-			/* A negative sony535_irq_used will attempt an autoirq. */
-			if (sony535_irq_used < 0) {
-				unsigned long irq_mask, delay;
-
-				irq_mask = probe_irq_on();
-				enable_interrupts();
-				outb(0, read_status_reg);	/* does a reset? */
-				delay = jiffies + HZ/10;
-				while (time_before(jiffies, delay)) ;
-
-				sony535_irq_used = probe_irq_off(irq_mask);
-				disable_interrupts();
-			}
-			if (sony535_irq_used > 0) {
-			    if (request_irq(sony535_irq_used, cdu535_interrupt,
-								SA_INTERRUPT, CDU535_HANDLE, NULL)) {
-					printk("Unable to grab IRQ%d for the " CDU535_MESSAGE_NAME
-							" driver; polling instead.\n", sony535_irq_used);
-					sony535_irq_used = 0;
-				}
-			}
-			cmd_buff[0] = SONY535_SET_DRIVE_MODE;
-			cmd_buff[1] = 0x0;	/* default audio */
-			if (do_sony_cmd(cmd_buff, 2, status, ret_buff, 1, 1) == 0) {
-				/* set the drive mode successful, we are set! */
-				sony_buffer_size = SONY535_BUFFER_SIZE;
-				sony_buffer_sectors = sony_buffer_size / CDU535_BLOCK_SIZE;
-
-				printk(KERN_INFO CDU535_MESSAGE_NAME " I/F CDROM : %8.8s %16.16s %4.4s",
-					   drive_config.vendor_id,
-					   drive_config.product_id,
-					   drive_config.product_rev_level);
-				printk("  base address %03X, ", sony535_cd_base_io);
-				if (tmp_irq > 0)
-					printk("IRQ%d, ", tmp_irq);
-				printk("using %d byte buffer\n", sony_buffer_size);
-
-				sony_devfs_handle = devfs_register (NULL, CDU535_HANDLE,
-								DEVFS_FL_DEFAULT,
-								MAJOR_NR, 0,
-								S_IFBLK | S_IRUGO | S_IWUGO,
-								&cdu_fops, NULL);
-				if (register_blkdev(MAJOR_NR, CDU535_HANDLE, &cdu_fops)) {
-					printk("Unable to get major %d for %s\n",
-							MAJOR_NR, CDU535_MESSAGE_NAME);
-					return -EIO;
-				}
-				blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR),
-						do_cdu535_request,
-						&sonycd535_lock);
-				blk_queue_hardsect_size(BLK_DEFAULT_QUEUE(MAJOR_NR), CDU535_BLOCK_SIZE);
-				sony_toc = (struct s535_sony_toc *)
-					kmalloc(sizeof *sony_toc, GFP_KERNEL);
-				if (sony_toc == NULL) {
-					blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR));
-					unregister_blkdev(MAJOR_NR, CDU535_HANDLE);
-					devfs_unregister(sony_devfs_handle);
-					return -ENOMEM;
-				}
-				last_sony_subcode = (struct s535_sony_subcode *)
-					kmalloc(sizeof *last_sony_subcode, GFP_KERNEL);
-				if (last_sony_subcode == NULL) {
-					blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR));
-					kfree(sony_toc);
-					unregister_blkdev(MAJOR_NR, CDU535_HANDLE);
-					devfs_unregister(sony_devfs_handle);
-					return -ENOMEM;
-				}
-				sony_buffer = (Byte **)
-					kmalloc(4 * sony_buffer_sectors, GFP_KERNEL);
-				if (sony_buffer == NULL) {
-					blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR));
-					kfree(sony_toc);
-					kfree(last_sony_subcode);
-					unregister_blkdev(MAJOR_NR, CDU535_HANDLE);
-					devfs_unregister(sony_devfs_handle);
-					return -ENOMEM;
-				}
-				for (i = 0; i < sony_buffer_sectors; i++) {
-					sony_buffer[i] =
-								(Byte *)kmalloc(CDU535_BLOCK_SIZE, GFP_KERNEL);
-					if (sony_buffer[i] == NULL) {
-						while (--i>=0)
-							kfree(sony_buffer[i]);
-						blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR));
-						kfree(sony_buffer);
-						kfree(sony_toc);
-						kfree(last_sony_subcode);
-						unregister_blkdev(MAJOR_NR, CDU535_HANDLE);
-						devfs_unregister(sony_devfs_handle);
-						return -ENOMEM;
-					}
-				}
-				initialized = 1;
-			}
+		sony535_irq_used = probe_irq_off(irq_mask);
+		disable_interrupts();
+	}
+	if (sony535_irq_used > 0) {
+	    if (request_irq(sony535_irq_used, cdu535_interrupt,
+						SA_INTERRUPT, CDU535_HANDLE, NULL)) {
+			printk("Unable to grab IRQ%d for the " CDU535_MESSAGE_NAME
+					" driver; polling instead.\n", sony535_irq_used);
+			sony535_irq_used = 0;
 		}
 	}
-
-	if (!initialized) {
-		printk("Did not find a " CDU535_MESSAGE_NAME " drive\n");
-		return -EIO;
+	cmd_buff[0] = SONY535_SET_DRIVE_MODE;
+	cmd_buff[1] = 0x0;	/* default audio */
+	if (do_sony_cmd(cmd_buff, 2, status, ret_buff, 1, 1) != 0)
+		goto Enodev_irq;
+
+	/* set the drive mode successful, we are set! */
+	sony_buffer_size = SONY535_BUFFER_SIZE;
+	sony_buffer_sectors = sony_buffer_size / CDU535_BLOCK_SIZE;
+
+	printk(KERN_INFO CDU535_MESSAGE_NAME " I/F CDROM : %8.8s %16.16s %4.4s",
+		   drive_config.vendor_id,
+		   drive_config.product_id,
+		   drive_config.product_rev_level);
+	printk("  base address %03X, ", sony535_cd_base_io);
+	if (tmp_irq > 0)
+		printk("IRQ%d, ", tmp_irq);
+	printk("using %d byte buffer\n", sony_buffer_size);
+
+	sony_devfs_handle = devfs_register (NULL, CDU535_HANDLE,
+					DEVFS_FL_DEFAULT,
+					MAJOR_NR, 0,
+					S_IFBLK | S_IRUGO | S_IWUGO,
+					&cdu_fops, NULL);
+	if (register_blkdev(MAJOR_NR, CDU535_HANDLE, &cdu_fops)) {
+		printk("Unable to get major %d for %s\n",
+				MAJOR_NR, CDU535_MESSAGE_NAME);
+		err = -EIO;
+		goto out1;
 	}
-	if (!request_region(sony535_cd_base_io, 4, CDU535_HANDLE))
-		{
-		printk(KERN_WARNING"sonycd535: Unable to request region 0x%x\n",
-			sony535_cd_base_io);
-		for (i = 0; i < sony_buffer_sectors; i++)
-			if (sony_buffer[i]) 
+	blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), do_cdu535_request,
+			&sonycd535_lock);
+	blk_queue_hardsect_size(BLK_DEFAULT_QUEUE(MAJOR_NR), CDU535_BLOCK_SIZE);
+	sony_toc = kamlloc(sizeof(struct s535_sony_toc), GFP_KERNEL);
+	err = -ENOMEM;
+	if (!sony_toc)
+		goto out2;
+	last_sony_subcode = kmalloc(sizeof(struct s535_sony_subcode), GFP_KERNEL);
+	if (!last_sony_subcode)
+		goto out3;
+	sony_buffer = kmalloc(sizeof(Byte *) * sony_buffer_sectors, GFP_KERNEL);
+	if (!sony_buffer)
+		goto out4;
+	for (i = 0; i < sony_buffer_sectors; i++) {
+		sony_buffer[i] = kmalloc(CDU535_BLOCK_SIZE, GFP_KERNEL);
+		if (!sony_buffer[i]) {
+			while (--i>=0)
 				kfree(sony_buffer[i]);
-		blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR));
-		kfree(sony_buffer);
-		kfree(sony_toc);
-		kfree(last_sony_subcode);
-		unregister_blkdev(MAJOR_NR, CDU535_HANDLE);
-		devfs_unregister(sony_devfs_handle);
-		if (sony535_irq_used)
-			free_irq(sony535_irq_used, NULL);
-
-		return -EIO;
+			goto out5;
 		}
-	add_disk(&cdu_disk);
+	}
+	initialized = 1;
+
+	cdu_disk = alloc_disk();
+	if (!cdu_disk)
+		goto out6;
+	cdu_disk->major = MAJOR_NR;
+	cdu_disk->first_minor = 0;
+	cdu_disk->minor_shift = 0;
+	cdu_disk->fops = &cdu_fops;
+	sprintf(cdu_disk->disk_name, "cdu");
+
+	if (!request_region(sony535_cd_base_io, 4, CDU535_HANDLE)) {
+		printk(KERN_WARNING"sonycd535: Unable to request region 0x%x\n",
+			sony535_cd_base_io);
+		goto out7;
+	}
+	add_disk(cdu_disk);
 	return 0;
+
+out7:
+	put_disk(cdu_disk);
+out6:
+	for (i = 0; i < sony_buffer_sectors; i++)
+		if (sony_buffer[i]) 
+			kfree(sony_buffer[i]);
+out5:
+	kfree(sony_buffer);
+out4:
+	kfree(last_sony_subcode);
+out3:
+	kfree(sony_toc);
+out2:
+	blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR));
+	unregister_blkdev(MAJOR_NR, CDU535_HANDLE);
+out1:
+	devfs_unregister(sony_devfs_handle);
+	if (sony535_irq_used)
+		free_irq(sony535_irq_used, NULL);
+	return err;
+Enodev_irq:
+	if (sony535_irq_used)
+		free_irq(sony535_irq_used, NULL);
+Enodev:
+	printk("Did not find a " CDU535_MESSAGE_NAME " drive\n");
+	return -EIO;
 }
 
 #ifndef MODULE
@@ -1707,7 +1698,8 @@
 	kfree(sony_toc);
 	devfs_find_and_unregister(NULL, CDU535_HANDLE, 0, 0,
 				  DEVFS_SPECIAL_BLK, 0);
-	del_gendisk(&cdu_disk);
+	del_gendisk(cdu_disk);
+	put_disk(cdu_disk);
 	if (unregister_blkdev(MAJOR_NR, CDU535_HANDLE) == -EINVAL)
 		printk("Uh oh, couldn't unregister " CDU535_HANDLE "\n");
 	else
diff -Nru a/drivers/char/Config.help b/drivers/char/Config.help
--- a/drivers/char/Config.help	Mon Oct  7 11:25:33 2002
+++ b/drivers/char/Config.help	Mon Oct  7 11:25:33 2002
@@ -648,6 +648,21 @@
 
   If unsure, say N.
 
+CONFIG_AMD_RNG
+  This driver provides kernel-side support for the Random Number
+  Generator hardware found on AMD 76x based motherboards.
+
+  Both a character driver, used to read() entropy data, and a timer
+  function which automatically adds entropy directly into the
+  kernel pool, are exported by this driver.
+
+  To compile this driver as a module ( = code which can be inserted in
+  and removed from the running kernel whenever you want), say M here
+  and read <file:Documentation/modules.txt>. The module will be called
+  i810_rng.o.
+
+  If unsure, say N.
+
 CONFIG_WATCHDOG
   If you say Y here (and to one of the following options) and create a
   character special file /dev/watchdog with major number 10 and minor
@@ -803,6 +818,12 @@
   module, say M here and read <file:Documentation/modules.txt>.  Most
   people will say N.
 
+CONFIG_SCx200_WDT
+  Enable the built-in watchdog timer support on the National 
+  Semiconductor SCx200 processors.
+
+  If compiled as a module, it will be called scx200_watchdog.o.
+
 CONFIG_MACHZ_WDT
   If you are using a ZF Micro MachZ processor, say Y here, otherwise
   N.  This is the driver for the watchdog timer builtin on that
@@ -1005,4 +1026,10 @@
   The raw driver permits block devices to be bound to /dev/raw/rawN. 
   Once bound, I/O against /dev/raw/rawN uses efficient zero-copy I/O. 
   See the raw(8) manpage for more details.
+
+CONFIG_SCx200_GPIO
+  Give userspace access to the GPIO pins on the National
+  Semiconductor SCx200 processors.
+
+  If compiled as a module, it will be called scx200_gpio.o.
 
diff -Nru a/drivers/char/Config.in b/drivers/char/Config.in
--- a/drivers/char/Config.in	Mon Oct  7 11:25:26 2002
+++ b/drivers/char/Config.in	Mon Oct  7 11:25:26 2002
@@ -131,6 +131,7 @@
    tristate '  IB700 SBC Watchdog Timer' CONFIG_IB700_WDT
    tristate '  Intel i810 TCO timer / Watchdog' CONFIG_I810_TCO
    tristate '  Mixcom Watchdog' CONFIG_MIXCOMWD 
+   tristate '  NatSemi SCx200 Watchdog' CONFIG_SCx200_WDT
    tristate '  SBC-60XX Watchdog Timer' CONFIG_60XX_WDT
    tristate '  W83877F (EMACS) Watchdog Timer' CONFIG_W83877F_WDT
    tristate '  ZF MachZ Watchdog' CONFIG_MACHZ_WDT
@@ -149,6 +150,9 @@
 if [ "$CONFIG_X86" = "y" -o "$CONFIG_IA64" = "y" ]; then
    dep_tristate 'Intel i8x0 Random Number Generator support' CONFIG_INTEL_RNG $CONFIG_PCI
 fi
+if [ "$CONFIG_X86" = "y" ]; then
+   dep_tristate 'AMD 768 Random Number Generator support' CONFIG_AMD_RNG $CONFIG_PCI
+fi
 tristate '/dev/nvram support' CONFIG_NVRAM
 tristate 'Enhanced Real Time Clock Support' CONFIG_RTC
 if [ "$CONFIG_RTC" != "y" ]; then
@@ -189,6 +193,8 @@
 if [ "$CONFIG_X86" = "y" ]; then
    tristate 'ACP Modem (Mwave) support' CONFIG_MWAVE
 fi
+
+dep_tristate 'NatSemi SCx200 GPIO Support' CONFIG_SCx200_GPIO $CONFIG_SCx200
 
 tristate '  RAW driver (/dev/raw/rawN)' CONFIG_RAW_DRIVER
 
diff -Nru a/drivers/char/Makefile b/drivers/char/Makefile
--- a/drivers/char/Makefile	Mon Oct  7 11:25:39 2002
+++ b/drivers/char/Makefile	Mon Oct  7 11:25:39 2002
@@ -67,6 +67,7 @@
 obj-$(CONFIG_I8K) += i8k.o
 obj-$(CONFIG_DS1620) += ds1620.o
 obj-$(CONFIG_INTEL_RNG) += i810_rng.o
+obj-$(CONFIG_AMD_RNG) += amd768_rng.o
 obj-$(CONFIG_QIC02_TAPE) += tpqic02.o
 obj-$(CONFIG_FTAPE) += ftape/
 obj-$(CONFIG_H8) += h8.o
@@ -74,6 +75,7 @@
 obj-$(CONFIG_DZ) += dz.o
 obj-$(CONFIG_NWBUTTON) += nwbutton.o
 obj-$(CONFIG_NWFLASH) += nwflash.o
+obj-$(CONFIG_SCx200_GPIO) += scx200_gpio.o
 
 # Only one watchdog can succeed. We probe the hardware watchdog
 # drivers first, then the softdog driver.  This means if your hardware
@@ -85,6 +87,7 @@
 obj-$(CONFIG_ADVANTECH_WDT) += advantechwdt.o
 obj-$(CONFIG_IB700_WDT) += ib700wdt.o
 obj-$(CONFIG_MIXCOMWD) += mixcomwd.o
+obj-$(CONFIG_SCx200_WDT) += scx200_wdt.o
 obj-$(CONFIG_60XX_WDT) += sbc60xxwdt.o
 obj-$(CONFIG_WDT) += wdt.o
 obj-$(CONFIG_WDTPCI) += wdt_pci.o
diff -Nru a/drivers/char/amd768_rng.c b/drivers/char/amd768_rng.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/char/amd768_rng.c	Mon Oct  7 11:25:41 2002
@@ -0,0 +1,295 @@
+/*
+ 	Hardware driver for the AMD 768 Random Number Generator (RNG)
+	(c) Copyright 2001 Red Hat Inc <alan@redhat.com>
+ 
+ 	derived from
+ 
+	Hardware driver for Intel i810 Random Number Generator (RNG)
+	Copyright 2000,2001 Jeff Garzik <jgarzik@mandrakesoft.com>
+	Copyright 2000,2001 Philipp Rumpf <prumpf@mandrakesoft.com>
+
+	Please read Documentation/i810_rng.txt for details on use.
+
+	----------------------------------------------------------
+	This software may be used and distributed according to the terms
+        of the GNU General Public License, incorporated herein by reference.
+
+ */
+
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/random.h>
+#include <linux/miscdevice.h>
+#include <linux/smp_lock.h>
+#include <linux/mm.h>
+#include <linux/delay.h>
+
+#include <asm/io.h>
+#include <asm/uaccess.h>
+
+
+/*
+ * core module and version information
+ */
+#define RNG_VERSION "0.1.0"
+#define RNG_MODULE_NAME "amd768_rng"
+#define RNG_DRIVER_NAME   RNG_MODULE_NAME " hardware driver " RNG_VERSION
+#define PFX RNG_MODULE_NAME ": "
+
+
+/*
+ * debugging macros
+ */
+#undef RNG_DEBUG /* define to enable copious debugging info */
+
+#ifdef RNG_DEBUG
+/* note: prints function name for you */
+#define DPRINTK(fmt, args...) printk(KERN_DEBUG "%s: " fmt, __FUNCTION__ , ## args)
+#else
+#define DPRINTK(fmt, args...)
+#endif
+
+#undef RNG_NDEBUG        /* define to disable lightweight runtime checks */
+#ifdef RNG_NDEBUG
+#define assert(expr)
+#else
+#define assert(expr) \
+        if(!(expr)) {                                   \
+        printk( "Assertion failed! %s,%s,%s,line=%d\n", \
+        #expr,__FILE__,__FUNCTION__,__LINE__);          \
+        }
+#endif
+
+#define RNG_MISCDEV_MINOR		183 /* official */
+
+/*
+ * various RNG status variables.  they are globals
+ * as we only support a single RNG device
+ */
+
+static u32 pmbase;			/* PMxx I/O base */
+static struct semaphore rng_open_sem;	/* Semaphore for serializing rng_open/release */
+
+
+/*
+ * inlined helper functions for accessing RNG registers
+ */
+
+static inline int rng_data_present (void)
+{
+      	return inl(pmbase+0xF4) & 1;
+}
+
+
+static inline int rng_data_read (void)
+{
+	return inl(pmbase+0xF0);
+}
+
+static int rng_dev_open (struct inode *inode, struct file *filp)
+{
+	if ((filp->f_mode & FMODE_READ) == 0)
+		return -EINVAL;
+	if (filp->f_mode & FMODE_WRITE)
+		return -EINVAL;
+
+	/* wait for device to become free */
+	if (filp->f_flags & O_NONBLOCK) {
+		if (down_trylock (&rng_open_sem))
+			return -EAGAIN;
+	} else {
+		if (down_interruptible (&rng_open_sem))
+			return -ERESTARTSYS;
+	}
+	return 0;
+}
+
+
+static int rng_dev_release (struct inode *inode, struct file *filp)
+{
+	up(&rng_open_sem);
+	return 0;
+}
+
+
+static ssize_t rng_dev_read (struct file *filp, char *buf, size_t size,
+			     loff_t * offp)
+{
+	static spinlock_t rng_lock = SPIN_LOCK_UNLOCKED;
+	int have_data;
+	u32 data = 0;
+	ssize_t ret = 0;
+
+	while (size) {
+		spin_lock(&rng_lock);
+
+		have_data = 0;
+		if (rng_data_present()) {
+			data = rng_data_read();
+			have_data = 4;
+		}
+
+		spin_unlock (&rng_lock);
+
+		while (have_data > 0) {
+			if (put_user((u8)data, buf++)) {
+				ret = ret ? : -EFAULT;
+				break;
+			}
+			size--;
+			ret++;
+			have_data--;
+			data>>=8;
+		}
+
+		if (filp->f_flags & O_NONBLOCK)
+			return ret ? : -EAGAIN;
+
+		if(need_resched())
+		{
+			current->state = TASK_INTERRUPTIBLE;
+			schedule_timeout(1);
+		}
+		else
+			udelay(200);	/* FIXME: We could poll for 250uS ?? */
+
+		if (signal_pending (current))
+			return ret ? : -ERESTARTSYS;
+	}
+	return ret;
+}
+
+
+static struct file_operations rng_chrdev_ops = {
+	owner:		THIS_MODULE,
+	open:		rng_dev_open,
+	release:	rng_dev_release,
+	read:		rng_dev_read,
+};
+
+
+static struct miscdevice rng_miscdev = {
+	RNG_MISCDEV_MINOR,
+	RNG_MODULE_NAME,
+	&rng_chrdev_ops,
+};
+
+
+/*
+ * rng_init_one - look for and attempt to init a single RNG
+ */
+static int __init rng_init_one (struct pci_dev *dev)
+{
+	int rc;
+	u8 rnen;
+
+	DPRINTK ("ENTER\n");
+
+	rc = misc_register (&rng_miscdev);
+	if (rc) {
+		printk (KERN_ERR PFX "cannot register misc device\n");
+		DPRINTK ("EXIT, returning %d\n", rc);
+		goto err_out;
+	}
+
+	pci_read_config_dword(dev, 0x58, &pmbase);
+
+	pmbase&=0x0000FF00;
+
+	if(pmbase == 0)
+	{
+		printk (KERN_ERR PFX "power management base not set\n");
+		DPRINTK ("EXIT, returning %d\n", rc);
+		goto err_out_free_miscdev;
+	}
+
+	pci_read_config_byte(dev, 0x40, &rnen);
+	rnen|=(1<<7);	/* RNG on */
+	pci_write_config_byte(dev, 0x40, rnen);
+
+	pci_read_config_byte(dev, 0x41, &rnen);
+	rnen|=(1<<7);	/* PMIO enable */
+	pci_write_config_byte(dev, 0x41, rnen);
+
+	printk(KERN_INFO PFX "AMD768 system management I/O registers at 0x%X.\n", pmbase);
+	DPRINTK ("EXIT, returning 0\n");
+	return 0;
+
+err_out_free_miscdev:
+	misc_deregister (&rng_miscdev);
+err_out:
+	return rc;
+}
+
+
+/*
+ * Data for PCI driver interface
+ *
+ * This data only exists for exporting the supported
+ * PCI ids via MODULE_DEVICE_TABLE.  We do not actually
+ * register a pci_driver, because someone else might one day
+ * want to register another driver on the same PCI id.
+ */
+static struct pci_device_id rng_pci_tbl[] __initdata = {
+	{ 0x1022, 0x7443, PCI_ANY_ID, PCI_ANY_ID, },
+	{ 0, },
+};
+MODULE_DEVICE_TABLE (pci, rng_pci_tbl);
+
+
+MODULE_AUTHOR("Alan Cox, Jeff Garzik, Philipp Rumpf, Matt Sottek");
+MODULE_DESCRIPTION("AMD 768 Random Number Generator (RNG) driver");
+MODULE_LICENSE("GPL");
+
+
+/*
+ * rng_init - initialize RNG module
+ */
+static int __init rng_init (void)
+{
+	int rc;
+	struct pci_dev *pdev;
+
+	DPRINTK ("ENTER\n");
+
+	init_MUTEX (&rng_open_sem);
+
+	pci_for_each_dev(pdev) {
+		if (pci_match_device (rng_pci_tbl, pdev) != NULL)
+			goto match;
+	}
+
+	DPRINTK ("EXIT, returning -ENODEV\n");
+	return -ENODEV;
+
+match:
+	rc = rng_init_one (pdev);
+	if (rc)
+		return rc;
+
+	printk (KERN_INFO RNG_DRIVER_NAME " loaded\n");
+
+	DPRINTK ("EXIT, returning 0\n");
+	return 0;
+}
+
+
+/*
+ * rng_init - shutdown RNG module
+ */
+static void __exit rng_cleanup (void)
+{
+	DPRINTK ("ENTER\n");
+	misc_deregister (&rng_miscdev);
+	DPRINTK ("EXIT\n");
+}
+
+
+module_init (rng_init);
+module_exit (rng_cleanup);
diff -Nru a/drivers/char/cyclades.c b/drivers/char/cyclades.c
--- a/drivers/char/cyclades.c	Mon Oct  7 11:25:26 2002
+++ b/drivers/char/cyclades.c	Mon Oct  7 11:25:26 2002
@@ -712,8 +712,6 @@
 
 #define	JIFFIES_DIFF(n, j)	((j) - (n))
 
-static DECLARE_TASK_QUEUE(tq_cyclades);
-
 static struct tty_driver cy_serial_driver, cy_callout_driver;
 static int serial_refcount;
 
@@ -934,8 +932,7 @@
 cy_sched_event(struct cyclades_port *info, int event)
 {
     info->event |= 1 << event; /* remember what kind of event and who */
-    queue_task(&info->tqueue, &tq_cyclades); /* it belongs to */
-    mark_bh(CYCLADES_BH);                       /* then trigger event */
+    schedule_work(&info->tqueue);
 } /* cy_sched_event */
 
 
@@ -951,9 +948,7 @@
  * This is done through one level of indirection--the task queue.
  * When a hardware interrupt service routine wants service by the
  * driver's bottom half, it enqueues the appropriate tq_struct (one
- * per port) to the tq_cyclades work queue and sets a request flag
- * via mark_bh for processing that queue.  When the time is right,
- * do_cyclades_bh is called (because of the mark_bh) and it requests
+ * per port) to the keventd work queue and sets a request flag
  * that the work queue be processed.
  *
  * Although this may seem unwieldy, it gives the system a way to
@@ -962,12 +957,6 @@
  * had to poll every port to see if that port needed servicing.
  */
 static void
-do_cyclades_bh(void)
-{
-    run_task_queue(&tq_cyclades);
-} /* do_cyclades_bh */
-
-static void
 do_softint(void *private_)
 {
   struct cyclades_port *info = (struct cyclades_port *) private_;
@@ -1291,7 +1280,7 @@
 #endif
                             }
                         }
-                        queue_task(&tty->flip.tqueue, &tq_timer);
+                        schedule_delayed_work(&tty->flip.work, 1);
                     }
                     /* end of service */
                     cy_writeb((u_long)base_addr+(CyRIR<<index), (save_xir & 0x3f));
@@ -1673,7 +1662,7 @@
 	    }
 #endif
 	    info->idle_stats.recv_idle = jiffies;
-	    queue_task(&tty->flip.tqueue, &tq_timer);
+	    schedule_delayed_work(&tty->flip.work, 1);
 	}
 	/* Update rx_get */
 	cy_writel(&buf_ctrl->rx_get, new_rx_get);
@@ -1888,7 +1877,7 @@
 	if(delta_count)
 	    cy_sched_event(info, Cy_EVENT_DELTA_WAKEUP);
 	if(special_count)
-	    queue_task(&tty->flip.tqueue, &tq_timer);
+	    schedule_delayed_work(&tty->flip.work, 1);
     }
 }
 
@@ -5513,8 +5502,6 @@
   unsigned short chip_number;
   int nports;
 
-    init_bh(CYCLADES_BH, do_cyclades_bh);
-
     show_version();
 
     /* Initialize the tty_driver structure */
@@ -5661,8 +5648,7 @@
                     info->blocked_open = 0;
                     info->default_threshold = 0;
                     info->default_timeout = 0;
-                    info->tqueue.routine = do_softint;
-                    info->tqueue.data = info;
+		    INIT_WORK(&info->tqueue, do_softint, info);
                     info->callout_termios =
 		                cy_callout_driver.init_termios;
                     info->normal_termios =
@@ -5740,8 +5726,7 @@
                     info->blocked_open = 0;
                     info->default_threshold = 0;
                     info->default_timeout = 0;
-                    info->tqueue.routine = do_softint;
-                    info->tqueue.data = info;
+		    INIT_WORK(&info->tqueue, do_softint, info);
                     info->callout_termios =
 		               cy_callout_driver.init_termios;
                     info->normal_termios =
@@ -5791,7 +5776,6 @@
 #endif /* CONFIG_CYZ_INTR */
 
     save_flags(flags); cli();
-    remove_bh(CYCLADES_BH);
 
     if ((e1 = tty_unregister_driver(&cy_serial_driver)))
             printk("cyc: failed to unregister Cyclades serial driver(%d)\n",
diff -Nru a/drivers/char/drm/Config.in b/drivers/char/drm/Config.in
--- a/drivers/char/drm/Config.in	Mon Oct  7 11:25:39 2002
+++ b/drivers/char/drm/Config.in	Mon Oct  7 11:25:39 2002
@@ -8,7 +8,7 @@
 bool 'Direct Rendering Manager (XFree86 4.1.0 and higher DRI support)' CONFIG_DRM
 if [ "$CONFIG_DRM" != "n" ]; then
     tristate '  3dfx Banshee/Voodoo3+' CONFIG_DRM_TDFX
-    tristate '  3dlabs GMX 2000' CONFIG_DRM_GAMMA
+#    tristate '  3dlabs GMX 2000' CONFIG_DRM_GAMMA
     tristate '  ATI Rage 128' CONFIG_DRM_R128
     dep_tristate '  ATI Radeon' CONFIG_DRM_RADEON $CONFIG_AGP
     dep_tristate '  Intel I810' CONFIG_DRM_I810 $CONFIG_AGP
diff -Nru a/drivers/char/drm/ati_pcigart.h b/drivers/char/drm/ati_pcigart.h
--- a/drivers/char/drm/ati_pcigart.h	Mon Oct  7 11:25:38 2002
+++ b/drivers/char/drm/ati_pcigart.h	Mon Oct  7 11:25:38 2002
@@ -30,14 +30,20 @@
 #define __NO_VERSION__
 #include "drmP.h"
 
-#if PAGE_SIZE == 8192
+#if PAGE_SIZE == 65536
+# define ATI_PCIGART_TABLE_ORDER	0
+# define ATI_PCIGART_TABLE_PAGES	(1 << 0)
+#elif PAGE_SIZE == 16384
+# define ATI_PCIGART_TABLE_ORDER	1
+# define ATI_PCIGART_TABLE_PAGES	(1 << 1)
+#elif PAGE_SIZE == 8192
 # define ATI_PCIGART_TABLE_ORDER 	2
 # define ATI_PCIGART_TABLE_PAGES 	(1 << 2)
 #elif PAGE_SIZE == 4096
 # define ATI_PCIGART_TABLE_ORDER 	3
 # define ATI_PCIGART_TABLE_PAGES 	(1 << 3)
 #else
-# error - PAGE_SIZE not 8K or 4K
+# error - PAGE_SIZE not 64K, 16K, 8K or 4K
 #endif
 
 # define ATI_MAX_PCIGART_PAGES		8192	/* 32 MB aperture, 4K pages */
diff -Nru a/drivers/char/drm/drm.h b/drivers/char/drm/drm.h
--- a/drivers/char/drm/drm.h	Mon Oct  7 11:25:27 2002
+++ b/drivers/char/drm/drm.h	Mon Oct  7 11:25:27 2002
@@ -345,6 +345,19 @@
 	int funcnum;
 } drm_irq_busid_t;
 
+typedef enum {
+    _DRM_VBLANK_ABSOLUTE = 0x0,	/* Wait for specific vblank sequence number */
+    _DRM_VBLANK_RELATIVE = 0x1	/* Wait for given number of vblanks */
+} drm_vblank_seq_type_t;
+
+typedef struct drm_radeon_vbl_wait {
+	drm_vblank_seq_type_t type;
+	unsigned int sequence;
+	long tval_sec;
+	long tval_usec;
+} drm_wait_vblank_t;
+
+
 typedef struct drm_agp_mode {
 	unsigned long mode;
 } drm_agp_mode_t;
@@ -438,6 +451,8 @@
 
 #define DRM_IOCTL_SG_ALLOC		DRM_IOW( 0x38, drm_scatter_gather_t)
 #define DRM_IOCTL_SG_FREE		DRM_IOW( 0x39, drm_scatter_gather_t)
+
+#define DRM_IOCTL_WAIT_VBLANK		DRM_IOWR(0x3a, drm_wait_vblank_t)
 
 /* Device specfic ioctls should only be in their respective headers
  * The device specific ioctl range is 0x40 to 0x79.                  */
diff -Nru a/drivers/char/drm/drmP.h b/drivers/char/drm/drmP.h
--- a/drivers/char/drm/drmP.h	Mon Oct  7 11:25:36 2002
+++ b/drivers/char/drm/drmP.h	Mon Oct  7 11:25:36 2002
@@ -66,7 +66,7 @@
 #include <linux/types.h>
 #include <linux/agp_backend.h>
 #endif
-#include <linux/tqueue.h>
+#include <linux/workqueue.h>
 #include <linux/poll.h>
 #include <asm/pgalloc.h>
 #include "drm.h"
@@ -575,7 +575,11 @@
 	int		  last_checked;	/* Last context checked for DMA	   */
 	int		  last_context;	/* Last current context		   */
 	unsigned long	  last_switch;	/* jiffies at last context switch  */
-	struct tq_struct  tq;
+	struct work_struct		  work;
+#if __HAVE_VBL_IRQ
+   	wait_queue_head_t vbl_queue;
+   	atomic_t          vbl_received;
+#endif
 	cycles_t	  ctx_start;
 	cycles_t	  lck_start;
 #if __HAVE_DMA_HISTOGRAM
@@ -808,6 +812,14 @@
 extern int           DRM(irq_uninstall)( drm_device_t *dev );
 extern void          DRM(dma_service)( int irq, void *device,
 				       struct pt_regs *regs );
+extern void          DRM(driver_irq_preinstall)( drm_device_t *dev );
+extern void          DRM(driver_irq_postinstall)( drm_device_t *dev );
+extern void          DRM(driver_irq_uninstall)( drm_device_t *dev );
+#if __HAVE_VBL_IRQ
+extern int           DRM(wait_vblank)(struct inode *inode, struct file *filp,
+				      unsigned int cmd, unsigned long arg);
+extern int           DRM(vblank_wait)(drm_device_t *dev, unsigned int *vbl_seq);
+#endif
 #if __HAVE_DMA_IRQ_BH
 extern void          DRM(dma_immediate_bh)( void *dev );
 #endif
diff -Nru a/drivers/char/drm/drm_dma.h b/drivers/char/drm/drm_dma.h
--- a/drivers/char/drm/drm_dma.h	Mon Oct  7 11:25:36 2002
+++ b/drivers/char/drm/drm_dma.h	Mon Oct  7 11:25:36 2002
@@ -532,14 +532,15 @@
 	dev->dma->this_buffer = NULL;
 
 #if __HAVE_DMA_IRQ_BH
-	INIT_LIST_HEAD( &dev->tq.list );
-	dev->tq.sync = 0;
-	dev->tq.routine = DRM(dma_immediate_bh);
-	dev->tq.data = dev;
+	INIT_WORK(&dev->work, DRM(dma_immediate_bh), dev);
+#endif
+
+#if __HAVE_VBL_IRQ
+	init_waitqueue_head(&dev->vbl_queue);
 #endif
 
 				/* Before installing handler */
-	DRIVER_PREINSTALL();
+	DRM(driver_irq_preinstall)(dev);
 
 				/* Install handler */
 	ret = request_irq( dev->irq, DRM(dma_service),
@@ -552,7 +553,7 @@
 	}
 
 				/* After installing handler */
-	DRIVER_POSTINSTALL();
+	DRM(driver_irq_postinstall)(dev);
 
 	return 0;
 }
@@ -571,7 +572,7 @@
 
 	DRM_DEBUG( "%s: irq=%d\n", __FUNCTION__, irq );
 
-	DRIVER_UNINSTALL();
+	DRM(driver_irq_uninstall)( dev );
 
 	free_irq( irq, dev );
 
@@ -597,6 +598,40 @@
 		return -EINVAL;
 	}
 }
+
+#if __HAVE_VBL_IRQ
+
+int DRM(wait_vblank)( DRM_IOCTL_ARGS )
+{
+	drm_file_t *priv = filp->private_data;
+	drm_device_t *dev = priv->dev;
+	drm_wait_vblank_t vblwait;
+	struct timeval now;
+	int ret;
+
+	if (!dev->irq)
+		return -EINVAL;
+
+	DRM_COPY_FROM_USER_IOCTL( vblwait, (drm_wait_vblank_t *)data,
+				  sizeof(vblwait) );
+
+	if ( vblwait.type == _DRM_VBLANK_RELATIVE ) {
+		vblwait.sequence += atomic_read( &dev->vbl_received );
+	}
+
+	ret = DRM(vblank_wait)( dev, &vblwait.sequence );
+
+	do_gettimeofday( &now );
+	vblwait.tval_sec = now.tv_sec;
+	vblwait.tval_usec = now.tv_usec;
+
+	DRM_COPY_TO_USER_IOCTL( (drm_wait_vblank_t *)data, vblwait,
+				sizeof(vblwait) );
+
+	return ret;
+}
+
+#endif	/* __HAVE_VBL_IRQ */
 
 #else
 
diff -Nru a/drivers/char/drm/drm_drv.h b/drivers/char/drm/drm_drv.h
--- a/drivers/char/drm/drm_drv.h	Mon Oct  7 11:25:28 2002
+++ b/drivers/char/drm/drm_drv.h	Mon Oct  7 11:25:28 2002
@@ -222,6 +222,10 @@
 	[DRM_IOCTL_NR(DRM_IOCTL_SG_FREE)]       = { DRM(sg_free),     1, 1 },
 #endif
 
+#if __HAVE_VBL_IRQ
+	[DRM_IOCTL_NR(DRM_IOCTL_WAIT_VBLANK)]   = { DRM(wait_vblank), 0, 0 },
+#endif
+
 	DRIVER_IOCTLS
 };
 
diff -Nru a/drivers/char/drm/drm_os_linux.h b/drivers/char/drm/drm_os_linux.h
--- a/drivers/char/drm/drm_os_linux.h	Mon Oct  7 11:25:31 2002
+++ b/drivers/char/drm/drm_os_linux.h	Mon Oct  7 11:25:31 2002
@@ -44,16 +44,47 @@
 #define DRM_MALLOC(x) kmalloc(x, GFP_KERNEL)
 #define DRM_FREE(x) kfree(x)
 
-#define DRM_GETSAREA()								 \
-do { 										 \
-	struct list_head *list;							 \
-	list_for_each( list, &dev->maplist->head ) {				 \
-		drm_map_list_t *entry = (drm_map_list_t *)list;			 \
-		if ( entry->map &&						 \
-		     entry->map->type == _DRM_SHM &&				 \
-		     (entry->map->flags & _DRM_CONTAINS_LOCK) ) {		 \
-			dev_priv->sarea = entry->map;				 \
- 			break;							 \
- 		}								 \
- 	}									 \
+#define DRM_GETSAREA()							 \
+do { 									 \
+	struct list_head *list;						 \
+	list_for_each( list, &dev->maplist->head ) {			 \
+		drm_map_list_t *entry = (drm_map_list_t *)list;		 \
+		if ( entry->map &&					 \
+		     entry->map->type == _DRM_SHM &&			 \
+		     (entry->map->flags & _DRM_CONTAINS_LOCK) ) {	 \
+			dev_priv->sarea = entry->map;			 \
+ 			break;						 \
+ 		}							 \
+ 	}								 \
 } while (0)
+
+#define DRM_HZ HZ
+
+#define DRM_WAIT_ON( ret, queue, timeout, condition )	\
+do {							\
+	DECLARE_WAITQUEUE(entry, current);		\
+	unsigned long end = jiffies + (timeout);	\
+	add_wait_queue(&(queue), &entry);		\
+							\
+	for (;;) {					\
+		current->state = TASK_INTERRUPTIBLE;	\
+		if (condition) 				\
+			break;				\
+		if((signed)(end - jiffies) <= 0) {	\
+			ret = -EBUSY;			\
+			break;				\
+		}					\
+		schedule_timeout(max(HZ/100,1));	\
+		if (signal_pending(current)) {		\
+			ret = -EINTR;			\
+			break;				\
+		}					\
+	}						\
+	current->state = TASK_RUNNING;			\
+	remove_wait_queue(&(queue), &entry);		\
+} while (0)
+
+
+#define DRM_WAKEUP( queue ) wake_up_interruptible( queue )
+#define DRM_INIT_WAITQUEUE( queue ) init_waitqueue_head( queue )
+ 
diff -Nru a/drivers/char/drm/gamma_dma.c b/drivers/char/drm/gamma_dma.c
--- a/drivers/char/drm/gamma_dma.c	Mon Oct  7 11:25:39 2002
+++ b/drivers/char/drm/gamma_dma.c	Mon Oct  7 11:25:39 2002
@@ -128,8 +128,7 @@
 		clear_bit(0, &dev->dma_flag);
 
 				/* Dispatch new buffer */
-		queue_task(&dev->tq, &tq_immediate);
-		mark_bh(IMMEDIATE_BH);
+		schedule_work(&dev->tq);
 	}
 }
 
diff -Nru a/drivers/char/drm/radeon.h b/drivers/char/drm/radeon.h
--- a/drivers/char/drm/radeon.h	Mon Oct  7 11:25:38 2002
+++ b/drivers/char/drm/radeon.h	Mon Oct  7 11:25:38 2002
@@ -100,6 +100,13 @@
  [DRM_IOCTL_NR(DRM_IOCTL_RADEON_IRQ_EMIT)]   = { radeon_irq_emit, 1, 0 }, \
  [DRM_IOCTL_NR(DRM_IOCTL_RADEON_IRQ_WAIT)]   = { radeon_irq_wait, 1, 0 },
 
+
+#define USE_IRQS 1
+#if USE_IRQS
+#define __HAVE_DMA_IRQ		1
+#define __HAVE_VBL_IRQ		1
+#define __HAVE_SHARED_IRQ       1
+
 /* When a client dies:
  *    - Check for and clean up flipped page state
  *    - Free any alloced agp memory.
@@ -128,56 +135,14 @@
 	}								\
 } while (0)
 
+#else
+#define __HAVE_DMA_IRQ 0
+#endif
+
 /* DMA customization:
  */
 #define __HAVE_DMA		1
 
-
-#define __HAVE_DMA_IRQ		1
-#define __HAVE_DMA_IRQ_BH	1 
-#define __HAVE_SHARED_IRQ       1
-
-#define DRIVER_PREINSTALL() do {				\
-	drm_radeon_private_t *dev_priv =			\
-		(drm_radeon_private_t *)dev->dev_private;	\
-	u32 tmp;						\
-								\
-	/* Clear bit if it's already high: */			\
-   	tmp = RADEON_READ( RADEON_GEN_INT_STATUS );		\
-   	tmp = tmp & RADEON_SW_INT_TEST_ACK;			\
-   	RADEON_WRITE( RADEON_GEN_INT_STATUS, tmp );		\
-								\
- 	/* Disable *all* interrupts */				\
-      	RADEON_WRITE( RADEON_GEN_INT_CNTL, 0 );			\
-} while (0)
-
-#ifdef __linux__ 
-#define IWH(x) init_waitqueue_head(x) 
-#else 
-#define IWH(x) 
-#endif 
-
-#define DRIVER_POSTINSTALL() do {				\
-	drm_radeon_private_t *dev_priv =			\
-		(drm_radeon_private_t *)dev->dev_private;	\
-								\
-   	atomic_set(&dev_priv->irq_received, 0);			\
-   	atomic_set(&dev_priv->irq_emitted, 0);			\
-	IWH(&dev_priv->irq_queue);		\
-								\
-	/* Turn on SW_INT only */				\
-   	RADEON_WRITE( RADEON_GEN_INT_CNTL,			\
-		      RADEON_SW_INT_ENABLE );			\
-} while (0)
-
-#define DRIVER_UNINSTALL() do {					\
-	drm_radeon_private_t *dev_priv =			\
-		(drm_radeon_private_t *)dev->dev_private;	\
-	if ( dev_priv ) {					\
-		/* Disable *all* interrupts */			\
-		RADEON_WRITE( RADEON_GEN_INT_CNTL, 0 );		\
-	}							\
-} while (0)
 
 /* Buffer customization:
  */
diff -Nru a/drivers/char/drm/radeon_drm.h b/drivers/char/drm/radeon_drm.h
--- a/drivers/char/drm/radeon_drm.h	Mon Oct  7 11:25:36 2002
+++ b/drivers/char/drm/radeon_drm.h	Mon Oct  7 11:25:36 2002
@@ -517,7 +517,7 @@
 #define RADEON_PARAM_LAST_FRAME            2
 #define RADEON_PARAM_LAST_DISPATCH         3
 #define RADEON_PARAM_LAST_CLEAR            4
-#define RADEON_PARAM_IRQ_ACTIVE            5
+#define RADEON_PARAM_IRQ_NR                5
 #define RADEON_PARAM_AGP_BASE              6 /* card offset of agp base */
 
 typedef struct drm_radeon_getparam {
diff -Nru a/drivers/char/drm/radeon_drv.h b/drivers/char/drm/radeon_drv.h
--- a/drivers/char/drm/radeon_drv.h	Mon Oct  7 11:25:35 2002
+++ b/drivers/char/drm/radeon_drv.h	Mon Oct  7 11:25:35 2002
@@ -139,9 +139,9 @@
 	struct mem_block *agp_heap;
 	struct mem_block *fb_heap;
 
-   	wait_queue_head_t irq_queue;
-   	atomic_t irq_received;
-   	atomic_t irq_emitted;
+	/* SW interrupt */
+   	wait_queue_head_t swi_queue;
+   	atomic_t swi_emitted;
 
 } drm_radeon_private_t;
 
@@ -187,11 +187,12 @@
 extern void radeon_mem_takedown( struct mem_block **heap );
 extern void radeon_mem_release( struct mem_block *heap );
 
+				/* radeon_irq.c */
 extern int radeon_irq_emit( DRM_IOCTL_ARGS );
 extern int radeon_irq_wait( DRM_IOCTL_ARGS );
 
 extern int radeon_emit_and_wait_irq(drm_device_t *dev);
-extern int radeon_wait_irq(drm_device_t *dev, int irq_nr);
+extern int radeon_wait_irq(drm_device_t *dev, int swi_nr);
 extern int radeon_emit_irq(drm_device_t *dev);
 
 
@@ -271,11 +272,15 @@
 
 
 #define RADEON_GEN_INT_CNTL		0x0040
+#	define RADEON_CRTC_VBLANK_MASK		(1 << 0)
 #	define RADEON_GUI_IDLE_INT_ENABLE	(1 << 19)
 #	define RADEON_SW_INT_ENABLE		(1 << 25)
 
 #define RADEON_GEN_INT_STATUS		0x0044
+#	define RADEON_CRTC_VBLANK_STAT		(1 << 0)
+#	define RADEON_CRTC_VBLANK_STAT_ACK   	(1 << 0)
 #	define RADEON_GUI_IDLE_INT_TEST_ACK     (1 << 19)
+#	define RADEON_SW_INT_TEST		(1 << 25)
 #	define RADEON_SW_INT_TEST_ACK   	(1 << 25)
 #	define RADEON_SW_INT_FIRE		(1 << 26)
 
@@ -632,6 +637,7 @@
 #define RADEON_LAST_FRAME_REG		RADEON_SCRATCH_REG0
 #define RADEON_LAST_DISPATCH_REG	RADEON_SCRATCH_REG1
 #define RADEON_LAST_CLEAR_REG		RADEON_SCRATCH_REG2
+#define RADEON_LAST_SWI_REG		RADEON_SCRATCH_REG3
 #define RADEON_LAST_DISPATCH		1
 
 #define RADEON_MAX_VB_AGE		0x7fffffff
diff -Nru a/drivers/char/drm/radeon_irq.c b/drivers/char/drm/radeon_irq.c
--- a/drivers/char/drm/radeon_irq.c	Mon Oct  7 11:25:39 2002
+++ b/drivers/char/drm/radeon_irq.c	Mon Oct  7 11:25:39 2002
@@ -1,4 +1,4 @@
-/* radeon_mem.c -- Simple agp/fb memory manager for radeon -*- linux-c -*-
+/* radeon_irq.c -- IRQ handling for radeon -*- linux-c -*-
  *
  * Copyright (C) The Weather Channel, Inc.  2002.  All Rights Reserved.
  * 
@@ -27,6 +27,7 @@
  *
  * Authors:
  *    Keith Whitwell <keith@tungstengraphics.com>
+ *    Michel Dänzer <michel@daenzer.net>
  */
 
 #include "radeon.h"
@@ -58,110 +59,117 @@
 	drm_device_t *dev = (drm_device_t *) arg;
 	drm_radeon_private_t *dev_priv = 
 	   (drm_radeon_private_t *)dev->dev_private;
-   	u32 temp;
+   	u32 stat;
 
-	/* Need to wait for fifo to drain?
+	stat = RADEON_READ(RADEON_GEN_INT_STATUS);
+	if (!stat)
+		return;
+
+	/* SW interrupt */
+	if (stat & RADEON_SW_INT_TEST) {
+		DRM_WAKEUP( &dev_priv->swi_queue );
+	}
+
+#if __HAVE_VBL_IRQ
+	/* VBLANK interrupt */
+	if (stat & RADEON_CRTC_VBLANK_STAT) {
+		atomic_inc(&dev->vbl_received);
+		DRM_WAKEUP(&dev->vbl_queue);
+	}
+#endif
+
+	/* Acknowledge all the bits in GEN_INT_STATUS -- seem to get
+	 * more than we asked for...
 	 */
-	temp = RADEON_READ(RADEON_GEN_INT_STATUS);  
-	temp = temp & RADEON_SW_INT_TEST_ACK;  
-	if (temp == 0) return;  
-	RADEON_WRITE(RADEON_GEN_INT_STATUS, temp);  
-
-	atomic_inc(&dev_priv->irq_received);
-#ifdef __linux__
-	schedule_task(&dev->tq);
-#endif /* __linux__ */
-#ifdef __FreeBSD__
-	taskqueue_enqueue(taskqueue_swi, &dev->task);
-#endif /* __FreeBSD__ */
+	RADEON_WRITE(RADEON_GEN_INT_STATUS, stat);
 }
 
-void DRM(dma_immediate_bh)( DRM_TASKQUEUE_ARGS )
+static __inline__ void radeon_acknowledge_irqs(drm_radeon_private_t *dev_priv)
 {
-	drm_device_t *dev = (drm_device_t *) arg;
-	drm_radeon_private_t *dev_priv = 
-	   (drm_radeon_private_t *)dev->dev_private;
-
-#ifdef __linux__
-	wake_up_interruptible(&dev_priv->irq_queue); 
-#endif /* __linux__ */
-#ifdef __FreeBSD__
-	wakeup( &dev_priv->irq_queue );
-#endif
+	u32 tmp = RADEON_READ( RADEON_GEN_INT_STATUS );
+	if (tmp)
+		RADEON_WRITE( RADEON_GEN_INT_STATUS, tmp );
 }
 
-
 int radeon_emit_irq(drm_device_t *dev)
 {
 	drm_radeon_private_t *dev_priv = dev->dev_private;
+	unsigned int ret;
 	RING_LOCALS;
 
-	atomic_inc(&dev_priv->irq_emitted);
+	atomic_inc(&dev_priv->swi_emitted);
+	ret = atomic_read(&dev_priv->swi_emitted);
 
-	BEGIN_RING(2); 
-	OUT_RING( CP_PACKET0( RADEON_GEN_INT_STATUS, 0 ) );
-	OUT_RING( RADEON_SW_INT_FIRE );
+	BEGIN_RING( 4 );
+	OUT_RING_REG( RADEON_LAST_SWI_REG, ret );
+	OUT_RING_REG( RADEON_GEN_INT_STATUS, RADEON_SW_INT_FIRE );
 	ADVANCE_RING(); 
  	COMMIT_RING();
 
-	return atomic_read(&dev_priv->irq_emitted);
+	return ret;
 }
 
 
-int radeon_wait_irq(drm_device_t *dev, int irq_nr)
+int radeon_wait_irq(drm_device_t *dev, int swi_nr)
 {
   	drm_radeon_private_t *dev_priv = 
 	   (drm_radeon_private_t *)dev->dev_private;
-#ifdef __linux__
-	DECLARE_WAITQUEUE(entry, current);
-	unsigned long end = jiffies + HZ*3;
-#endif /* __linux__ */
 	int ret = 0;
 
- 	if (atomic_read(&dev_priv->irq_received) >= irq_nr)  
+ 	if (RADEON_READ( RADEON_LAST_SWI_REG ) >= swi_nr)  
  		return 0; 
 
 	dev_priv->stats.boxes |= RADEON_BOX_WAIT_IDLE;
 
-#ifdef __linux__
-	add_wait_queue(&dev_priv->irq_queue, &entry);
+	/* This is a hack to work around mysterious freezes on certain
+	 * systems:
+	 */ 
+	radeon_acknowledge_irqs( dev_priv );
 
-	for (;;) {
-		current->state = TASK_INTERRUPTIBLE;
-	   	if (atomic_read(&dev_priv->irq_received) >= irq_nr) 
-		   break;
-		if((signed)(end - jiffies) <= 0) {
-		   	ret = -EBUSY;	/* Lockup?  Missed irq? */
-			break;
-		}
-	      	schedule_timeout(HZ*3);
-	      	if (signal_pending(current)) {
-		   	ret = -EINTR;
-			break;
-		}
-	}
+	DRM_WAIT_ON( ret, dev_priv->swi_queue, 3 * DRM_HZ, 
+		     RADEON_READ( RADEON_LAST_SWI_REG ) >= swi_nr );
 
-	current->state = TASK_RUNNING;
-	remove_wait_queue(&dev_priv->irq_queue, &entry);
-	return ret;
-#endif /* __linux__ */
-	
-#ifdef __FreeBSD__
-	ret = tsleep( &dev_priv->irq_queue, PZERO | PCATCH, \
-		"rdnirq", 3*hz );
-	if ( (ret == EWOULDBLOCK) || (ret == EINTR) )
-		return DRM_ERR(EBUSY);
 	return ret;
-#endif /* __FreeBSD__ */
 }
 
-
 int radeon_emit_and_wait_irq(drm_device_t *dev)
 {
 	return radeon_wait_irq( dev, radeon_emit_irq(dev) );
 }
 
 
+#if __HAVE_VBL_IRQ
+int DRM(vblank_wait)(drm_device_t *dev, unsigned int *sequence)
+{
+  	drm_radeon_private_t *dev_priv = 
+	   (drm_radeon_private_t *)dev->dev_private;
+	unsigned int cur_vblank;
+	int ret = 0;
+
+	if ( !dev_priv ) {
+		DRM_ERROR( "%s called with no initialization\n", __FUNCTION__ );
+		return DRM_ERR(EINVAL);
+	}
+
+	radeon_acknowledge_irqs( dev_priv );
+
+	dev_priv->stats.boxes |= RADEON_BOX_WAIT_IDLE;
+
+	/* Assume that the user has missed the current sequence number
+	 * by about a day rather than she wants to wait for years
+	 * using vertical blanks... 
+	 */
+	DRM_WAIT_ON( ret, dev->vbl_queue, 3*DRM_HZ, 
+		     ( ( ( cur_vblank = atomic_read(&dev->vbl_received ) )
+			 + ~*sequence + 1 ) <= (1<<23) ) );
+
+	*sequence = cur_vblank;
+
+	return ret;
+}
+#endif
+
+
 /* Needs the lock as it touches the ring.
  */
 int radeon_irq_emit( DRM_IOCTL_ARGS )
@@ -211,3 +219,38 @@
 	return radeon_wait_irq( dev, irqwait.irq_seq );
 }
 
+
+/* drm_dma.h hooks
+*/
+void DRM(driver_irq_preinstall)( drm_device_t *dev ) {
+	drm_radeon_private_t *dev_priv =
+		(drm_radeon_private_t *)dev->dev_private;
+
+ 	/* Disable *all* interrupts */
+      	RADEON_WRITE( RADEON_GEN_INT_CNTL, 0 );
+
+	/* Clear bits if they're already high */
+	radeon_acknowledge_irqs( dev_priv );
+}
+
+void DRM(driver_irq_postinstall)( drm_device_t *dev ) {
+	drm_radeon_private_t *dev_priv =
+		(drm_radeon_private_t *)dev->dev_private;
+
+   	atomic_set(&dev_priv->swi_emitted, 0);
+	DRM_INIT_WAITQUEUE( &dev_priv->swi_queue );
+
+	/* Turn on SW and VBL ints */
+   	RADEON_WRITE( RADEON_GEN_INT_CNTL,
+		      RADEON_CRTC_VBLANK_MASK |	
+		      RADEON_SW_INT_ENABLE );
+}
+
+void DRM(driver_irq_uninstall)( drm_device_t *dev ) {
+	drm_radeon_private_t *dev_priv =
+		(drm_radeon_private_t *)dev->dev_private;
+	if ( dev_priv ) {
+		/* Disable *all* interrupts */
+		RADEON_WRITE( RADEON_GEN_INT_CNTL, 0 );
+	}
+}
diff -Nru a/drivers/char/drm/radeon_mem.c b/drivers/char/drm/radeon_mem.c
--- a/drivers/char/drm/radeon_mem.c	Mon Oct  7 11:25:37 2002
+++ b/drivers/char/drm/radeon_mem.c	Mon Oct  7 11:25:37 2002
@@ -118,7 +118,7 @@
 		p->size += q->size;
 		p->next = q->next;
 		p->next->prev = p;
-		DRM_FREE(p);
+		DRM_FREE(q);
 	}
 
 	if (p->prev->pid == 0) {
diff -Nru a/drivers/char/drm/radeon_state.c b/drivers/char/drm/radeon_state.c
--- a/drivers/char/drm/radeon_state.c	Mon Oct  7 11:25:37 2002
+++ b/drivers/char/drm/radeon_state.c	Mon Oct  7 11:25:37 2002
@@ -1283,7 +1283,6 @@
 }
 
 
-
 /* Not sure why this isn't set all the time:
  */ 
 static int radeon_do_init_pageflip( drm_device_t *dev )
@@ -2172,8 +2171,8 @@
 		dev_priv->stats.last_clear_reads++;
 		value = GET_SCRATCH( 2 );
 		break;
-	case RADEON_PARAM_IRQ_ACTIVE:
-		value = dev->irq ? 1 : 0;
+	case RADEON_PARAM_IRQ_NR:
+		value = dev->irq;
 		break;
 	case RADEON_PARAM_AGP_BASE:
 		value = dev_priv->agp_vm_start;
diff -Nru a/drivers/char/epca.c b/drivers/char/epca.c
--- a/drivers/char/epca.c	Mon Oct  7 11:25:35 2002
+++ b/drivers/char/epca.c	Mon Oct  7 11:25:35 2002
@@ -488,7 +488,7 @@
 
 	ch->event |= 1 << event;
 	MOD_INC_USE_COUNT;
-	if (schedule_task(&ch->tqueue) == 0)
+	if (schedule_work(&ch->tqueue) == 0)
 		MOD_DEC_USE_COUNT;
 
 
@@ -2039,8 +2039,7 @@
 
 		ch->brdchan        = bc;
 		ch->mailbox        = gd; 
-		ch->tqueue.routine = do_softint;
-		ch->tqueue.data    = ch;
+		INIT_WORK(&ch->tqueue, do_softint, ch);
 		ch->board          = &boards[crd];
 
 		switch (bd->type)
diff -Nru a/drivers/char/epca.h b/drivers/char/epca.h
--- a/drivers/char/epca.h	Mon Oct  7 11:25:35 2002
+++ b/drivers/char/epca.h	Mon Oct  7 11:25:35 2002
@@ -142,7 +142,7 @@
 	struct termios              callout_termios;
 	wait_queue_head_t           open_wait;
 	wait_queue_head_t           close_wait;
-	struct tq_struct            tqueue;
+	struct work_struct            tqueue;
 	volatile struct global_data *mailbox;
 };
 
diff -Nru a/drivers/char/genrtc.c b/drivers/char/genrtc.c
--- a/drivers/char/genrtc.c	Mon Oct  7 11:25:39 2002
+++ b/drivers/char/genrtc.c	Mon Oct  7 11:25:39 2002
@@ -48,7 +48,7 @@
 #include <linux/init.h>
 #include <linux/poll.h>
 #include <linux/proc_fs.h>
-#include <linux/tqueue.h>
+#include <linux/workqueue.h>
 
 #include <asm/uaccess.h>
 #include <asm/system.h>
@@ -82,7 +82,7 @@
 static int irq_active;
 
 #ifdef CONFIG_GEN_RTC_X
-struct tq_struct genrtc_task;
+struct work_struct genrtc_task;
 static struct timer_list timer_task;
 
 static unsigned int oldsecs;
@@ -91,7 +91,7 @@
 
 void gen_rtc_timer(unsigned long data);
 
-static volatile int stask_active;              /* schedule_task */
+static volatile int stask_active;              /* schedule_work */
 static volatile int ttask_active;              /* timer_task */
 static int stop_rtc_timers;                    /* don't requeue tasks */
 static spinlock_t gen_rtc_lock = SPIN_LOCK_UNLOCKED;
@@ -120,7 +120,7 @@
 		add_timer(&timer_task);
 
 		gen_rtc_interrupt(0);
-	} else if (schedule_task(&genrtc_task) == 0)
+	} else if (schedule_work(&genrtc_task) == 0)
 		stask_active = 0;
 }
 
@@ -134,7 +134,7 @@
 		       jiffies-tt_exp);
 	ttask_active=0;
 	stask_active=1;
-	if ((schedule_task(&genrtc_task) == 0))
+	if ((schedule_work(&genrtc_task) == 0))
 		stask_active = 0;
 }
 
@@ -253,12 +253,12 @@
 		irq_active = 1;
 		stop_rtc_timers = 0;
 		lostint = 0;
-		genrtc_task.routine = genrtc_troutine;
+		INIT_WORK(&genrtc_task, genrtc_troutine, NULL);
 		oldsecs = get_rtc_ss();
 		init_timer(&timer_task);
 
 		stask_active = 1;
-		if (schedule_task(&genrtc_task) == 0){
+		if (schedule_work(&genrtc_task) == 0){
 			stask_active = 0;
 		}
 	}
diff -Nru a/drivers/char/ip2/i2ellis.h b/drivers/char/ip2/i2ellis.h
--- a/drivers/char/ip2/i2ellis.h	Mon Oct  7 11:25:25 2002
+++ b/drivers/char/ip2/i2ellis.h	Mon Oct  7 11:25:25 2002
@@ -401,7 +401,7 @@
 	rwlock_t	write_fifo_spinlock;
 
 //	For queuing interupt bottom half handlers.	/\/\|=mhw=|\/\/
-	struct tq_struct	tqueue_interrupt;
+	struct work_struct	tqueue_interrupt;
 
 	struct timer_list  SendPendingTimer;   // Used by iiSendPending
 	unsigned int	SendPendingRetry;
diff -Nru a/drivers/char/ip2/i2lib.c b/drivers/char/ip2/i2lib.c
--- a/drivers/char/ip2/i2lib.c	Mon Oct  7 11:25:35 2002
+++ b/drivers/char/ip2/i2lib.c	Mon Oct  7 11:25:35 2002
@@ -1582,8 +1582,7 @@
 			WRITE_UNLOCK_IRQRESTORE(&pB->read_fifo_spinlock,bflags);
 
 #ifdef USE_IQ
-			queue_task(&pCh->tqueue_input, &tq_immediate);
-			mark_bh(IMMEDIATE_BH);
+			schedule_work(&pCh->tqueue_input);
 #else
 			do_input(pCh);
 #endif
@@ -1820,8 +1819,7 @@
 					}  /* End of switch on status type */
 					if (dss_change) {
 #ifdef USE_IQ
-						queue_task(&pCh->tqueue_status, &tq_immediate);
-						mark_bh(IMMEDIATE_BH);
+						schedule_work(&pCh->tqueue_status);
 #else
 						do_status(pCh);
 #endif
diff -Nru a/drivers/char/ip2/i2lib.h b/drivers/char/ip2/i2lib.h
--- a/drivers/char/ip2/i2lib.h	Mon Oct  7 11:25:30 2002
+++ b/drivers/char/ip2/i2lib.h	Mon Oct  7 11:25:30 2002
@@ -27,6 +27,7 @@
 #include "i2ellis.h"
 #include "i2pack.h"
 #include "i2cmd.h"
+#include <linux/workqueue.h>
 
 //------------------------------------------------------------------------------
 // i2ChanStr -- Channel Structure:
@@ -224,9 +225,9 @@
 	/*
 	 *	Task queues for processing input packets from the board.
 	 */
-	struct tq_struct	tqueue_input;
-	struct tq_struct	tqueue_status;
-	struct tq_struct	tqueue_hangup;
+	struct work_struct	tqueue_input;
+	struct work_struct	tqueue_status;
+	struct work_struct	tqueue_hangup;
 
 	rwlock_t Ibuf_spinlock;
 	rwlock_t Obuf_spinlock;
diff -Nru a/drivers/char/ip2main.c b/drivers/char/ip2main.c
--- a/drivers/char/ip2main.c	Mon Oct  7 11:25:40 2002
+++ b/drivers/char/ip2main.c	Mon Oct  7 11:25:40 2002
@@ -1097,8 +1097,7 @@
 		pCh++;
 	}
 ex_exit:
-	pB->tqueue_interrupt.routine = (void(*)(void*)) ip2_interrupt_bh;
-	pB->tqueue_interrupt.data = pB;
+	INIT_WORK(&pB->tqueue_interrupt, (void(*)(void*)) ip2_interrupt_bh, pB);
 	return;
 
 err_release_region:
@@ -1376,10 +1375,9 @@
 			iiDisableMailIrq(pB);
 
 //			Park the board on the immediate queue for processing.
-			queue_task(&pB->tqueue_interrupt, &tq_immediate);
+			schedule_work(&pB->tqueue_interrupt);
 
 //			Make sure the immediate queue is flagged to fire.
-			mark_bh(IMMEDIATE_BH);
 		    }
 #else
 //		We are using immediate servicing here.  This sucks and can
diff -Nru a/drivers/char/moxa.c b/drivers/char/moxa.c
--- a/drivers/char/moxa.c	Mon Oct  7 11:25:39 2002
+++ b/drivers/char/moxa.c	Mon Oct  7 11:25:39 2002
@@ -159,7 +159,7 @@
 	struct termios callout_termios;
 	wait_queue_head_t open_wait;
 	wait_queue_head_t close_wait;
-	struct tq_struct tqueue;
+	struct work_struct tqueue;
 };
 
 struct mxser_mstatus {
@@ -384,8 +384,7 @@
 	for (i = 0, ch = moxaChannels; i < MAX_PORTS; i++, ch++) {
 		ch->type = PORT_16550A;
 		ch->port = i;
-		ch->tqueue.routine = do_moxa_softint;
-		ch->tqueue.data = ch;
+		INIT_WORK(&ch->tqueue, do_moxa_softint, ch);
 		ch->tty = 0;
 		ch->close_delay = 5 * HZ / 10;
 		ch->closing_wait = 30 * HZ;
@@ -1026,7 +1025,7 @@
 					else {
 						set_bit(MOXA_EVENT_HANGUP, &ch->event);
 						MOD_DEC_USE_COUNT;
-						if (schedule_task(&ch->tqueue) == 0)
+						if (schedule_work(&ch->tqueue) == 0)
 							MOD_INC_USE_COUNT;
 					}
 				}
diff -Nru a/drivers/char/mxser.c b/drivers/char/mxser.c
--- a/drivers/char/mxser.c	Mon Oct  7 11:25:36 2002
+++ b/drivers/char/mxser.c	Mon Oct  7 11:25:36 2002
@@ -262,7 +262,7 @@
 	int xmit_head;
 	int xmit_tail;
 	int xmit_cnt;
-	struct tq_struct tqueue;
+	struct work_struct tqueue;
 	struct termios normal_termios;
 	struct termios callout_termios;
 	wait_queue_head_t open_wait;
@@ -449,8 +449,7 @@
 		info->custom_divisor = hwconf->baud_base[i] * 16;
 		info->close_delay = 5 * HZ / 10;
 		info->closing_wait = 30 * HZ;
-		info->tqueue.routine = mxser_do_softint;
-		info->tqueue.data = info;
+		INIT_WORK(&info->tqueue, mxser_do_softint, info);
 		info->callout_termios = mxvar_cdriver.init_termios;
 		info->normal_termios = mxvar_sdriver.init_termios;
 		init_waitqueue_head(&info->open_wait);
@@ -1480,7 +1479,7 @@
 		*status = inb(info->base + UART_LSR) & info->read_status_mask;
 	} while (*status & UART_LSR_DR);
 	mxvar_log.rxcnt[info->port] += cnt;
-	queue_task(&tty->flip.tqueue, &tq_timer);
+	schedule_delayed_work(&tty->flip.work, 1);
 
 }
 
@@ -1513,7 +1512,7 @@
 	if (info->xmit_cnt < WAKEUP_CHARS) {
 		set_bit(MXSER_EVENT_TXLOW, &info->event);
 		MOD_INC_USE_COUNT;
-		if (schedule_task(&info->tqueue) == 0)
+		if (schedule_work(&info->tqueue) == 0)
 		    MOD_DEC_USE_COUNT;
 	}
 	if (info->xmit_cnt <= 0) {
@@ -1544,7 +1543,7 @@
 			   (info->flags & ASYNC_CALLOUT_NOHUP)))
 			set_bit(MXSER_EVENT_HANGUP, &info->event);
 		MOD_INC_USE_COUNT;
-		if (schedule_task(&info->tqueue) == 0)
+		if (schedule_work(&info->tqueue) == 0)
 		    MOD_DEC_USE_COUNT;
 	}
 	if (info->flags & ASYNC_CTS_FLOW) {
@@ -1556,7 +1555,7 @@
 
 				set_bit(MXSER_EVENT_TXLOW, &info->event);
 				MOD_INC_USE_COUNT;
-				if (schedule_task(&info->tqueue) == 0)
+				if (schedule_work(&info->tqueue) == 0)
 					MOD_DEC_USE_COUNT;
 			}
 		} else {
diff -Nru a/drivers/char/pcmcia/synclink_cs.c b/drivers/char/pcmcia/synclink_cs.c
--- a/drivers/char/pcmcia/synclink_cs.c	Mon Oct  7 11:25:30 2002
+++ b/drivers/char/pcmcia/synclink_cs.c	Mon Oct  7 11:25:30 2002
@@ -68,7 +68,7 @@
 #include <asm/bitops.h>
 #include <asm/types.h>
 #include <linux/termios.h>
-#include <linux/tqueue.h>
+#include <linux/workqueue.h>
 
 #include <pcmcia/version.h>
 #include <pcmcia/cs_types.h>
@@ -195,7 +195,7 @@
 	unsigned char  pim_value;
 
 	spinlock_t lock;
-	struct tq_struct task;		/* task structure for scheduling bh */
+	struct work_struct task;		/* task structure for scheduling bh */
 
 	u32 max_frame_size;
 
@@ -562,9 +562,7 @@
 
     memset(info, 0, sizeof(MGSLPC_INFO));
     info->magic = MGSLPC_MAGIC;
-    info->task.sync = 0;
-    info->task.routine = bh_handler;
-    info->task.data    = info;
+    INIT_WORK(&info->task, bh_handler, info);
     info->max_frame_size = 4096;
     info->close_delay = 5*HZ/10;
     info->closing_wait = 30*HZ;
@@ -1460,8 +1458,7 @@
 		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);
+		schedule_work(&info->task);
 		info->bh_requested = 1;
 	}
 
diff -Nru a/drivers/char/random.c b/drivers/char/random.c
--- a/drivers/char/random.c	Mon Oct  7 11:25:25 2002
+++ b/drivers/char/random.c	Mon Oct  7 11:25:25 2002
@@ -252,7 +252,7 @@
 #include <linux/poll.h>
 #include <linux/init.h>
 #include <linux/fs.h>
-#include <linux/tqueue.h>
+#include <linux/workqueue.h>
 
 #include <asm/processor.h>
 #include <asm/uaccess.h>
@@ -624,8 +624,8 @@
 static int	*batch_entropy_credit;
 static int	batch_max;
 static int	batch_head, batch_tail;
-static struct tq_struct	batch_tqueue;
 static void batch_entropy_process(void *private_);
+static DECLARE_WORK(batch_work, batch_entropy_process, NULL);
 
 /* note: the size must be a power of 2 */
 static int __init batch_entropy_init(int size, struct entropy_store *r)
@@ -640,8 +640,7 @@
 	}
 	batch_head = batch_tail = 0;
 	batch_max = size;
-	batch_tqueue.routine = batch_entropy_process;
-	batch_tqueue.data = r;
+	batch_work.data = r;
 	return 0;
 }
 
@@ -664,8 +663,10 @@
 
 	new = (batch_head+1) & (batch_max-1);
 	if (new != batch_tail) {
-		// FIXME: is this correct?
-		schedule_task(&batch_tqueue);
+		/*
+		 * Schedule it for the next timer tick:
+		 */
+		schedule_delayed_work(&batch_work, 1);
 		batch_head = new;
 	} else {
 		DEBUG_ENT("batch entropy buffer full\n");
@@ -1749,7 +1750,7 @@
 
 	sysctl_init_random(new_store);
 	old_store = random_state;
-	random_state = batch_tqueue.data = new_store;
+	random_state = batch_work.data = new_store;
 	free_entropy_store(old_store);
 	return 0;
 }
diff -Nru a/drivers/char/raw.c b/drivers/char/raw.c
--- a/drivers/char/raw.c	Mon Oct  7 11:25:36 2002
+++ b/drivers/char/raw.c	Mon Oct  7 11:25:36 2002
@@ -222,7 +222,7 @@
 		count = inode->i_size - *offp;
 		nr_segs = iov_shorten((struct iovec *)iov, nr_segs, count);
 	}
-	ret = generic_file_direct_IO(rw, inode, iov, *offp, nr_segs);
+	ret = generic_file_direct_IO(rw, filp, iov, *offp, nr_segs);
 
 	if (ret > 0)
 		*offp += ret;
diff -Nru a/drivers/char/rocket.c b/drivers/char/rocket.c
--- a/drivers/char/rocket.c	Mon Oct  7 11:25:26 2002
+++ b/drivers/char/rocket.c	Mon Oct  7 11:25:26 2002
@@ -1993,7 +1993,7 @@
 			isa_boards_found++;
 	}
 #ifdef CONFIG_PCI
-	if (pcibios_present()) {
+	if (pci_present()) {
 		if(isa_boards_found < NUM_BOARDS)
 			pci_boards_found = init_PCI(isa_boards_found);
 	} else {
diff -Nru a/drivers/char/rocket_int.h b/drivers/char/rocket_int.h
--- a/drivers/char/rocket_int.h	Mon Oct  7 11:25:26 2002
+++ b/drivers/char/rocket_int.h	Mon Oct  7 11:25:26 2002
@@ -1143,7 +1143,6 @@
 	int			cps;
 	struct termios		normal_termios;
 	struct termios		callout_termios;
-	struct tq_struct	tqueue;
 	wait_queue_head_t	open_wait;
 	wait_queue_head_t	close_wait;
 };
diff -Nru a/drivers/char/scx200_gpio.c b/drivers/char/scx200_gpio.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/char/scx200_gpio.c	Mon Oct  7 11:25:40 2002
@@ -0,0 +1,154 @@
+/* linux/drivers/char/scx200_gpio.c 
+
+   National Semiconductor SCx200 GPIO driver.  Allows a user space
+   process to play with the GPIO pins.
+
+   Copyright (c) 2001,2002 Christer Weinigel <wingel@nano-system.com> */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+
+#include <linux/scx200_gpio.h>
+
+#define NAME "scx200_gpio"
+
+MODULE_AUTHOR("Christer Weinigel <wingel@nano-system.com>");
+MODULE_DESCRIPTION("NatSemi SCx200 GPIO Pin Driver");
+MODULE_LICENSE("GPL");
+
+static int major = 0;		/* default to dynamic major */
+MODULE_PARM(major, "i");
+MODULE_PARM_DESC(major, "Major device number");
+
+static ssize_t scx200_gpio_write(struct file *file, const char *data, 
+				 size_t len, loff_t *ppos)
+{
+	unsigned m = minor(file->f_dentry->d_inode->i_rdev);
+	size_t i;
+
+	if (ppos != &file->f_pos)
+		return -ESPIPE;
+
+	for (i = 0; i < len; ++i) {
+		char c;
+		if (get_user(c, data+i))
+			return -EFAULT;
+		switch (c)
+		{
+		case '0': 
+			scx200_gpio_set(m, 0); 
+			break;
+		case '1': 
+			scx200_gpio_set(m, 1); 
+			break;
+		case 'O':
+			printk(KERN_INFO NAME ": GPIO%d output enabled\n", m);
+			scx200_gpio_configure(m, ~1, 1);
+			break;
+		case 'o':
+			printk(KERN_INFO NAME ": GPIO%d output disabled\n", m);
+			scx200_gpio_configure(m, ~1, 0);
+			break;
+		case 'T':
+			printk(KERN_INFO NAME ": GPIO%d output is push pull\n", m);
+			scx200_gpio_configure(m, ~2, 2);
+			break;
+		case 't':
+			printk(KERN_INFO NAME ": GPIO%d output is open drain\n", m);
+			scx200_gpio_configure(m, ~2, 0);
+			break;
+		case 'P':
+			printk(KERN_INFO NAME ": GPIO%d pull up enabled\n", m);
+			scx200_gpio_configure(m, ~4, 4);
+			break;
+		case 'p':
+			printk(KERN_INFO NAME ": GPIO%d pull up disabled\n", m);
+			scx200_gpio_configure(m, ~4, 0);
+			break;
+		}
+	}
+
+	return len;
+}
+
+static ssize_t scx200_gpio_read(struct file *file, char *buf,
+				size_t len, loff_t *ppos)
+{
+	unsigned m = minor(file->f_dentry->d_inode->i_rdev);
+	int value;
+
+	if (ppos != &file->f_pos)
+		return -ESPIPE;
+
+	value = scx200_gpio_get(m);
+	if (put_user(value ? '1' : '0', buf))
+		return -EFAULT;
+	
+	return 1;
+}
+
+static int scx200_gpio_open(struct inode *inode, struct file *file)
+{
+	unsigned m = minor(inode->i_rdev);
+	if (m > 63)
+		return -EINVAL;
+	return 0;
+}
+
+static int scx200_gpio_release(struct inode *inode, struct file *file)
+{
+	return 0;
+}
+
+
+static struct file_operations scx200_gpio_fops = {
+	.owner   = THIS_MODULE,
+	.write   = scx200_gpio_write,
+	.read    = scx200_gpio_read,
+	.open    = scx200_gpio_open,
+	.release = scx200_gpio_release,
+};
+
+static int __init scx200_gpio_init(void)
+{
+	int r;
+
+	printk(KERN_DEBUG NAME ": NatSemi SCx200 GPIO Driver\n");
+
+	if (!scx200_gpio_present()) {
+		printk(KERN_ERR NAME ": no SCx200 gpio pins available\n");
+		return -ENODEV;
+	}
+
+	r = register_chrdev(major, NAME, &scx200_gpio_fops);
+	if (r < 0) {
+		printk(KERN_ERR NAME ": unable to register character device\n");
+		return r;
+	}
+	if (!major) {
+		major = r;
+		printk(KERN_DEBUG NAME ": got dynamic major %d\n", major);
+	}
+
+	return 0;
+}
+
+static void __exit scx200_gpio_cleanup(void)
+{
+	unregister_chrdev(major, NAME);
+}
+
+module_init(scx200_gpio_init);
+module_exit(scx200_gpio_cleanup);
+
+/*
+    Local variables:
+        compile-command: "make -k -C ../.. SUBDIRS=drivers/char modules"
+        c-basic-offset: 8
+    End:
+*/
diff -Nru a/drivers/char/scx200_wdt.c b/drivers/char/scx200_wdt.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/char/scx200_wdt.c	Mon Oct  7 11:26:06 2002
@@ -0,0 +1,277 @@
+/* linux/drivers/char/scx200_wdt.c 
+
+   National Semiconductor SCx200 Watchdog support
+
+   Copyright (c) 2001,2002 Christer Weinigel <wingel@nano-system.com>
+
+   Som code taken from:
+   National Semiconductor PC87307/PC97307 (ala SC1200) WDT driver
+   (c) Copyright 2002 Zwane Mwaikambo <zwane@commfireservices.com>
+
+   This program is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The author(s) of this software shall not be held liable for damages
+   of any nature resulting due to the use of this software. This
+   software is provided AS-IS with no warranties. */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/miscdevice.h>
+#include <linux/watchdog.h>
+#include <linux/notifier.h>
+#include <linux/reboot.h>
+#include <linux/pci.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+
+#include <linux/scx200.h>
+
+#define NAME "scx200_wdt"
+
+MODULE_AUTHOR("Christer Weinigel <wingel@nano-system.com>");
+MODULE_DESCRIPTION("NatSemi SCx200 Watchdog Driver");
+MODULE_LICENSE("GPL");
+
+#ifndef CONFIG_WATCHDOG_NOWAYOUT
+#define CONFIG_WATCHDOG_NOWAYOUT 0
+#endif
+
+static int margin = 60;		/* in seconds */
+MODULE_PARM(margin, "i");
+MODULE_PARM_DESC(margin, "Watchdog margin in seconds");
+
+static int nowayout = CONFIG_WATCHDOG_NOWAYOUT;
+MODULE_PARM(nowayout, "i");
+MODULE_PARM_DESC(nowayout, "Disable watchdog shutdown on close");
+
+static u16 wdto_restart;
+static struct semaphore open_semaphore;
+static unsigned expect_close;
+
+/* Bits of the WDCNFG register */
+#define W_ENABLE 0x00fa		/* Enable watchdog */
+#define W_DISABLE 0x0000	/* Disable watchdog */
+
+/* The scaling factor for the timer, this depends on the value of W_ENABLE */
+#define W_SCALE (32768/1024)
+
+static void scx200_wdt_ping(void)
+{
+	outw(wdto_restart, SCx200_CB_BASE + SCx200_WDT_WDTO);
+}
+
+static void scx200_wdt_update_margin(void)
+{
+	printk(KERN_INFO NAME ": timer margin %d seconds\n", margin);
+	wdto_restart = margin * W_SCALE;
+}
+
+static void scx200_wdt_enable(void)
+{
+	printk(KERN_DEBUG NAME ": enabling watchdog timer, wdto_restart = %d\n", 
+	       wdto_restart);
+
+	outw(0, SCx200_CB_BASE + SCx200_WDT_WDTO);
+	outb(SCx200_WDT_WDSTS_WDOVF, SCx200_CB_BASE + SCx200_WDT_WDSTS);
+	outw(W_ENABLE, SCx200_CB_BASE + SCx200_WDT_WDCNFG);
+
+	scx200_wdt_ping();
+}
+
+static void scx200_wdt_disable(void)
+{
+	printk(KERN_DEBUG NAME ": disabling watchdog timer\n");
+		
+	outw(0, SCx200_CB_BASE + SCx200_WDT_WDTO);
+	outb(SCx200_WDT_WDSTS_WDOVF, SCx200_CB_BASE + SCx200_WDT_WDSTS);
+	outw(W_DISABLE, SCx200_CB_BASE + SCx200_WDT_WDCNFG);
+}
+
+static int scx200_wdt_open(struct inode *inode, struct file *file)
+{
+        /* only allow one at a time */
+        if (down_trylock(&open_semaphore))
+                return -EBUSY;
+	scx200_wdt_enable();
+	expect_close = 0;
+
+	return 0;
+}
+
+static int scx200_wdt_release(struct inode *inode, struct file *file)
+{
+	if (!expect_close) {
+		printk(KERN_WARNING NAME ": watchdog device closed unexpectedly, will not disable the watchdog timer\n");
+	} else if (!nowayout) {
+		scx200_wdt_disable();
+	}
+        up(&open_semaphore);
+
+	return 0;
+}
+
+static int scx200_wdt_notify_sys(struct notifier_block *this, 
+				      unsigned long code, void *unused)
+{
+        if (code == SYS_HALT || code == SYS_POWER_OFF)
+		if (!nowayout)
+			scx200_wdt_disable();
+
+        return NOTIFY_DONE;
+}
+
+static struct notifier_block scx200_wdt_notifier =
+{
+        notifier_call: scx200_wdt_notify_sys
+};
+
+static ssize_t scx200_wdt_write(struct file *file, const char *data, 
+				     size_t len, loff_t *ppos)
+{
+	if (ppos != &file->f_pos)
+		return -ESPIPE;
+
+	/* check for a magic close character */
+	if (len) 
+	{
+		size_t i;
+
+		scx200_wdt_ping();
+
+		expect_close = 0;
+		for (i = 0; i < len; ++i) {
+			char c;
+			if (get_user(c, data+i))
+				return -EFAULT;
+			if (c == 'V')
+				expect_close = 1;
+		}
+
+		return len;
+	}
+
+	return 0;
+}
+
+static int scx200_wdt_ioctl(struct inode *inode, struct file *file,
+	unsigned int cmd, unsigned long arg)
+{
+	static struct watchdog_info ident = {
+		.identity = "NatSemi SCx200 Watchdog",
+		.firmware_version = 1, 
+		.options = (WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING),
+	};
+	int new_margin;
+	
+	switch (cmd) {
+	default:
+		return -ENOTTY;
+	case WDIOC_GETSUPPORT:
+		if(copy_to_user((struct watchdog_info *)arg, &ident, 
+				sizeof(ident)))
+			return -EFAULT;
+		return 0;
+	case WDIOC_GETSTATUS:
+	case WDIOC_GETBOOTSTATUS:
+		if (put_user(0, (int *)arg))
+			return -EFAULT;
+		return 0;
+	case WDIOC_KEEPALIVE:
+		scx200_wdt_ping();
+		return 0;
+	case WDIOC_SETTIMEOUT:
+		if (get_user(new_margin, (int *)arg))
+			return -EFAULT;
+		if (new_margin < 1)
+			return -EINVAL;
+		margin = new_margin;
+		scx200_wdt_update_margin();
+		scx200_wdt_ping();
+	case WDIOC_GETTIMEOUT:
+		if (put_user(margin, (int *)arg))
+			return -EFAULT;
+		return 0;
+	}
+}
+
+static struct file_operations scx200_wdt_fops = {
+	.owner	 = THIS_MODULE,
+	.write   = scx200_wdt_write,
+	.ioctl   = scx200_wdt_ioctl,
+	.open    = scx200_wdt_open,
+	.release = scx200_wdt_release,
+};
+
+static struct miscdevice scx200_wdt_miscdev = {
+	.minor = WATCHDOG_MINOR,
+	.name  = NAME,
+	.fops  = &scx200_wdt_fops,
+};
+
+static int __init scx200_wdt_init(void)
+{
+	int r;
+
+	printk(KERN_DEBUG NAME ": NatSemi SCx200 Watchdog Driver\n");
+
+	/* First check that this really is a NatSemi SCx200 CPU */
+	if ((pci_find_device(PCI_VENDOR_ID_NS, 
+			     PCI_DEVICE_ID_NS_SCx200_BRIDGE,
+			     NULL)) == NULL)
+		return -ENODEV;
+
+	/* More sanity checks, verify that the configuration block is there */
+	if (!scx200_cb_probe(SCx200_CB_BASE)) {
+		printk(KERN_WARNING NAME ": no configuration block found\n");
+		return -ENODEV;
+	}
+
+	if (!request_region(SCx200_CB_BASE + SCx200_WDT_OFFSET, 
+			    SCx200_WDT_SIZE, 
+			    "NatSemi SCx200 Watchdog")) {
+		printk(KERN_WARNING NAME ": watchdog I/O region busy\n");
+		return -EBUSY;
+	}
+
+	scx200_wdt_update_margin();
+	scx200_wdt_disable();
+
+	sema_init(&open_semaphore, 1);
+
+	r = misc_register(&scx200_wdt_miscdev);
+	if (r)
+		return r;
+
+	r = register_reboot_notifier(&scx200_wdt_notifier);
+        if (r) {
+                printk(KERN_ERR NAME ": unable to register reboot notifier");
+		misc_deregister(&scx200_wdt_miscdev);
+                return r;
+        }
+
+	return 0;
+}
+
+static void __exit scx200_wdt_cleanup(void)
+{
+        unregister_reboot_notifier(&scx200_wdt_notifier);
+	misc_deregister(&scx200_wdt_miscdev);
+	release_region(SCx200_CB_BASE + SCx200_WDT_OFFSET,
+		       SCx200_WDT_SIZE);
+}
+
+module_init(scx200_wdt_init);
+module_exit(scx200_wdt_cleanup);
+
+/*
+    Local variables:
+        compile-command: "make -k -C ../.. SUBDIRS=drivers/char modules"
+        c-basic-offset: 8
+    End:
+*/
diff -Nru a/drivers/char/sh-sci.c b/drivers/char/sh-sci.c
--- a/drivers/char/sh-sci.c	Mon Oct  7 11:25:33 2002
+++ b/drivers/char/sh-sci.c	Mon Oct  7 11:25:33 2002
@@ -431,8 +431,7 @@
 static inline void sci_sched_event(struct sci_port *port, int event)
 {
 	port->event |= 1 << event;
-	queue_task(&port->tqueue, &tq_immediate);
-	mark_bh(IMMEDIATE_BH);
+	schedule_work(&port->tqueue);
 }
 
 static void sci_transmit_chars(struct sci_port *port)
@@ -825,8 +824,7 @@
 	port->gs.count++;
 
 	port->event = 0;
-	port->tqueue.routine = do_softint;
-	port->tqueue.data = port;
+	INIT_WORK(&port->tqueue, do_softint, port);
 
 	/*
 	 * Start up serial port
diff -Nru a/drivers/char/sh-sci.h b/drivers/char/sh-sci.h
--- a/drivers/char/sh-sci.h	Mon Oct  7 11:25:36 2002
+++ b/drivers/char/sh-sci.h	Mon Oct  7 11:25:36 2002
@@ -200,7 +200,7 @@
 	void (*init_pins)(struct sci_port* port, unsigned int cflag);
 	unsigned int old_cflag;
 	struct async_icount icount;
-	struct tq_struct tqueue;
+	struct work_struct tqueue;
 	unsigned long event;
 };
 
diff -Nru a/drivers/char/sx.c b/drivers/char/sx.c
--- a/drivers/char/sx.c	Mon Oct  7 11:25:32 2002
+++ b/drivers/char/sx.c	Mon Oct  7 11:25:32 2002
@@ -220,7 +220,6 @@
 #include <linux/fcntl.h>
 #include <linux/major.h>
 #include <linux/delay.h>
-#include <linux/tqueue.h>
 #include <linux/version.h>
 #include <linux/pci.h>
 #include <linux/slab.h>
diff -Nru a/drivers/char/sx.h b/drivers/char/sx.h
--- a/drivers/char/sx.h	Mon Oct  7 11:25:26 2002
+++ b/drivers/char/sx.h	Mon Oct  7 11:25:26 2002
@@ -22,10 +22,6 @@
 
 struct sx_port {
   struct gs_port          gs;
-  /*
-  struct tq_struct        tqueue;
-  struct tq_struct        tqueue_hangup;
-  */
   struct wait_queue       *shutdown_wait;
   int                     ch_base;
   int                     c_dcd;
diff -Nru a/drivers/char/synclink.c b/drivers/char/synclink.c
--- a/drivers/char/synclink.c	Mon Oct  7 11:25:31 2002
+++ b/drivers/char/synclink.c	Mon Oct  7 11:25:31 2002
@@ -100,7 +100,7 @@
 #include <asm/bitops.h>
 #include <asm/types.h>
 #include <linux/termios.h>
-#include <linux/tqueue.h>
+#include <linux/workqueue.h>
 
 #ifdef CONFIG_SYNCLINK_SYNCPPP_MODULE
 #define CONFIG_SYNCLINK_SYNCPPP 1
@@ -223,7 +223,7 @@
 	struct mgsl_struct	*next_device;	/* device list link */
 	
 	spinlock_t irq_spinlock;		/* spinlock for synchronizing with ISR */
-	struct tq_struct task;		/* task structure for scheduling bh */
+	struct work_struct task;		/* task structure for scheduling bh */
 
 	u32 EventMask;			/* event trigger mask */
 	u32 RecordedEvents;		/* pending events */
@@ -1757,8 +1757,7 @@
 		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);
+		schedule_work(&info->task);
 		info->bh_requested = 1;
 	}
 
@@ -4543,9 +4542,7 @@
 	} else {
 		memset(info, 0, sizeof(struct mgsl_struct));
 		info->magic = MGSL_MAGIC;
-		info->task.sync = 0;
-		info->task.routine = mgsl_bh_handler;
-		info->task.data    = info;
+		INIT_WORK(&info->task, mgsl_bh_handler, info);
 		info->max_frame_size = 4096;
 		info->close_delay = 5*HZ/10;
 		info->closing_wait = 30*HZ;
diff -Nru a/drivers/char/synclinkmp.c b/drivers/char/synclinkmp.c
--- a/drivers/char/synclinkmp.c	Mon Oct  7 11:25:26 2002
+++ b/drivers/char/synclinkmp.c	Mon Oct  7 11:25:26 2002
@@ -67,7 +67,7 @@
 #include <asm/bitops.h>
 #include <asm/types.h>
 #include <linux/termios.h>
-#include <linux/tqueue.h>
+#include <linux/workqueue.h>
 
 #ifdef CONFIG_SYNCLINK_SYNCPPP_MODULE
 #define CONFIG_SYNCLINK_SYNCPPP 1
@@ -194,7 +194,7 @@
 	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 */
+	struct work_struct task;	 		/* task structure for scheduling bh */
 
 	u32 max_frame_size;			/* as set by device config */
 
@@ -2572,8 +2572,7 @@
 			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);
+			schedule_work(&port->task);
 			port->bh_requested = 1;
 		}
 	}
@@ -3736,9 +3735,7 @@
 	} else {
 		memset(info, 0, sizeof(SLMP_INFO));
 		info->magic = MGSL_MAGIC;
-		info->task.sync = 0;
-		info->task.routine = bh_handler;
-		info->task.data    = info;
+		INIT_WORK(&info->task, bh_handler, info);
 		info->max_frame_size = 4096;
 		info->close_delay = 5*HZ/10;
 		info->closing_wait = 30*HZ;
diff -Nru a/drivers/char/tty_io.c b/drivers/char/tty_io.c
--- a/drivers/char/tty_io.c	Mon Oct  7 11:25:35 2002
+++ b/drivers/char/tty_io.c	Mon Oct  7 11:25:35 2002
@@ -538,7 +538,7 @@
 	
 	printk(KERN_DEBUG "%s hangup...\n", tty_name(tty, buf));
 #endif
-	schedule_task(&tty->tq_hangup);
+	schedule_work(&tty->hangup_work);
 }
 
 void tty_vhangup(struct tty_struct * tty)
@@ -1265,7 +1265,7 @@
 	/*
 	 * Make sure that the tty's task queue isn't activated. 
 	 */
-	flush_scheduled_tasks();
+	flush_scheduled_work();
 
 	/* 
 	 * The release_mem function takes care of the details of clearing
@@ -1874,8 +1874,8 @@
 }
 
 /*
- * The tq handling here is a little racy - tty->SAK_tq may already be queued.
- * Fortunately we don't need to worry, because if ->SAK_tq is already queued,
+ * The tq handling here is a little racy - tty->SAK_work may already be queued.
+ * Fortunately we don't need to worry, because if ->SAK_work is already queued,
  * the values which we write to it will be identical to the values which it
  * already has. --akpm
  */
@@ -1883,8 +1883,8 @@
 {
 	if (!tty)
 		return;
-	PREPARE_TQUEUE(&tty->SAK_tq, __do_SAK, tty);
-	schedule_task(&tty->SAK_tq);
+	PREPARE_WORK(&tty->SAK_work, __do_SAK, tty);
+	schedule_work(&tty->SAK_work);
 }
 
 /*
@@ -1900,7 +1900,10 @@
 	unsigned long flags;
 
 	if (test_bit(TTY_DONT_FLIP, &tty->flags)) {
-		schedule_task(&tty->flip.tqueue);
+		/*
+		 * Do it after the next timer tick:
+		 */
+		schedule_delayed_work(&tty->flip.work, 1);
 		return;
 	}
 	if (tty->flip.buf_num) {
@@ -1977,7 +1980,7 @@
 	if (tty->low_latency)
 		flush_to_ldisc((void *) tty);
 	else
-		schedule_task(&tty->flip.tqueue);
+		schedule_delayed_work(&tty->flip.work, 1);
 }
 
 /*
@@ -1991,18 +1994,16 @@
 	tty->pgrp = -1;
 	tty->flip.char_buf_ptr = tty->flip.char_buf;
 	tty->flip.flag_buf_ptr = tty->flip.flag_buf;
-	tty->flip.tqueue.routine = flush_to_ldisc;
-	tty->flip.tqueue.data = tty;
+	INIT_WORK(&tty->flip.work, flush_to_ldisc, tty);
 	init_MUTEX(&tty->flip.pty_sem);
 	init_waitqueue_head(&tty->write_wait);
 	init_waitqueue_head(&tty->read_wait);
-	tty->tq_hangup.routine = do_tty_hangup;
-	tty->tq_hangup.data = tty;
+	INIT_WORK(&tty->hangup_work, do_tty_hangup, tty);
 	sema_init(&tty->atomic_read, 1);
 	sema_init(&tty->atomic_write, 1);
 	spin_lock_init(&tty->read_lock);
 	INIT_LIST_HEAD(&tty->tty_files);
-	INIT_TQUEUE(&tty->SAK_tq, 0, 0);
+	INIT_WORK(&tty->SAK_work, NULL, NULL);
 }
 
 /*
diff -Nru a/drivers/char/vt.c b/drivers/char/vt.c
--- a/drivers/char/vt.c	Mon Oct  7 11:25:37 2002
+++ b/drivers/char/vt.c	Mon Oct  7 11:25:37 2002
@@ -97,7 +97,7 @@
 #include <linux/interrupt.h>
 #include <linux/config.h>
 #include <linux/version.h>
-#include <linux/tqueue.h>
+#include <linux/workqueue.h>
 #include <linux/bootmem.h>
 #include <linux/pm.h>
 
@@ -161,9 +161,7 @@
 static int blankinterval = 10*60*HZ;
 static int vesa_off_interval;
 
-static struct tq_struct console_callback_tq = {
-	routine: console_callback,
-};
+static DECLARE_WORK(console_work, console_callback, NULL);
 
 /*
  * fg_console is the current virtual console,
@@ -241,7 +239,7 @@
 
 void schedule_console_callback(void)
 {
-	schedule_task(&console_callback_tq);
+	schedule_work(&console_work);
 }
 
 static void scrup(int currcons, unsigned int t, unsigned int b, int nr)
diff -Nru a/drivers/hotplug/cpqphp.h b/drivers/hotplug/cpqphp.h
--- a/drivers/hotplug/cpqphp.h	Mon Oct  7 11:25:28 2002
+++ b/drivers/hotplug/cpqphp.h	Mon Oct  7 11:25:28 2002
@@ -317,7 +317,7 @@
 	u16 vendor_id;
 	char proc_name[20];
 	char proc_name2[20];
-	struct tq_struct int_task_event;
+	struct work_struct int_task_event;
 	wait_queue_head_t queue;	/* sleep & wake process */
 };
 
diff -Nru a/drivers/hotplug/cpqphp_core.c b/drivers/hotplug/cpqphp_core.c
--- a/drivers/hotplug/cpqphp_core.c	Mon Oct  7 11:25:25 2002
+++ b/drivers/hotplug/cpqphp_core.c	Mon Oct  7 11:25:25 2002
@@ -33,7 +33,7 @@
 #include <linux/proc_fs.h>
 #include <linux/miscdevice.h>
 #include <linux/slab.h>
-#include <linux/tqueue.h>
+#include <linux/workqueue.h>
 #include <linux/pci.h>
 #include <linux/init.h>
 #include <asm/uaccess.h>
diff -Nru a/drivers/hotplug/cpqphp_ctrl.c b/drivers/hotplug/cpqphp_ctrl.c
--- a/drivers/hotplug/cpqphp_ctrl.c	Mon Oct  7 11:25:30 2002
+++ b/drivers/hotplug/cpqphp_ctrl.c	Mon Oct  7 11:25:30 2002
@@ -31,7 +31,7 @@
 #include <linux/kernel.h>
 #include <linux/types.h>
 #include <linux/slab.h>
-#include <linux/tqueue.h>
+#include <linux/workqueue.h>
 #include <linux/interrupt.h>
 #include <linux/delay.h>
 #include <linux/wait.h>
@@ -971,7 +971,6 @@
 	if (schedule_flag) {
 		up(&event_semaphore);
 		dbg("Signal event_semaphore\n");
-		mark_bh(IMMEDIATE_BH);
 	}
 
 }
diff -Nru a/drivers/hotplug/cpqphp_nvram.c b/drivers/hotplug/cpqphp_nvram.c
--- a/drivers/hotplug/cpqphp_nvram.c	Mon Oct  7 11:25:38 2002
+++ b/drivers/hotplug/cpqphp_nvram.c	Mon Oct  7 11:25:38 2002
@@ -33,7 +33,7 @@
 #include <linux/proc_fs.h>
 #include <linux/miscdevice.h>
 #include <linux/slab.h>
-#include <linux/tqueue.h>
+#include <linux/workqueue.h>
 #include <linux/pci.h>
 #include <linux/init.h>
 #include <asm/uaccess.h>
diff -Nru a/drivers/hotplug/cpqphp_pci.c b/drivers/hotplug/cpqphp_pci.c
--- a/drivers/hotplug/cpqphp_pci.c	Mon Oct  7 11:25:39 2002
+++ b/drivers/hotplug/cpqphp_pci.c	Mon Oct  7 11:25:39 2002
@@ -31,7 +31,7 @@
 #include <linux/kernel.h>
 #include <linux/types.h>
 #include <linux/slab.h>
-#include <linux/tqueue.h>
+#include <linux/workqueue.h>
 #include <linux/proc_fs.h>
 #include <linux/pci.h>
 #include "cpqphp.h"
diff -Nru a/drivers/hotplug/cpqphp_proc.c b/drivers/hotplug/cpqphp_proc.c
--- a/drivers/hotplug/cpqphp_proc.c	Mon Oct  7 11:25:39 2002
+++ b/drivers/hotplug/cpqphp_proc.c	Mon Oct  7 11:25:39 2002
@@ -31,7 +31,7 @@
 #include <linux/kernel.h>
 #include <linux/types.h>
 #include <linux/proc_fs.h>
-#include <linux/tqueue.h>
+#include <linux/workqueue.h>
 #include <linux/pci.h>
 #include "cpqphp.h"
 
diff -Nru a/drivers/i2c/Config.help b/drivers/i2c/Config.help
--- a/drivers/i2c/Config.help	Mon Oct  7 11:25:26 2002
+++ b/drivers/i2c/Config.help	Mon Oct  7 11:25:26 2002
@@ -91,6 +91,28 @@
   <file:Documentation/modules.txt>.
   The module will be called i2c-dev.o.
 
+CONFIG_SCx200_I2C
+  Enable the use of two GPIO pins of a SCx200 processor as an I2C bus.
+
+  If you don't know what to do here, say N.
+
+  If compiled as a module, it will be called scx200_i2c.o.
+
+CONFIG_SCx200_I2C_SCL
+  Enter the GPIO pin number used for the SCL signal.  This value can
+  also be specified with a module parameter.
+
+CONFIG_SCx200_I2C_SDA
+  Enter the GPIO pin number used for the SSA signal.  This value can
+  also be specified with a module parameter.
+
+CONFIG_SCx200_ACB
+  Enable the use of the ACCESS.bus controllers of a SCx200 processor.
+
+  If you don't know what to do here, say N.
+
+  If compiled as a module, it will be called scx200_acb.o.
+
 CONFIG_I2C_PROC
   This provides support for i2c device entries in the /proc filesystem.
   The entries will be found in /proc/sys/dev/sensors.
diff -Nru a/drivers/i2c/Config.in b/drivers/i2c/Config.in
--- a/drivers/i2c/Config.in	Mon Oct  7 11:25:39 2002
+++ b/drivers/i2c/Config.in	Mon Oct  7 11:25:39 2002
@@ -13,6 +13,12 @@
       dep_tristate '  Philips style parallel port adapter' CONFIG_I2C_PHILIPSPAR $CONFIG_I2C_ALGOBIT $CONFIG_PARPORT
       dep_tristate '  ELV adapter' CONFIG_I2C_ELV $CONFIG_I2C_ALGOBIT
       dep_tristate '  Velleman K9000 adapter' CONFIG_I2C_VELLEMAN $CONFIG_I2C_ALGOBIT
+      dep_tristate '  NatSemi SCx200 I2C using GPIO pins' CONFIG_SCx200_I2C $CONFIG_SCx200 $CONFIG_I2C_ALGOBIT
+      if [ "$CONFIG_SCx200_I2C" != "n" ]; then
+         int  '    GPIO pin used for SCL' CONFIG_SCx200_I2C_SCL 12
+         int  '    GPIO pin used for SDA' CONFIG_SCx200_I2C_SDA 13
+      fi
+      dep_tristate '  NatSemi SCx200 ACCESS.bus' CONFIG_SCx200_ACB $CONFIG_I2C
    fi
 
    dep_tristate 'I2C PCF 8584 interfaces' CONFIG_I2C_ALGOPCF $CONFIG_I2C
diff -Nru a/drivers/i2c/Makefile b/drivers/i2c/Makefile
--- a/drivers/i2c/Makefile	Mon Oct  7 11:25:25 2002
+++ b/drivers/i2c/Makefile	Mon Oct  7 11:25:25 2002
@@ -15,6 +15,8 @@
 obj-$(CONFIG_I2C_ELEKTOR)	+= i2c-elektor.o
 obj-$(CONFIG_ITE_I2C_ALGO)	+= i2c-algo-ite.o
 obj-$(CONFIG_ITE_I2C_ADAP)	+= i2c-adap-ite.o
+obj-$(CONFIG_SCx200_I2C)	+= scx200_i2c.o
+obj-$(CONFIG_SCx200_ACB)	+= scx200_acb.o
 obj-$(CONFIG_I2C_PROC)		+= i2c-proc.o
 
 # This is needed for automatic patch generation: sensors code starts here
diff -Nru a/drivers/i2c/scx200_acb.c b/drivers/i2c/scx200_acb.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/i2c/scx200_acb.c	Mon Oct  7 11:26:06 2002
@@ -0,0 +1,578 @@
+/*  linux/drivers/i2c/scx200_acb.c 
+
+    Copyright (c) 2001,2002 Christer Weinigel <wingel@nano-system.com>
+
+    National Semiconductor SCx200 ACCESS.bus support
+    
+    Based on i2c-keywest.c which is:
+        Copyright (c) 2001 Benjamin Herrenschmidt <benh@kernel.crashing.org>
+        Copyright (c) 2000 Philip Edelbrock <phil@stimpy.netroedge.com>
+    
+    This program is free software; you can redistribute it and/or
+    modify it under the terms of the GNU General Public License as
+    published by the Free Software Foundation; either version 2 of the
+    License, or (at your option) any later version.
+   
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    General Public License for more details.
+   
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/smp_lock.h>
+#include <linux/pci.h>
+#include <asm/io.h>
+
+#include <linux/scx200.h>
+
+#define NAME "scx200_acb"
+
+MODULE_AUTHOR("Christer Weinigel <wingel@nano-system.com>");
+MODULE_DESCRIPTION("NatSemi SCx200 ACCESS.bus Driver");
+MODULE_LICENSE("GPL");
+
+#define MAX_DEVICES 4
+static int base[MAX_DEVICES] = { 0x840 };
+MODULE_PARM(base, "1-4i");
+MODULE_PARM_DESC(base, "Base addresses for the ACCESS.bus controllers");
+
+#define DEBUG 0
+
+#if DEBUG
+#define DBG(x...) printk(KERN_DEBUG NAME ": " x)
+#else
+#define DBG(x...)
+#endif
+
+/* The hardware supports interrupt driven mode too, but I haven't
+   implemented that. */
+#define POLLED_MODE 1
+#define POLL_TIMEOUT (HZ)
+
+enum scx200_acb_state {
+	state_idle,
+	state_address,
+	state_command,
+	state_repeat_start,
+	state_quick,
+	state_read,
+	state_write,
+};
+
+static const char *scx200_acb_state_name[] = {
+	"idle",
+	"address",
+	"command",
+	"repeat_start",
+	"quick",
+	"read",
+	"write",
+};
+
+/* Physical interface */
+struct scx200_acb_iface
+{
+	struct scx200_acb_iface *next;
+	struct i2c_adapter adapter;
+	unsigned base;
+	struct semaphore sem;
+
+	/* State machine data */
+	enum scx200_acb_state state;
+	int result;
+	u8 address_byte;
+	u8 command;
+	u8 *ptr;
+	char needs_reset;
+	unsigned len;
+};
+
+/* Register Definitions */
+#define ACBSDA		(iface->base + 0)
+#define ACBST		(iface->base + 1)
+#define    ACBST_SDAST		0x40 /* SDA Status */
+#define    ACBST_BER		0x20 
+#define    ACBST_NEGACK		0x10 /* Negative Acknowledge */
+#define    ACBST_STASTR		0x08 /* Stall After Start */
+#define    ACBST_MASTER		0x02
+#define ACBCST		(iface->base + 2)
+#define    ACBCST_BB		0x02
+#define ACBCTL1		(iface->base + 3)
+#define    ACBCTL1_STASTRE	0x80
+#define    ACBCTL1_NMINTE	0x40
+#define	   ACBCTL1_ACK		0x10
+#define	   ACBCTL1_STOP		0x02
+#define	   ACBCTL1_START	0x01
+#define ACBADDR		(iface->base + 4)
+#define ACBCTL2		(iface->base + 5)
+#define    ACBCTL2_ENABLE	0x01
+
+/************************************************************************/
+
+static void scx200_acb_machine(struct scx200_acb_iface *iface, u8 status)
+{
+	const char *errmsg;
+
+	DBG("state %s, status = 0x%02x\n", 
+	    scx200_acb_state_name[iface->state], status);
+
+	if (status & ACBST_BER) {
+		errmsg = "bus error";
+		goto error;
+	}
+	if (!(status & ACBST_MASTER)) {
+		errmsg = "not master";
+		goto error;
+	}
+	if (status & ACBST_NEGACK)
+		goto negack;
+
+	switch (iface->state) {
+	case state_idle:
+		printk(KERN_WARNING NAME ": %s, interrupt in idle state\n", 
+		       iface->adapter.name);
+		break;
+
+	case state_address:
+		/* Do a pointer write first */
+		outb(iface->address_byte & ~1, ACBSDA);
+
+		iface->state = state_command;
+		break;
+
+	case state_command:
+		outb(iface->command, ACBSDA);
+
+		if (iface->address_byte & 1)
+			iface->state = state_repeat_start;
+		else
+			iface->state = state_write;
+		break;
+
+	case state_repeat_start:
+		outb(inb(ACBCTL1) | ACBCTL1_START, ACBCTL1);
+		/* fallthrough */
+		
+	case state_quick:
+		if (iface->address_byte & 1) {
+			if (iface->len == 1) 
+				outb(inb(ACBCTL1) | ACBCTL1_ACK, ACBCTL1);
+			else
+				outb(inb(ACBCTL1) & ~ACBCTL1_ACK, ACBCTL1);
+			outb(iface->address_byte, ACBSDA);
+
+			iface->state = state_read;
+		} else {
+			outb(iface->address_byte, ACBSDA);
+
+			iface->state = state_write;
+		}
+		break;
+
+	case state_read:
+		/* Set ACK if receiving the last byte */
+		if (iface->len == 1)
+			outb(inb(ACBCTL1) | ACBCTL1_ACK, ACBCTL1);
+		else
+			outb(inb(ACBCTL1) & ~ACBCTL1_ACK, ACBCTL1);
+
+		*iface->ptr++ = inb(ACBSDA);
+		--iface->len;
+
+		if (iface->len == 0) {
+			iface->result = 0;
+			iface->state = state_idle;
+			outb(inb(ACBCTL1) | ACBCTL1_STOP, ACBCTL1);
+		}
+
+		break;
+
+	case state_write:
+		if (iface->len == 0) {
+			iface->result = 0;
+			iface->state = state_idle;
+			outb(inb(ACBCTL1) | ACBCTL1_STOP, ACBCTL1);
+			break;
+		}
+		
+		outb(*iface->ptr++, ACBSDA);
+		--iface->len;
+		
+		break;
+	}
+
+	return;
+
+ negack:
+	DBG("negative acknowledge in state %s\n", 
+	    scx200_acb_state_name[iface->state]);
+
+	iface->state = state_idle;
+	iface->result = -ENXIO;
+
+	outb(inb(ACBCTL1) | ACBCTL1_STOP, ACBCTL1);
+	outb(ACBST_STASTR | ACBST_NEGACK, ACBST);
+	return;
+
+ error:
+	printk(KERN_ERR NAME ": %s, %s in state %s\n", iface->adapter.name, 
+	       errmsg, scx200_acb_state_name[iface->state]);
+
+	iface->state = state_idle;
+	iface->result = -EIO;
+	iface->needs_reset = 1;
+}
+
+static void scx200_acb_timeout(struct scx200_acb_iface *iface) 
+{
+	printk(KERN_ERR NAME ": %s, timeout in state %s\n", 
+	       iface->adapter.name, scx200_acb_state_name[iface->state]);
+
+	iface->state = state_idle;
+	iface->result = -EIO;
+	iface->needs_reset = 1;
+}
+
+#ifdef POLLED_MODE
+static void scx200_acb_poll(struct scx200_acb_iface *iface)
+{
+	u8 status = 0;
+	unsigned long timeout;
+
+	timeout = jiffies + POLL_TIMEOUT;
+	while (time_before(jiffies, timeout)) {
+		status = inb(ACBST);
+		if ((status & (ACBST_SDAST|ACBST_BER|ACBST_NEGACK)) != 0) {
+			scx200_acb_machine(iface, status);
+			return;
+		}
+		schedule_timeout(HZ/100+1);
+	}
+
+	scx200_acb_timeout(iface);
+}
+#endif /* POLLED_MODE */
+
+static void scx200_acb_reset(struct scx200_acb_iface *iface)
+{
+	/* Disable the ACCESS.bus device and Configure the SCL
+           frequency: 16 clock cycles */
+	outb(0x70, ACBCTL2);
+	/* Polling mode */
+	outb(0, ACBCTL1);
+	/* Disable slave address */
+	outb(0, ACBADDR);
+	/* Enable the ACCESS.bus device */
+	outb(inb(ACBCTL2) | ACBCTL2_ENABLE, ACBCTL2);
+	/* Free STALL after START */
+	outb(inb(ACBCTL1) & ~(ACBCTL1_STASTRE | ACBCTL1_NMINTE), ACBCTL1);
+	/* Send a STOP */
+	outb(inb(ACBCTL1) | ACBCTL1_STOP, ACBCTL1);
+	/* Clear BER, NEGACK and STASTR bits */
+	outb(ACBST_BER | ACBST_NEGACK | ACBST_STASTR, ACBST);
+	/* Clear BB bit */
+	outb(inb(ACBCST) | ACBCST_BB, ACBCST);
+}
+
+static s32 scx200_acb_smbus_xfer(struct i2c_adapter *adapter,
+				u16 address, unsigned short flags,	
+				char rw, u8 command, int size, 
+				union i2c_smbus_data *data)
+{
+	struct scx200_acb_iface *iface = adapter->data;
+	int len;
+	u8 *buffer;
+	u16 cur_word;
+	int rc;
+
+	switch (size) {
+	case I2C_SMBUS_QUICK:
+	    	len = 0;
+	    	buffer = NULL;
+	    	break;
+	case I2C_SMBUS_BYTE:
+		if (rw == I2C_SMBUS_READ) {
+			len = 1;
+			buffer = &data->byte;
+		} else {
+			len = 1;
+			buffer = &command;
+		}
+	    	break;
+	case I2C_SMBUS_BYTE_DATA:
+	    	len = 1;
+	    	buffer = &data->byte;
+	    	break;
+	case I2C_SMBUS_WORD_DATA:
+		len = 2;
+	    	cur_word = cpu_to_le16(data->word);
+	    	buffer = (u8 *)&cur_word;
+		break;
+	case I2C_SMBUS_BLOCK_DATA:
+	    	len = data->block[0];
+	    	buffer = &data->block[1];
+		break;
+	default:
+	    	return -EINVAL;
+	}
+
+	DBG("size=%d, address=0x%x, command=0x%x, len=%d, read=%d\n",
+	    size, address, command, len, rw == I2C_SMBUS_READ);
+
+	if (!len && rw == I2C_SMBUS_READ) {
+		printk(KERN_WARNING NAME ": %s, zero length read\n", 
+		       adapter->name);
+		return -EINVAL;
+	}
+
+	if (len && !buffer) {
+		printk(KERN_WARNING NAME ": %s, nonzero length but no buffer\n", adapter->name);
+		return -EFAULT;
+	}
+
+	down(&iface->sem);
+
+	iface->address_byte = address<<1;
+	if (rw == I2C_SMBUS_READ)
+		iface->address_byte |= 1;
+	iface->command = command;
+	iface->ptr = buffer;
+	iface->len = len;
+	iface->result = -EINVAL;
+	iface->needs_reset = 0;
+
+	outb(inb(ACBCTL1) | ACBCTL1_START, ACBCTL1);
+
+	if (size == I2C_SMBUS_QUICK || size == I2C_SMBUS_BYTE)
+		iface->state = state_quick;
+	else
+		iface->state = state_address;
+
+#ifdef POLLED_MODE
+	while (iface->state != state_idle)
+		scx200_acb_poll(iface);
+#else /* POLLED_MODE */
+#error Interrupt driven mode not implemented
+#endif /* POLLED_MODE */	
+
+	if (iface->needs_reset)
+		scx200_acb_reset(iface);
+
+	rc = iface->result;
+
+	up(&iface->sem);
+
+	if (rc == 0 && size == I2C_SMBUS_WORD_DATA && rw == I2C_SMBUS_READ)
+	    	data->word = le16_to_cpu(cur_word);
+
+#if DEBUG
+	printk(KERN_DEBUG NAME ": transfer done, result: %d", rc);
+	if (buffer) {
+		int i;
+		printk(" data:");
+		for (i = 0; i < len; ++i)
+			printk(" %02x", buffer[i]);
+	}
+	printk("\n");
+#endif
+
+	return rc;
+}
+
+static u32 scx200_acb_func(struct i2c_adapter *adapter)
+{
+	return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
+	       I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
+	       I2C_FUNC_SMBUS_BLOCK_DATA;
+}
+
+static int scx200_acb_reg(struct i2c_client *client)
+{
+	return 0;
+}
+
+static int scx200_acb_unreg(struct i2c_client *client)
+{
+	return 0;
+}
+
+static void scx200_acb_inc_use(struct i2c_adapter *adapter)
+{
+	MOD_INC_USE_COUNT;
+}
+
+static void scx200_acb_dec_use(struct i2c_adapter *adapter)
+{
+	MOD_DEC_USE_COUNT;
+}
+
+/* For now, we only handle combined mode (smbus) */
+static struct i2c_algorithm scx200_acb_algorithm = {
+	name:		"NatSemi SCx200 ACCESS.bus",
+	id:		I2C_ALGO_SMBUS,
+	smbus_xfer:	scx200_acb_smbus_xfer,
+	functionality:	scx200_acb_func,
+};
+
+struct scx200_acb_iface *scx200_acb_list;
+
+int scx200_acb_probe(struct scx200_acb_iface *iface)
+{
+	u8 val;
+
+	/* Disable the ACCESS.bus device and Configure the SCL
+           frequency: 16 clock cycles */
+	outb(0x70, ACBCTL2);
+
+	if (inb(ACBCTL2) != 0x70) {
+		DBG("ACBCTL2 readback failed\n");
+		return -ENXIO;
+	}
+
+	outb(inb(ACBCTL1) | ACBCTL1_NMINTE, ACBCTL1);
+
+	val = inb(ACBCTL1);
+	if (val) {
+		DBG("disabled, but ACBCTL1=0x%02x\n", val);
+		return -ENXIO;
+	}
+
+	outb(inb(ACBCTL2) | ACBCTL2_ENABLE, ACBCTL2);
+
+	outb(inb(ACBCTL1) | ACBCTL1_NMINTE, ACBCTL1);
+
+	val = inb(ACBCTL1);
+	if ((val & ACBCTL1_NMINTE) != ACBCTL1_NMINTE) {
+		DBG("enabled, but NMINTE won't be set, ACBCTL1=0x%02x\n", val);
+		return -ENXIO;
+	}
+
+	return 0;
+}
+
+static int  __init scx200_acb_create(int base, int index)
+{
+	struct scx200_acb_iface *iface;
+	struct i2c_adapter *adapter;
+	int rc = 0;
+	char description[64];
+
+	iface = kmalloc(sizeof(*iface), GFP_KERNEL);
+	if (!iface) {
+		printk(KERN_ERR NAME ": can't allocate memory\n");
+		rc = -ENOMEM;
+		goto errout;
+	}
+
+	memset(iface, 0, sizeof(*iface));
+	adapter = &iface->adapter;
+	adapter->data = iface;
+	sprintf(adapter->name, "SCx200 ACB%d", index);
+	adapter->id = I2C_ALGO_SMBUS;
+	adapter->algo = &scx200_acb_algorithm;
+	adapter->inc_use = scx200_acb_inc_use;
+	adapter->dec_use = scx200_acb_dec_use;
+	adapter->client_register = scx200_acb_reg;
+	adapter->client_unregister = scx200_acb_unreg;
+
+	init_MUTEX(&iface->sem);
+
+	sprintf(description, "NatSemi SCx200 ACCESS.bus [%s]", adapter->name);
+	if (request_region(base, 8, description) == 0) {
+		printk(KERN_ERR NAME ": %s, can't allocate io 0x%x-0x%x\n", 
+		       adapter->name, base, base + 8-1);
+		rc = -EBUSY;
+		goto errout;
+	}
+	iface->base = base;
+
+	rc = scx200_acb_probe(iface);
+	if (rc) {
+		printk(KERN_WARNING NAME ": %s, probe failed\n", adapter->name);
+		goto errout;
+	}
+
+	scx200_acb_reset(iface);
+
+	if (i2c_add_adapter(adapter) < 0) {
+		printk(KERN_ERR NAME ": %s, failed to register\n", adapter->name);
+		rc = -ENODEV;
+		goto errout;
+	}
+
+	lock_kernel();
+	iface->next = scx200_acb_list;
+	scx200_acb_list = iface;
+	unlock_kernel();
+
+	return 0;
+
+ errout:
+	if (iface) {
+		if (iface->base)
+			release_region(iface->base, 8);
+		kfree(iface);
+	}
+	return rc;
+}
+
+static int __init scx200_acb_init(void)
+{
+	int i;
+	int rc;
+
+	printk(KERN_DEBUG NAME ": NatSemi SCx200 ACCESS.bus Driver\n");
+
+	/* Verify that this really is a SCx200 processor */
+	if (pci_find_device(PCI_VENDOR_ID_NS,
+			    PCI_DEVICE_ID_NS_SCx200_BRIDGE,
+			    NULL) == NULL)
+		return -ENODEV;
+
+	rc = -ENXIO;
+	for (i = 0; i < MAX_DEVICES; ++i) {
+		if (base[i] > 0)
+			rc = scx200_acb_create(base[i], i);
+	}
+	if (scx200_acb_list)
+		return 0;
+	return rc;
+}
+
+static void __exit scx200_acb_cleanup(void)
+{
+	struct scx200_acb_iface *iface;
+	lock_kernel();
+	while ((iface = scx200_acb_list) != NULL) {
+		scx200_acb_list = iface->next;
+		unlock_kernel();
+
+		i2c_del_adapter(&iface->adapter);
+		release_region(iface->base, 8);
+		kfree(iface);
+		lock_kernel();
+	}
+	unlock_kernel();
+}
+
+module_init(scx200_acb_init);
+module_exit(scx200_acb_cleanup);
+
+/*
+    Local variables:
+        compile-command: "make -k -C ../.. SUBDIRS=drivers/i2c modules"
+        c-basic-offset: 8
+    End:
+*/
+
diff -Nru a/drivers/i2c/scx200_i2c.c b/drivers/i2c/scx200_i2c.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/i2c/scx200_i2c.c	Mon Oct  7 11:26:06 2002
@@ -0,0 +1,156 @@
+/* linux/drivers/i2c/scx200_i2c.c 
+
+   Copyright (c) 2001,2002 Christer Weinigel <wingel@nano-system.com>
+
+   National Semiconductor SCx200 I2C bus on GPIO pins
+
+   Based on i2c-velleman.c Copyright (C) 1995-96, 2000 Simon G. Vogl
+
+   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.		     
+*/
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/i2c-algo-bit.h>
+#include <asm/io.h>
+
+#include <linux/scx200_gpio.h>
+
+#define NAME "scx200_i2c"
+
+MODULE_AUTHOR("Christer Weinigel <wingel@nano-system.com>");
+MODULE_DESCRIPTION("NatSemi SCx200 I2C Driver");
+MODULE_LICENSE("GPL");
+
+MODULE_PARM(scl, "i");
+MODULE_PARM_DESC(scl, "GPIO line for SCL");
+MODULE_PARM(sda, "i");
+MODULE_PARM_DESC(sda, "GPIO line for SDA");
+
+static int scl = CONFIG_SCx200_I2C_SCL;
+static int sda = CONFIG_SCx200_I2C_SDA;
+
+static void scx200_i2c_setscl(void *data, int state)
+{
+	scx200_gpio_set(scl, state);
+}
+
+static void scx200_i2c_setsda(void *data, int state)
+{
+	scx200_gpio_set(sda, state);
+} 
+
+static int scx200_i2c_getscl(void *data)
+{
+	return scx200_gpio_get(scl);
+}
+
+static int scx200_i2c_getsda(void *data)
+{
+	return scx200_gpio_get(sda);
+}
+
+static int scx200_i2c_reg(struct i2c_client *client)
+{
+	return 0;
+}
+
+static int scx200_i2c_unreg(struct i2c_client *client)
+{
+	return 0;
+}
+
+static void scx200_i2c_inc_use(struct i2c_adapter *adap)
+{
+	MOD_INC_USE_COUNT;
+}
+
+static void scx200_i2c_dec_use(struct i2c_adapter *adap)
+{
+	MOD_DEC_USE_COUNT;
+}
+
+/* ------------------------------------------------------------------------
+ * Encapsulate the above functions in the correct operations structure.
+ * This is only done when more than one hardware adapter is supported.
+ */
+
+static struct i2c_algo_bit_data scx200_i2c_data = {
+	NULL,
+	scx200_i2c_setsda,
+	scx200_i2c_setscl,
+	scx200_i2c_getsda,
+	scx200_i2c_getscl,
+	10, 10, 100,		/* waits, timeout */
+};
+
+static struct i2c_adapter scx200_i2c_ops = {
+	.name              = "NatSemi SCx200 I2C",
+	.id		   = I2C_HW_B_VELLE,
+	.algo_data	   = &scx200_i2c_data,
+	.inc_use	   = scx200_i2c_inc_use,
+	.dec_use	   = scx200_i2c_dec_use,
+	.client_register   = scx200_i2c_reg,
+	.client_unregister = scx200_i2c_unreg,
+};
+
+int scx200_i2c_init(void)
+{
+	printk(KERN_DEBUG NAME ": NatSemi SCx200 I2C Driver\n");
+
+	if (!scx200_gpio_present()) {
+		printk(KERN_ERR NAME ": no SCx200 gpio pins available\n");
+		return -ENODEV;
+	}
+
+	printk(KERN_DEBUG NAME ": SCL=GPIO%02u, SDA=GPIO%02u\n", 
+	       scl, sda);
+
+	if (scl == -1 || sda == -1 || scl == sda) {
+		printk(KERN_ERR NAME ": scl and sda must be specified\n");
+		return -EINVAL;
+	}
+
+	/* Configure GPIOs as open collector outputs */
+	scx200_gpio_configure(scl, ~2, 5);
+	scx200_gpio_configure(sda, ~2, 5);
+
+	if (i2c_bit_add_bus(&scx200_i2c_ops) < 0) {
+		printk(KERN_ERR NAME ": adapter %s registration failed\n", 
+		       scx200_i2c_ops.name);
+		return -ENODEV;
+	}
+	
+	return 0;
+}
+
+void scx200_i2c_cleanup(void)
+{
+	i2c_bit_del_bus(&scx200_i2c_ops);
+}
+
+module_init(scx200_i2c_init);
+module_exit(scx200_i2c_cleanup);
+
+/*
+    Local variables:
+        compile-command: "make -k -C ../.. SUBDIRS=drivers/i2c modules"
+        c-basic-offset: 8
+    End:
+*/
diff -Nru a/drivers/ide/Config.in b/drivers/ide/Config.in
--- a/drivers/ide/Config.in	Mon Oct  7 11:25:26 2002
+++ b/drivers/ide/Config.in	Mon Oct  7 11:25:26 2002
@@ -31,7 +31,7 @@
       dep_bool '    CMD640 enhanced support' CONFIG_BLK_DEV_CMD640_ENHANCED $CONFIG_BLK_DEV_CMD640
       dep_bool '  ISA-PNP EIDE support' CONFIG_BLK_DEV_ISAPNP $CONFIG_ISAPNP
       if [ "$CONFIG_PCI" = "y" ]; then
-	 bool '  Generic PCI IDE chipset support' CONFIG_BLK_DEV_IDEPCI
+	 bool '  PCI IDE chipset support' CONFIG_BLK_DEV_IDEPCI
 	 if [ "$CONFIG_BLK_DEV_IDEPCI" = "y" ]; then
 	    dep_bool '    Generic PCI IDE Chipset Support' CONFIG_BLK_DEV_GENERIC $CONFIG_BLK_DEV_IDEPCI
 	    bool '    Sharing PCI IDE interrupts support' CONFIG_IDEPCI_SHARE_IRQ
diff -Nru a/drivers/ide/arm/Makefile b/drivers/ide/arm/Makefile
--- a/drivers/ide/arm/Makefile	Mon Oct  7 11:25:25 2002
+++ b/drivers/ide/arm/Makefile	Mon Oct  7 11:25:25 2002
@@ -2,6 +2,6 @@
 obj-$(CONFIG_BLK_DEV_IDE_ICSIDE)	+= icside.o
 obj-$(CONFIG_BLK_DEV_IDE_RAPIDE)	+= rapide.o
 
-EXTRA_CFLAGS	:= -I../
+EXTRA_CFLAGS	:= -Idrivers/ide
 
 include $(TOPDIR)/Rules.make
diff -Nru a/drivers/ide/ide-cd.c b/drivers/ide/ide-cd.c
--- a/drivers/ide/ide-cd.c	Mon Oct  7 11:25:34 2002
+++ b/drivers/ide/ide-cd.c	Mon Oct  7 11:25:34 2002
@@ -830,9 +830,6 @@
 	if (IDE_CONTROL_REG)
 		HWIF(drive)->OUTB(drive->ctl, IDE_CONTROL_REG);
  
-	if (info->dma)
-		(void) (HWIF(drive)->ide_dma_begin(drive));
-
 	if (CDROM_CONFIG_FLAGS (drive)->drq_interrupt) {
 		if (HWGROUP(drive)->handler != NULL)
 			BUG();
@@ -863,6 +860,7 @@
 	unsigned char *cmd_buf	= pc->c;
 	int cmd_len		= sizeof(pc->c);
 	unsigned int timeout	= pc->timeout;
+	struct cdrom_info *info = drive->driver_data;
 	ide_startstop_t startstop;
 
 	if (CDROM_CONFIG_FLAGS(drive)->drq_interrupt) {
@@ -888,6 +886,11 @@
 
 	/* Send the command to the device. */
 	HWIF(drive)->atapi_output_bytes(drive, cmd_buf, cmd_len);
+
+	/* Start the DMA if need be */
+	if (info->dma)
+		(void) HWIF(drive)->ide_dma_begin(drive);
+
 	return ide_started;
 }
 
@@ -2245,6 +2248,8 @@
 	   touch it at all. */
 	memset(&pc, 0, sizeof(pc));
 	memcpy(pc.c, cgc->cmd, CDROM_PACKET_SIZE);
+	if (cgc->sense)
+		memset(cgc->sense, 0, sizeof(struct request_sense));
 	pc.buffer = cgc->buffer;
 	pc.buflen = cgc->buflen;
 	pc.quiet = cgc->quiet;
diff -Nru a/drivers/ide/ide-disk.c b/drivers/ide/ide-disk.c
--- a/drivers/ide/ide-disk.c	Mon Oct  7 11:25:33 2002
+++ b/drivers/ide/ide-disk.c	Mon Oct  7 11:25:33 2002
@@ -544,6 +544,7 @@
  */
 static ide_startstop_t do_rw_disk (ide_drive_t *drive, struct request *rq, unsigned long block)
 {
+	BUG_ON(drive->blocked);
 	if (!blk_fs_request(rq)) {
 		blk_dump_rq_flags(rq, "do_rw_disk - bad command");
 		ide_end_request(drive, 0, 0);
@@ -1495,8 +1496,70 @@
  	ide_add_setting(drive,	"max_failures",		SETTING_RW,					-1,			-1,			TYPE_INT,	0,	65535,				1,	1,	&drive->max_failures,		NULL);
 }
 
+static int idedisk_suspend(struct device *dev, u32 state, u32 level)
+{
+	ide_drive_t *drive = dev->driver_data;
+
+	printk("Suspending device %p\n", dev->driver_data);
+
+	/* I hope that every freeze operation from the upper levels have
+	 * already been done...
+	 */
+
+	if (level != SUSPEND_SAVE_STATE)
+		return 0;
+	BUG_ON(in_interrupt());
+
+	printk("Waiting for commands to finish\n");
+
+	/* wait until all commands are finished */
+	/* FIXME: waiting for spinlocks should be done instead. */
+	if (!(HWGROUP(drive)))
+		printk("No hwgroup?\n");
+	while (HWGROUP(drive)->handler)
+		yield();
+
+	/* set the drive to standby */
+	printk(KERN_INFO "suspending: %s ", drive->name);
+	if (drive->driver) {
+		if (drive->driver->standby)
+			drive->driver->standby(drive);
+	}
+	drive->blocked = 1;
+
+	while (HWGROUP(drive)->handler)
+		yield();
+
+	return 0;
+}
+
+static int idedisk_resume(struct device *dev, u32 level)
+{
+	ide_drive_t *drive = dev->driver_data;
+
+	if (level != RESUME_RESTORE_STATE)
+		return 0;
+	if (!drive->blocked)
+		panic("ide: Resume but not suspended?\n");
+
+	drive->blocked = 0;
+	return 0;
+}
+
+
+/* This is just a hook for the overall driver tree.
+ */
+
+static struct device_driver idedisk_devdrv = {
+	.bus = &ide_bus_type,
+	.name = "IDE disk driver",
+
+	.suspend = idedisk_suspend,
+	.resume = idedisk_resume,
+};
+
 static int idedisk_ioctl (ide_drive_t *drive, struct inode *inode,
-		struct file *file, unsigned int cmd, unsigned long arg)
+	struct file *file, unsigned int cmd, unsigned long arg)
 {
 #if 0
 HDIO_GET_ADDRESS
@@ -1540,6 +1603,11 @@
 			drive->doorlocking = 1;
 		}
 	}
+	{
+		sprintf(drive->disk->disk_dev.name, "ide-disk");
+		drive->disk->disk_dev.driver = &idedisk_devdrv;
+		drive->disk->disk_dev.driver_data = drive;
+	}
 
 #if 1
 	(void) probe_lba_addressing(drive, 1);
@@ -1623,6 +1691,8 @@
 static int idedisk_cleanup (ide_drive_t *drive)
 {
 	struct gendisk *g = drive->disk;
+
+	put_device(&drive->disk->disk_dev);
 	if ((drive->id->cfs_enable_2 & 0x3000) && drive->wcache)
 		if (do_idedisk_flushcache(drive))
 			printk (KERN_INFO "%s: Write Cache FAILED Flushing!\n",
@@ -1721,6 +1791,7 @@
 static int idedisk_init (void)
 {
 	ide_register_driver(&idedisk_driver);
+	driver_register(&idedisk_devdrv);
 	return 0;
 }
 
diff -Nru a/drivers/ide/ide-iops.c b/drivers/ide/ide-iops.c
--- a/drivers/ide/ide-iops.c	Mon Oct  7 11:25:30 2002
+++ b/drivers/ide/ide-iops.c	Mon Oct  7 11:25:30 2002
@@ -32,135 +32,156 @@
 #include <asm/bitops.h>
 
 
-static inline u8 ide_inb (u32 port)
+static u8 ide_inb (ide_ioreg_t port)
 {
-	return (u8) IN_BYTE(port);
+	return (u8) inb(port);
 }
 
-static inline u8 ide_inb_p (u32 port)
+static u16 ide_inw (ide_ioreg_t port)
 {
-	return (u8) IN_BYTE_P(port);
+	return (u16) inw(port);
 }
 
-static inline u16 ide_inw (u32 port)
+static void ide_insw (ide_ioreg_t port, void *addr, u32 count)
 {
-	return (u16) IN_WORD(port);
+	return insw(port, addr, count);
 }
 
-static inline u16 ide_inw_p (u32 port)
+static u32 ide_inl (ide_ioreg_t port)
 {
-	return (u16) IN_WORD_P(port);
+	return (u32) inl(port);
 }
 
-static inline void ide_insw (u32 port, void *addr, u32 count)
+static void ide_insl (ide_ioreg_t port, void *addr, u32 count)
 {
-	while (count--) { *(u16 *)addr = IN_WORD(port); addr += 2; }
+	insl(port, addr, count);
 }
 
-static inline void ide_insw_p (u32 port, void *addr, u32 count)
+static void ide_outb (u8 addr, ide_ioreg_t port)
 {
-	while (count--) { *(u16 *)addr = IN_WORD_P(port); addr += 2; }
+	outb(addr, port);
 }
 
-static inline u32 ide_inl (u32 port)
+static void ide_outw (u16 addr, ide_ioreg_t port)
 {
-	return (u32) IN_LONG(port);
+	outw(addr, port);
 }
 
-static inline u32 ide_inl_p (u32 port)
+static void ide_outsw (ide_ioreg_t port, void *addr, u32 count)
 {
-        return (u32) IN_LONG_P(port);
+	outsw(port, addr, count);
 }
 
-static inline void ide_insl (u32 port, void *addr, u32 count)
+static void ide_outl (u32 addr, ide_ioreg_t port)
 {
-	ide_insw(port, addr, (count)<<1);
-//	while (count--) { *(u32 *)addr = IN_LONG(port); addr += 4; }
+	outl(addr, port);
 }
 
-static inline void ide_insl_p (u32 port, void *addr, u32 count)
+static void ide_outsl (ide_ioreg_t port, void *addr, u32 count)
 {
-	ide_insw_p(port, addr, (count)<<1);
-//	while (count--) { *(u32 *)addr = IN_LONG(port); addr += 4; }
+	return outsl(port, addr, count);
 }
 
-static inline void ide_outb (u8 addr, u32 port)
+void default_hwif_iops (ide_hwif_t *hwif)
 {
-	OUT_BYTE(addr, port);
+	hwif->OUTB	= ide_outb;
+	hwif->OUTW	= ide_outw;
+	hwif->OUTL	= ide_outl;
+	hwif->OUTSW	= ide_outsw;
+	hwif->OUTSL	= ide_outsl;
+	hwif->INB	= ide_inb;
+	hwif->INW	= ide_inw;
+	hwif->INL	= ide_inl;
+	hwif->INSW	= ide_insw;
+	hwif->INSL	= ide_insl;
 }
 
-static inline void ide_outb_p (u8 addr, u32 port)
+EXPORT_SYMBOL(default_hwif_iops);
+
+static u8 ide_mm_inb (ide_ioreg_t port)
 {
-	OUT_BYTE_P(addr, port);
+	return (u8) readb(port);
 }
 
-static inline void ide_outw (u16 addr, u32 port)
+static u16 ide_mm_inw (ide_ioreg_t port)
 {
-	OUT_WORD(addr, port);
+	return (u16) readw(port);
 }
 
-static inline void ide_outw_p (u16 addr, u32 port)
+static void ide_mm_insw (ide_ioreg_t port, void *addr, u32 count)
 {
-	OUT_WORD_P(addr, port);
+#ifdef CONFIG_PPC
+	/* Can we move the barrier out of the loop ? */
+	while (count--) { *(u16 *)addr = __raw_readw(port); iobarrier_r(); addr += 2; }
+#else /* everything else is sane benh */
+	while (count--) { *(u16 *)addr = readw(port); addr += 2; }
+#endif
 }
 
-static inline void ide_outsw (u32 port, void *addr, u32 count)
+static u32 ide_mm_inl (ide_ioreg_t port)
 {
-	while (count--) { OUT_WORD(*(u16 *)addr, port); addr += 2; }
+	return (u32) readl(port);
 }
 
-static inline void ide_outsw_p (u32 port, void *addr, u32 count)
+static void ide_mm_insl (ide_ioreg_t port, void *addr, u32 count)
 {
-	while (count--) { OUT_WORD_P(*(u16 *)addr, port); addr += 2; }
+#ifdef CONFIG_PPC
+	/* Can we move the barrier out of the loop ? */
+	while (count--) { *(u32 *)addr = __raw_readl(port); iobarrier_r(); addr += 4; }
+#else /* everything else is sane benh */
+	while (count--) { *(u32 *)addr = readl(port); addr += 4; }
+#endif
 }
 
-static inline void ide_outl (u32 addr, u32 port)
+static void ide_mm_outb (u8 value, ide_ioreg_t port)
 {
-	OUT_LONG(addr, port);
+	writeb(value, port);
 }
 
-static inline void ide_outl_p (u32 addr, u32 port)
+static void ide_mm_outw (u16 value, ide_ioreg_t port)
 {
-	OUT_LONG_P(addr, port);
+	writew(value, port);
 }
 
-static inline void ide_outsl (u32 port, void *addr, u32 count)
+static void ide_mm_outsw (ide_ioreg_t port, void *addr, u32 count)
 {
-	ide_outsw(port, addr, (count)<<1);
-//	while (count--) { OUT_LONG(*(u32 *)addr, port); addr += 4; }
+#ifdef CONFIG_PPC
+	/* Can we move the barrier out of the loop ? */
+	while (count--) { __raw_writew(*(u16 *)addr, port); iobarrier_w(); addr += 2; }
+#else /* everything else is sane benh */
+	while (count--) { writew(*(u16 *)addr, port); addr += 2; }
+#endif
 }
 
-static inline void ide_outsl_p (u32 port, void *addr, u32 count)
+static void ide_mm_outl (u32 value, ide_ioreg_t port)
 {
-	ide_outsw_p(port, addr, (count)<<1);
-//	while (count--) { OUT_LONG_P(*(u32 *)addr, port); addr += 4; }
+	writel(value, port);
 }
 
-void default_hwif_iops (ide_hwif_t *hwif)
+static void ide_mm_outsl (ide_ioreg_t port, void *addr, u32 count)
 {
-	hwif->OUTB	= ide_outb;
-	hwif->OUTBP	= ide_outb_p;
-	hwif->OUTW	= ide_outw;
-	hwif->OUTWP	= ide_outw_p;
-	hwif->OUTL	= ide_outl;
-	hwif->OUTLP	= ide_outl_p;
-	hwif->OUTSW	= ide_outsw;
-	hwif->OUTSWP	= ide_outsw_p;
-	hwif->OUTSL	= ide_outsl;
-	hwif->OUTSLP	= ide_outsl_p;
-	hwif->INB	= ide_inb;
-	hwif->INBP	= ide_inb_p;
-	hwif->INW	= ide_inw;
-	hwif->INWP	= ide_inw_p;
-	hwif->INL	= ide_inl;
-	hwif->INLP	= ide_inl_p;
-	hwif->INSW	= ide_insw;
-	hwif->INSWP	= ide_insw_p;
-	hwif->INSL	= ide_insl;
-	hwif->INSLP	= ide_insl_p;
+#ifdef CONFIG_PPC
+	while (count--) { __raw_writel(*(u32 *)addr, port); iobarrier_w(); addr += 4; }
+#else /* everything else is sane benh */
+	while (count--) { writel(*(u32 *)addr, port); addr += 4; }
+#endif
 }
 
-EXPORT_SYMBOL(default_hwif_iops);
+void default_hwif_mmiops (ide_hwif_t *hwif)
+{
+	hwif->OUTB	= ide_mm_outb;
+	hwif->OUTW	= ide_mm_outw;
+	hwif->OUTL	= ide_mm_outl;
+	hwif->OUTSW	= ide_mm_outsw;
+	hwif->OUTSL	= ide_mm_outsl;
+	hwif->INB	= ide_mm_inb;
+	hwif->INW	= ide_mm_inw;
+	hwif->INL	= ide_mm_inl;
+	hwif->INSW	= ide_mm_insw;
+	hwif->INSL	= ide_mm_insl;
+}
+
+EXPORT_SYMBOL(default_hwif_mmiops);
 
 void default_hwif_transport (ide_hwif_t *hwif)
 {
@@ -217,7 +238,6 @@
 
 EXPORT_SYMBOL(QUIRK_LIST);
 
-#if SUPPORT_VLB_SYNC
 /*
  * Some localbus EIDE interfaces require a special access sequence
  * when using 32-bit I/O instructions to transfer data.  We call this
@@ -233,7 +253,6 @@
 }
 
 EXPORT_SYMBOL(ata_vlb_sync);
-#endif /* SUPPORT_VLB_SYNC */
 
 /*
  * This is used for most PIO data transfers *from* the IDE interface
@@ -244,7 +263,6 @@
 	u8 io_32bit		= drive->io_32bit;
 
 	if (io_32bit) {
-#if SUPPORT_VLB_SYNC
 		if (io_32bit & 2) {
 			unsigned long flags;
 			local_irq_save(flags);
@@ -252,19 +270,9 @@
 			hwif->INSL(IDE_DATA_REG, buffer, wcount);
 			local_irq_restore(flags);
 		} else
-#endif /* SUPPORT_VLB_SYNC */
 			hwif->INSL(IDE_DATA_REG, buffer, wcount);
 	} else {
-#if SUPPORT_SLOW_DATA_PORTS
-		if (drive->slow) {
-			u16 *ptr = (u16 *) buffer;
-			while (wcount--) {
-				*ptr++ = hwif->INWP(IDE_DATA_REG);
-				*ptr++ = hwif->INWP(IDE_DATA_REG);
-			}
-		} else
-#endif /* SUPPORT_SLOW_DATA_PORTS */
-			hwif->INSW(IDE_DATA_REG, buffer, wcount<<1);
+		hwif->INSW(IDE_DATA_REG, buffer, wcount<<1);
 	}
 }
 
@@ -279,7 +287,6 @@
 	u8 io_32bit		= drive->io_32bit;
 
 	if (io_32bit) {
-#if SUPPORT_VLB_SYNC
 		if (io_32bit & 2) {
 			unsigned long flags;
 			local_irq_save(flags);
@@ -287,19 +294,9 @@
 			hwif->OUTSL(IDE_DATA_REG, buffer, wcount);
 			local_irq_restore(flags);
 		} else
-#endif /* SUPPORT_VLB_SYNC */
 			hwif->OUTSL(IDE_DATA_REG, buffer, wcount);
 	} else {
-#if SUPPORT_SLOW_DATA_PORTS
-		if (drive->slow) {
-			u16 *ptr = (u16 *) buffer;
-			while (wcount--) {
-				hwif->OUTWP(*ptr++, IDE_DATA_REG);
-				hwif->OUTWP(*ptr++, IDE_DATA_REG);
-			}
-		} else
-#endif /* SUPPORT_SLOW_DATA_PORTS */
-			hwif->OUTSW(IDE_DATA_REG, buffer, wcount<<1);
+		hwif->OUTSW(IDE_DATA_REG, buffer, wcount<<1);
 	}
 }
 
@@ -312,6 +309,7 @@
  * so if an odd bytecount is specified, be sure that there's at least one
  * extra byte allocated for the buffer.
  */
+
 void atapi_input_bytes (ide_drive_t *drive, void *buffer, u32 bytecount)
 {
 	ide_hwif_t *hwif = HWIF(drive);
diff -Nru a/drivers/ide/ide-pnp.c b/drivers/ide/ide-pnp.c
--- a/drivers/ide/ide-pnp.c	Mon Oct  7 11:25:36 2002
+++ b/drivers/ide/ide-pnp.c	Mon Oct  7 11:25:36 2002
@@ -52,6 +52,7 @@
 static int __init pnpide_generic_init(struct pci_dev *dev, int enable)
 {
 	hw_regs_t hw;
+	ide_hwif_t *hwif;
 	int index;
 
 	if (!enable)
@@ -67,10 +68,11 @@
 //			generic_pnp_ide_iops,
 			DEV_IRQ(dev, 0));
 
-	index = ide_register_hw(&hw, NULL);
+	index = ide_register_hw(&hw, &hwif);
 
 	if (index != -1) {
 	    	printk(KERN_INFO "ide%d: %s IDE interface\n", index, DEV_NAME(dev));
+		hwif->pci_dev = dev;
 		return 0;
 	}
 
diff -Nru a/drivers/ide/ide-probe.c b/drivers/ide/ide-probe.c
--- a/drivers/ide/ide-probe.c	Mon Oct  7 11:25:35 2002
+++ b/drivers/ide/ide-probe.c	Mon Oct  7 11:25:35 2002
@@ -46,6 +46,7 @@
 #include <linux/delay.h>
 #include <linux/ide.h>
 #include <linux/spinlock.h>
+#include <linux/pci.h>
 
 #include <asm/byteorder.h>
 #include <asm/irq.h>
@@ -566,6 +567,11 @@
 	/* register with global device tree */
 	strncpy(hwif->gendev.bus_id,hwif->name,BUS_ID_SIZE);
 	snprintf(hwif->gendev.name,DEVICE_NAME_SIZE,"IDE Controller");
+	hwif->gendev.driver_data = hwif;
+	if (hwif->pci_dev)
+		hwif->gendev.parent = &hwif->pci_dev->dev;
+	else
+		hwif->gendev.parent = NULL; /* Would like to do = &device_legacy */
 	device_register(&hwif->gendev);
 
 	if (hwif->mmio == 2)
diff -Nru a/drivers/ide/ide-taskfile.c b/drivers/ide/ide-taskfile.c
--- a/drivers/ide/ide-taskfile.c	Mon Oct  7 11:25:25 2002
+++ b/drivers/ide/ide-taskfile.c	Mon Oct  7 11:25:25 2002
@@ -183,39 +183,30 @@
 			return task->prehandler(drive, task->rq);
 		return ide_started;
 	}
-	/* for dma commands we down set the handler */
-	if (blk_fs_request(task->rq) && drive->using_dma) {
-		if (rq_data_dir(task->rq) == READ) {
+
+	if (!drive->using_dma)
+		return ide_stopped;
+
+	switch (taskfile->command) {
+		case WIN_WRITEDMA_ONCE:
+		case WIN_WRITEDMA:
+		case WIN_WRITEDMA_EXT:
+			if (hwif->ide_dma_write(drive))
+				return ide_stopped;
+			break;
+		case WIN_READDMA_ONCE:
+		case WIN_READDMA:
+		case WIN_READDMA_EXT:
+		case WIN_IDENTIFY_DMA:
 			if (hwif->ide_dma_read(drive))
 				return ide_stopped;
-		} else {
-			if (hwif->ide_dma_write(drive))
+			break;
+		default:
+			if (task->handler == NULL)
 				return ide_stopped;
-		}
-	} else {
-		if (!drive->using_dma && (task->handler == NULL))
-			return ide_stopped;
-
-		switch(taskfile->command) {
-			case WIN_WRITEDMA_ONCE:
-			case WIN_WRITEDMA:
-			case WIN_WRITEDMA_EXT:
-				if (hwif->ide_dma_write(drive))
-					return ide_stopped;
-				break;
-			case WIN_READDMA_ONCE:
-			case WIN_READDMA:
-			case WIN_READDMA_EXT:
-			case WIN_IDENTIFY_DMA:
-				if (hwif->ide_dma_read(drive))
-					return ide_stopped;
-				break;
-			default:
-				if (task->handler == NULL)
-					return ide_stopped;
-		}
 	}
-	return ide_started;
+
+	return ide_stopped;
 }
 
 EXPORT_SYMBOL(do_rw_taskfile);
diff -Nru a/drivers/ide/ide.c b/drivers/ide/ide.c
--- a/drivers/ide/ide.c	Mon Oct  7 11:25:29 2002
+++ b/drivers/ide/ide.c	Mon Oct  7 11:25:29 2002
@@ -1945,24 +1945,14 @@
 	hwif->OUTB		= old_hwif.OUTB;
 	hwif->OUTW		= old_hwif.OUTW;
 	hwif->OUTL		= old_hwif.OUTL;
-	hwif->OUTBP		= old_hwif.OUTBP;
-	hwif->OUTWP		= old_hwif.OUTWP;
-	hwif->OUTLP		= old_hwif.OUTLP;
 	hwif->OUTSW		= old_hwif.OUTSW;
-	hwif->OUTSWP		= old_hwif.OUTSWP;
 	hwif->OUTSL		= old_hwif.OUTSL;
-	hwif->OUTSLP		= old_hwif.OUTSLP;
 
 	hwif->INB		= old_hwif.INB;
 	hwif->INW		= old_hwif.INW;
 	hwif->INL		= old_hwif.INL;
-	hwif->INBP		= old_hwif.INBP;
-	hwif->INWP		= old_hwif.INWP;
-	hwif->INLP		= old_hwif.INLP;
 	hwif->INSW		= old_hwif.INSW;
-	hwif->INSWP		= old_hwif.INSWP;
 	hwif->INSL		= old_hwif.INSL;
-	hwif->INSLP		= old_hwif.INSLP;
 #endif
 
 	hwif->mmio			= old_hwif.mmio;
diff -Nru a/drivers/ide/legacy/Makefile b/drivers/ide/legacy/Makefile
--- a/drivers/ide/legacy/Makefile	Mon Oct  7 11:25:26 2002
+++ b/drivers/ide/legacy/Makefile	Mon Oct  7 11:25:26 2002
@@ -17,6 +17,6 @@
 # Last of all
 obj-$(CONFIG_BLK_DEV_HD)		+= hd.o
 
-EXTRA_CFLAGS	:= -I../
+EXTRA_CFLAGS	:= -Idrivers/ide
 
 include $(TOPDIR)/Rules.make
diff -Nru a/drivers/ide/legacy/hd.c b/drivers/ide/legacy/hd.c
--- a/drivers/ide/legacy/hd.c	Mon Oct  7 11:25:25 2002
+++ b/drivers/ide/legacy/hd.c	Mon Oct  7 11:25:25 2002
@@ -138,6 +138,8 @@
 static int NR_HD;
 #endif
 
+static struct gendisk *hd_gendisk[MAX_HD];
+
 static struct timer_list device_timer;
 
 #define TIMEOUT_VALUE (6*HZ)
@@ -590,14 +592,15 @@
 	dev = DEVICE_NR(CURRENT->rq_dev);
 	block = CURRENT->sector;
 	nsect = CURRENT->nr_sectors;
-	if (dev >= NR_HD || block >= get_capacity(hd_gendisk+dev) ||
-	    ((block+nsect) > get_capacity(hd_gendisk+unit))) {
-		if (dev >= NR_HD)
-			printk("hd: bad minor number: device=%s\n",
-			       kdevname(CURRENT->rq_dev));
-		else
-			printk("hd%c: bad access: block=%d, count=%d\n",
-				dev+'a', block, nsect);
+	if (dev >= NR_HD) {
+		printk("hd: bad disk number: %d\n", dev);
+		end_request(CURRENT, 0);
+		goto repeat;
+	}
+	if (block >= get_capacity(hd_gendisk[dev]) ||
+	    ((block+nsect) > get_capacity(hd_gendisk[dev]))) {
+		printk("%s: bad access: block=%d, count=%d\n",
+			hd_gendisk[dev]->disk_name, block, nsect);
 		end_request(CURRENT, 0);
 		goto repeat;
 	}
@@ -691,22 +694,6 @@
 
 extern struct block_device_operations hd_fops;
 
-static struct gendisk hd_gendisk[2] = {
-{
-	.major =	MAJOR_NR,
-	.first_minor =	0,
-	.disk_name =	"hda",
-	.minor_shift =	6,
-	.fops =		&hd_fops,
-},{
-	.major =	MAJOR_NR,
-	.first_minor =	64,
-	.disk_name =	"hdb",
-	.minor_shift =	6,
-	.fops =		&hd_fops,
-}
-};
-	
 static void hd_interrupt(int irq, void *dev_id, struct pt_regs *regs)
 {
 	void (*handler)(void) = do_hd;
@@ -733,10 +720,18 @@
  * We enable interrupts in some of the routines after making sure it's
  * safe.
  */
-static void __init hd_geninit(void)
+
+static int __init hd_init(void)
 {
 	int drive;
-
+	if (register_blkdev(MAJOR_NR,"hd",&hd_fops)) {
+		printk("hd: unable to get major %d for hard disk\n",MAJOR_NR);
+		return -1;
+	}
+	blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), do_hd_request, &hd_lock);
+	blk_queue_max_sectors(BLK_DEFAULT_QUEUE(MAJOR_NR), 255);
+	init_timer(&device_timer);
+	device_timer.function = hd_times_out;
 	blk_queue_hardsect_size(QUEUE, 512);
 
 #ifdef __i386__
@@ -805,57 +800,68 @@
 			" on kernel command line\n");
 	}
 #endif
+	if (!NR_HD)
+		goto out;
 
 	for (drive=0 ; drive < NR_HD ; drive++) {
+		struct gendisk *disk = alloc_disk();
+		if (!disk)
+			goto Enomem;
+		disk->major = MAJOR_NR;
+		disk->first_minor = drive << 6;
+		disk->minor_shift = 6;
+		disk->fops = &hd_fops;
+		sprintf(disk->disk_name, "hd%c", 'a'+drive);
+		hd_gendisk[drive] = disk;
+	}
+	for (drive=0 ; drive < NR_HD ; drive++) {
 		sector_t size = hd_info[drive].head *
 			hd_info[drive].sect * hd_info[drive].cyl;
-		set_capacity(hd_gendisk + drive, size);
-		printk ("%s: %ldMB, CHS=%d/%d/%d\n", hd_gendisk[drive].disk_name,
+		set_capacity(hd_gendisk[drive], size);
+		printk ("%s: %ldMB, CHS=%d/%d/%d\n",
+			hd_gendisk[drive]->disk_name,
 			size / 2048, hd_info[drive].cyl,
 			hd_info[drive].head, hd_info[drive].sect);
 	}
-	if (!NR_HD)
-		return;
 
 	if (request_irq(HD_IRQ, hd_interrupt, SA_INTERRUPT, "hd", NULL)) {
 		printk("hd: unable to get IRQ%d for the hard disk driver\n",
 			HD_IRQ);
-		NR_HD = 0;
-		return;
+		goto out1;
 	}
 	if (!request_region(HD_DATA, 8, "hd")) {
 		printk(KERN_WARNING "hd: port 0x%x busy\n", HD_DATA);
-		NR_HD = 0;
-		free_irq(HD_IRQ, NULL);
-		return;
+		goto out2;
 	}
 	if (!request_region(HD_CMD, 1, "hd(cmd)")) {
 		printk(KERN_WARNING "hd: port 0x%x busy\n", HD_CMD);
-		NR_HD = 0;
-		free_irq(HD_IRQ, NULL);
-		release_region(HD_DATA, 8);
-		return;
+		goto out3;
 	}
 
 	for(drive=0; drive < NR_HD; drive++) {
 		struct hd_i_struct *p = hd_info + drive;
-		set_capacity(hd_gendisk + drive, p->head * p->sect * p->cyl);
-		add_disk(hd_gendisk + drive);
-	}
-}
-
-static int __init hd_init(void)
-{
-	if (register_blkdev(MAJOR_NR,"hd",&hd_fops)) {
-		printk("hd: unable to get major %d for hard disk\n",MAJOR_NR);
-		return -1;
+		set_capacity(hd_gendisk[drive], p->head * p->sect * p->cyl);
+		add_disk(hd_gendisk[drive]);
 	}
-	blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), do_hd_request, &hd_lock);
-	blk_queue_max_sectors(BLK_DEFAULT_QUEUE(MAJOR_NR), 255);
-	init_timer(&device_timer);
-	device_timer.function = hd_times_out;
-	hd_geninit();
 	return 0;
+
+out3:
+	release_region(HD_DATA, 8);
+out2:
+	free_irq(HD_IRQ, NULL);
+out1:
+	for (drive = 0; drive < NR_HD; drive++)
+		put_disk(hd_gendisk[drive]);
+	NR_HD = 0;
+out:
+	del_timer(&device_timer);
+	unregister_blkdev(MAJOR_NR,"hd");
+	blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR));
+	return -1;
+Enomem:
+	while (drive--)
+		put_disk(hd_gendisk[drive]);
+	goto out;
 }
 
 static int parse_hd_setup (char *line) {
diff -Nru a/drivers/ide/pci/Makefile b/drivers/ide/pci/Makefile
--- a/drivers/ide/pci/Makefile	Mon Oct  7 11:25:26 2002
+++ b/drivers/ide/pci/Makefile	Mon Oct  7 11:25:26 2002
@@ -31,6 +31,6 @@
 # Must appear at the end of the block
 obj-$(CONFIG_BLK_DEV_GENERIC)          += generic.o
 
-EXTRA_CFLAGS	:= -I../
+EXTRA_CFLAGS	:= -Idrivers/ide
 
 include $(TOPDIR)/Rules.make
diff -Nru a/drivers/ide/pci/adma100.h b/drivers/ide/pci/adma100.h
--- a/drivers/ide/pci/adma100.h	Mon Oct  7 11:25:38 2002
+++ b/drivers/ide/pci/adma100.h	Mon Oct  7 11:25:38 2002
@@ -10,7 +10,7 @@
 extern void init_hwif_pdcadma(ide_hwif_t *);
 extern void init_dma_pdcadma(ide_hwif_t *, unsigned long);
 
-static ide_pci_device_t pdcadma_chipsets[] __initdata = {
+static ide_pci_device_t pdcadma_chipsets[] __devinitdata = {
 	{
 		vendor:	PCI_VENDOR_ID_PDC,
 		device:	PCI_DEVICE_ID_PDC_1841,
diff -Nru a/drivers/ide/pci/aec62xx.c b/drivers/ide/pci/aec62xx.c
--- a/drivers/ide/pci/aec62xx.c	Mon Oct  7 11:25:37 2002
+++ b/drivers/ide/pci/aec62xx.c	Mon Oct  7 11:25:37 2002
@@ -530,6 +530,7 @@
 	if (dev->device != d->device)
 		BUG();
 	d->init_setup(dev, d);
+	MOD_INC_USE_COUNT;
 	return 0;
 }
 
diff -Nru a/drivers/ide/pci/aec62xx.h b/drivers/ide/pci/aec62xx.h
--- a/drivers/ide/pci/aec62xx.h	Mon Oct  7 11:25:35 2002
+++ b/drivers/ide/pci/aec62xx.h	Mon Oct  7 11:25:35 2002
@@ -98,7 +98,7 @@
 static void init_hwif_aec62xx(ide_hwif_t *);
 static void init_dma_aec62xx(ide_hwif_t *, unsigned long);
 
-static ide_pci_device_t aec62xx_chipsets[] __initdata = {
+static ide_pci_device_t aec62xx_chipsets[] __devinitdata = {
 	{	/* 0 */
 		vendor:		PCI_VENDOR_ID_ARTOP,
 		device:		PCI_DEVICE_ID_ARTOP_ATP850UF,
diff -Nru a/drivers/ide/pci/alim15x3.c b/drivers/ide/pci/alim15x3.c
--- a/drivers/ide/pci/alim15x3.c	Mon Oct  7 11:25:31 2002
+++ b/drivers/ide/pci/alim15x3.c	Mon Oct  7 11:25:31 2002
@@ -858,6 +858,7 @@
 	d->init_hwif = init_hwif_common_ali15x3;
 #endif /* CONFIG_SPARC64 */
 	ide_setup_pci_device(dev, d);
+	MOD_INC_USE_COUNT;
 	return 0;
 }
 
diff -Nru a/drivers/ide/pci/alim15x3.h b/drivers/ide/pci/alim15x3.h
--- a/drivers/ide/pci/alim15x3.h	Mon Oct  7 11:25:25 2002
+++ b/drivers/ide/pci/alim15x3.h	Mon Oct  7 11:25:25 2002
@@ -30,7 +30,7 @@
 static void init_hwif_ali15x3(ide_hwif_t *);
 static void init_dma_ali15x3(ide_hwif_t *, unsigned long);
 
-static ide_pci_device_t ali15x3_chipsets[] __initdata = {
+static ide_pci_device_t ali15x3_chipsets[] __devinitdata = {
 	{	/* 0 */
 		vendor:		PCI_VENDOR_ID_AL,
 		device:		PCI_DEVICE_ID_AL_M5229,
diff -Nru a/drivers/ide/pci/amd74xx.c b/drivers/ide/pci/amd74xx.c
--- a/drivers/ide/pci/amd74xx.c	Mon Oct  7 11:25:40 2002
+++ b/drivers/ide/pci/amd74xx.c	Mon Oct  7 11:25:40 2002
@@ -400,6 +400,7 @@
 	if (dev->device != d->device)
 		BUG();
 	ide_setup_pci_device(dev, d);
+	MOD_INC_USE_COUNT;
 	return 0;
 }
 
diff -Nru a/drivers/ide/pci/amd74xx.h b/drivers/ide/pci/amd74xx.h
--- a/drivers/ide/pci/amd74xx.h	Mon Oct  7 11:25:25 2002
+++ b/drivers/ide/pci/amd74xx.h	Mon Oct  7 11:25:25 2002
@@ -29,7 +29,7 @@
 static void init_hwif_amd74xx(ide_hwif_t *);
 static void init_dma_amd74xx(ide_hwif_t *, unsigned long);
 
-static ide_pci_device_t amd74xx_chipsets[] __initdata = {
+static ide_pci_device_t amd74xx_chipsets[] __devinitdata = {
 	{	/* 0 */
 		vendor:		PCI_VENDOR_ID_AMD,
 		device:		PCI_DEVICE_ID_AMD_COBRA_7401,
diff -Nru a/drivers/ide/pci/cmd64x.c b/drivers/ide/pci/cmd64x.c
--- a/drivers/ide/pci/cmd64x.c	Mon Oct  7 11:25:38 2002
+++ b/drivers/ide/pci/cmd64x.c	Mon Oct  7 11:25:38 2002
@@ -728,12 +728,9 @@
 	if (dev->device == PCI_DEVICE_ID_CMD_643)
 		hwif->ultra_mask = 0x80;
 	if (dev->device == PCI_DEVICE_ID_CMD_646)
-	{
-		if (class_rev > 0x04)
-			hwif->ultra_mask = 0x07;
-		else
-			hwif->ultra_mask = 0x80;
-	}
+		hwif->ultra_mask = (class_rev > 0x04) ? 0x07 : 0x80;
+	if (dev->device == PCI_DEVICE_ID_CMD_648)
+		hwif->ultra_mask = 0x1f;
 
 #ifdef CONFIG_BLK_DEV_IDEDMA
 	hwif->ide_dma_check = &cmd64x_config_drive_for_dma;
@@ -774,6 +771,7 @@
 	if (dev->device != d->device)
 		BUG();
 	ide_setup_pci_device(dev, d);
+	MOD_INC_USE_COUNT;
 	return 0;
 }
 
diff -Nru a/drivers/ide/pci/cmd64x.h b/drivers/ide/pci/cmd64x.h
--- a/drivers/ide/pci/cmd64x.h	Mon Oct  7 11:25:33 2002
+++ b/drivers/ide/pci/cmd64x.h	Mon Oct  7 11:25:33 2002
@@ -83,7 +83,7 @@
 static void init_hwif_cmd64x(ide_hwif_t *);
 static void init_dma_cmd64x(ide_hwif_t *, unsigned long);
 
-static ide_pci_device_t cmd64x_chipsets[] __initdata = {
+static ide_pci_device_t cmd64x_chipsets[] __devinitdata = {
 	{	/* 0 */
 		vendor:		PCI_VENDOR_ID_CMD,
 		device:		PCI_DEVICE_ID_CMD_643,
diff -Nru a/drivers/ide/pci/cs5530.c b/drivers/ide/pci/cs5530.c
--- a/drivers/ide/pci/cs5530.c	Mon Oct  7 11:25:30 2002
+++ b/drivers/ide/pci/cs5530.c	Mon Oct  7 11:25:30 2002
@@ -427,6 +427,7 @@
 	if (dev->device != d->device)
 		BUG();
 	ide_setup_pci_device(dev, d);
+	MOD_INC_USE_COUNT;
 	return 0;
 }
 
diff -Nru a/drivers/ide/pci/cs5530.h b/drivers/ide/pci/cs5530.h
--- a/drivers/ide/pci/cs5530.h	Mon Oct  7 11:25:25 2002
+++ b/drivers/ide/pci/cs5530.h	Mon Oct  7 11:25:25 2002
@@ -29,7 +29,7 @@
 static void init_hwif_cs5530(ide_hwif_t *);
 static void init_dma_cs5530(ide_hwif_t *, unsigned long);
 
-static ide_pci_device_t cs5530_chipsets[] __initdata = {
+static ide_pci_device_t cs5530_chipsets[] __devinitdata = {
 	{	/* 0 */
 		vendor:		PCI_VENDOR_ID_CYRIX,
 		device:		PCI_DEVICE_ID_CYRIX_5530_IDE,
diff -Nru a/drivers/ide/pci/cy82c693.c b/drivers/ide/pci/cy82c693.c
--- a/drivers/ide/pci/cy82c693.c	Mon Oct  7 11:25:26 2002
+++ b/drivers/ide/pci/cy82c693.c	Mon Oct  7 11:25:26 2002
@@ -438,6 +438,7 @@
 		dev2 = pci_find_slot(dev->bus->number, dev->devfn + 1);
 		ide_setup_pci_devices(dev, dev2, d);
 	}
+	MOD_INC_USE_COUNT;
 	return 0;
 }
 
diff -Nru a/drivers/ide/pci/cy82c693.h b/drivers/ide/pci/cy82c693.h
--- a/drivers/ide/pci/cy82c693.h	Mon Oct  7 11:25:29 2002
+++ b/drivers/ide/pci/cy82c693.h	Mon Oct  7 11:25:29 2002
@@ -68,7 +68,7 @@
 extern void init_hwif_cy82c693(ide_hwif_t *);
 extern void init_iops_cy82c693(ide_hwif_t *);
 
-static ide_pci_device_t cy82c693_chipsets[] __initdata = {
+static ide_pci_device_t cy82c693_chipsets[] __devinitdata = {
 	{	/* 0 */
 		vendor:		PCI_VENDOR_ID_CONTAQ,
 		device:		PCI_DEVICE_ID_CONTAQ_82C693,
diff -Nru a/drivers/ide/pci/generic.c b/drivers/ide/pci/generic.c
--- a/drivers/ide/pci/generic.c	Mon Oct  7 11:25:25 2002
+++ b/drivers/ide/pci/generic.c	Mon Oct  7 11:25:25 2002
@@ -65,26 +65,6 @@
 
 extern void ide_setup_pci_device(struct pci_dev *, ide_pci_device_t *);
 
-static void __init init_setup_generic (struct pci_dev *dev, ide_pci_device_t *d)
-{
-	if ((d->vendor == PCI_VENDOR_ID_UMC) &&
-	    (d->device == PCI_DEVICE_ID_UMC_UM8886A) &&
-	    (!(PCI_FUNC(dev->devfn) & 1)))
-		return; /* UM8886A/BF pair */
-
-	if ((d->vendor == PCI_VENDOR_ID_OPTI) &&
-	    (d->device == PCI_DEVICE_ID_OPTI_82C558) &&
-	    (!(PCI_FUNC(dev->devfn) & 1)))
-		return;
-
-	ide_setup_pci_device(dev, d);
-}
-
-static void __init init_setup_unknown (struct pci_dev *dev, ide_pci_device_t *d)
-{
-	ide_setup_pci_device(dev, d);
-}
-
 #if 0
 
 	/* Logic to add back later on */
@@ -114,7 +94,18 @@
 
 	if (dev->device != d->device)
 		BUG();
-	d->init_setup(dev, d);
+	if ((d->vendor == PCI_VENDOR_ID_UMC) &&
+	    (d->device == PCI_DEVICE_ID_UMC_UM8886A) &&
+	    (!(PCI_FUNC(dev->devfn) & 1)))
+		return 1; /* UM8886A/BF pair */
+
+	if ((d->vendor == PCI_VENDOR_ID_OPTI) &&
+	    (d->device == PCI_DEVICE_ID_OPTI_82C558) &&
+	    (!(PCI_FUNC(dev->devfn) & 1)))
+		return 1;
+
+	ide_setup_pci_device(dev, d);
+	MOD_INC_USE_COUNT;
 	return 0;
 }
 
diff -Nru a/drivers/ide/pci/generic.h b/drivers/ide/pci/generic.h
--- a/drivers/ide/pci/generic.h	Mon Oct  7 11:25:26 2002
+++ b/drivers/ide/pci/generic.h	Mon Oct  7 11:25:26 2002
@@ -5,17 +5,15 @@
 #include <linux/pci.h>
 #include <linux/ide.h>
 
-static void init_setup_generic(struct pci_dev *, ide_pci_device_t *);
 static unsigned int init_chipset_generic(struct pci_dev *, const char *);
 static void init_hwif_generic(ide_hwif_t *);
 static void init_dma_generic(ide_hwif_t *, unsigned long);
 
-static ide_pci_device_t generic_chipsets[] __initdata = {
+static ide_pci_device_t generic_chipsets[] __devinitdata = {
 	{	/* 0 */
 		vendor:		PCI_VENDOR_ID_NS,
 		device:		PCI_DEVICE_ID_NS_87410,
 		name:		"NS87410",
-		init_setup:	init_setup_generic,
 		init_chipset:	init_chipset_generic,
 		init_iops:	NULL,
 		init_hwif:	init_hwif_generic,
@@ -29,7 +27,6 @@
 		vendor:		PCI_VENDOR_ID_PCTECH,
 		device:		PCI_DEVICE_ID_PCTECH_SAMURAI_IDE,
 		name:		"SAMURAI",
-		init_setup:	init_setup_generic,
 		init_chipset:	init_chipset_generic,
 		init_iops:	NULL,
 		init_hwif:	init_hwif_generic,
@@ -43,7 +40,6 @@
 		vendor:		PCI_VENDOR_ID_HOLTEK,
 		device:		PCI_DEVICE_ID_HOLTEK_6565,
 		name:		"HT6565",
-		init_setup:	init_setup_generic,
 		init_chipset:	init_chipset_generic,
 		init_iops:	NULL,
 		init_hwif:	init_hwif_generic,
@@ -57,7 +53,6 @@
 		vendor:		PCI_VENDOR_ID_UMC,
 		device:		PCI_DEVICE_ID_UMC_UM8673F,
 		name:		"UM8673F",
-		init_setup:	init_setup_generic,
 		init_chipset:	init_chipset_generic,
 		init_iops:	NULL,
 		init_hwif:	init_hwif_generic,
@@ -71,7 +66,6 @@
 		vendor:		PCI_VENDOR_ID_UMC,
 		device:		PCI_DEVICE_ID_UMC_UM8886A,
 		name:		"UM8886A",
-		init_setup:	init_setup_generic,
 		init_chipset:	init_chipset_generic,
 		init_iops:	NULL,
 		init_hwif:	init_hwif_generic,
@@ -85,7 +79,6 @@
 		vendor:		PCI_VENDOR_ID_UMC,
 		device:		PCI_DEVICE_ID_UMC_UM8886BF,
 		name:		"UM8886BF",
-		init_setup:	init_setup_generic,
 		init_chipset:	init_chipset_generic,
 		init_iops:	NULL,
 		init_hwif:	init_hwif_generic,
@@ -99,7 +92,6 @@
 		vendor:		PCI_VENDOR_ID_HINT,
 		device:		PCI_DEVICE_ID_HINT_VXPROII_IDE,
 		name:		"HINT_IDE",
-		init_setup:	init_setup_generic,
 		init_chipset:	init_chipset_generic,
 		init_iops:	NULL,
 		init_hwif:	init_hwif_generic,
@@ -113,7 +105,6 @@
 		vendor:		PCI_VENDOR_ID_VIA,
 		device:		PCI_DEVICE_ID_VIA_82C561,
 		name:		"VIA_IDE",
-		init_setup:	init_setup_generic,
 		init_chipset:	init_chipset_generic,
 		init_iops:	NULL,
 		init_hwif:	init_hwif_generic,
@@ -127,7 +118,6 @@
 		vendor:		PCI_VENDOR_ID_OPTI,
 		device:		PCI_DEVICE_ID_OPTI_82C558,
 		name:		"OPTI621V",
-		init_setup:	init_setup_generic,
 		init_chipset:	init_chipset_generic,
 		init_iops:	NULL,
 		init_hwif:	init_hwif_generic,
@@ -145,12 +135,11 @@
 	}
 };
 
-static ide_pci_device_t unknown_chipset[] __initdata = {
+static ide_pci_device_t unknown_chipset[] __devinitdata = {
 	{	/* 0 */
 		vendor:		0,
 		device:		0,
 		name:		"PCI_IDE",
-		init_setup:	init_setup_generic,
 		init_chipset:	init_chipset_generic,
 		init_iops:	NULL,
 		init_hwif:	init_hwif_generic,
diff -Nru a/drivers/ide/pci/hpt34x.c b/drivers/ide/pci/hpt34x.c
--- a/drivers/ide/pci/hpt34x.c	Mon Oct  7 11:25:32 2002
+++ b/drivers/ide/pci/hpt34x.c	Mon Oct  7 11:25:32 2002
@@ -334,6 +334,7 @@
 	d->bootable = (pcicmd & PCI_COMMAND_MEMORY) ? OFF_BOARD : NEVER_BOARD;
 
 	ide_setup_pci_device(dev, d);
+	MOD_INC_USE_COUNT;
 	return 0;
 }
 
diff -Nru a/drivers/ide/pci/hpt34x.h b/drivers/ide/pci/hpt34x.h
--- a/drivers/ide/pci/hpt34x.h	Mon Oct  7 11:25:25 2002
+++ b/drivers/ide/pci/hpt34x.h	Mon Oct  7 11:25:25 2002
@@ -35,7 +35,7 @@
 static void init_hwif_hpt34x(ide_hwif_t *);
 static void init_dma_hpt34x(ide_hwif_t *, unsigned long);
 
-static ide_pci_device_t hpt34x_chipsets[] __initdata = {
+static ide_pci_device_t hpt34x_chipsets[] __devinitdata = {
 	{	/* 0 */
 		vendor:		PCI_VENDOR_ID_TTI,
 		device:		PCI_DEVICE_ID_TTI_HPT343,
diff -Nru a/drivers/ide/pci/hpt366.c b/drivers/ide/pci/hpt366.c
--- a/drivers/ide/pci/hpt366.c	Mon Oct  7 11:25:35 2002
+++ b/drivers/ide/pci/hpt366.c	Mon Oct  7 11:25:35 2002
@@ -1,5 +1,5 @@
 /*
- * linux/drivers/ide/hpt366.c		Version 0.33	April 17, 2002
+ * linux/drivers/ide/hpt366.c		Version 0.34	Sept 17, 2002
  *
  * Copyright (C) 1999-2002		Andre Hedrick <andre@linux-ide.org>
  * Portions Copyright (C) 2001	        Sun Microsystems, Inc.
@@ -1185,6 +1185,7 @@
 	if (dev->device != d->device)
 		BUG();
 	d->init_setup(dev, d);
+	MOD_INC_USE_COUNT;
 	return 0;
 }
 
diff -Nru a/drivers/ide/pci/hpt366.h b/drivers/ide/pci/hpt366.h
--- a/drivers/ide/pci/hpt366.h	Mon Oct  7 11:25:26 2002
+++ b/drivers/ide/pci/hpt366.h	Mon Oct  7 11:25:26 2002
@@ -441,7 +441,7 @@
 static void init_hwif_hpt366(ide_hwif_t *);
 static void init_dma_hpt366(ide_hwif_t *, unsigned long);
 
-static ide_pci_device_t hpt366_chipsets[] __initdata = {
+static ide_pci_device_t hpt366_chipsets[] __devinitdata = {
 	{	/* 0 */
 		vendor:		PCI_VENDOR_ID_TTI,
 		device:		PCI_DEVICE_ID_TTI_HPT366,
diff -Nru a/drivers/ide/pci/it8172.c b/drivers/ide/pci/it8172.c
--- a/drivers/ide/pci/it8172.c	Mon Oct  7 11:25:27 2002
+++ b/drivers/ide/pci/it8172.c	Mon Oct  7 11:25:27 2002
@@ -303,8 +303,9 @@
 	ide_pci_device_t *d = &it8172_chipsets[id->driver_data];
         if ((!(PCI_FUNC(dev->devfn) & 1) ||
             (!((dev->class >> 8) == PCI_CLASS_STORAGE_IDE))))
-                return 0; /* IT8172 is more than only a IDE controller */
+                return 1; /* IT8172 is more than only a IDE controller */
 	ide_setup_pci_device(dev, d);
+	MOD_INC_USE_COUNT;
 	return 0;
 }
 
diff -Nru a/drivers/ide/pci/it8172.h b/drivers/ide/pci/it8172.h
--- a/drivers/ide/pci/it8172.h	Mon Oct  7 11:25:32 2002
+++ b/drivers/ide/pci/it8172.h	Mon Oct  7 11:25:32 2002
@@ -19,7 +19,7 @@
 static void init_hwif_it8172(ide_hwif_t *);
 static void init_dma_it8172(ide_hwif_t *, unsigned long);
 
-static ide_pci_device_t it8172_chipsets[] __initdata = {
+static ide_pci_device_t it8172_chipsets[] __devinitdata = {
 	{	/* 0 */
 		vendor:		PCI_VENDOR_ID_ITE,
 		device:		PCI_DEVICE_ID_ITE_IT8172G,
diff -Nru a/drivers/ide/pci/ns87415.c b/drivers/ide/pci/ns87415.c
--- a/drivers/ide/pci/ns87415.c	Mon Oct  7 11:25:35 2002
+++ b/drivers/ide/pci/ns87415.c	Mon Oct  7 11:25:35 2002
@@ -236,6 +236,7 @@
 	if (dev->device != d->device)
 		BUG();
 	ide_setup_pci_device(dev, d);
+	MOD_INC_USE_COUNT;
 	return 0;
 }
 
diff -Nru a/drivers/ide/pci/ns87415.h b/drivers/ide/pci/ns87415.h
--- a/drivers/ide/pci/ns87415.h	Mon Oct  7 11:25:25 2002
+++ b/drivers/ide/pci/ns87415.h	Mon Oct  7 11:25:25 2002
@@ -8,7 +8,7 @@
 static void init_hwif_ns87415(ide_hwif_t *);
 static void init_dma_ns87415(ide_hwif_t *, unsigned long);
 
-static ide_pci_device_t ns87415_chipsets[] __initdata = {
+static ide_pci_device_t ns87415_chipsets[] __devinitdata = {
 	{	/* 0 */
 		vendor:		PCI_VENDOR_ID_NS,
 		device:		PCI_DEVICE_ID_NS_87415,
diff -Nru a/drivers/ide/pci/nvidia.c b/drivers/ide/pci/nvidia.c
--- a/drivers/ide/pci/nvidia.c	Mon Oct  7 11:25:39 2002
+++ b/drivers/ide/pci/nvidia.c	Mon Oct  7 11:25:39 2002
@@ -343,6 +343,7 @@
 	if (dev->device != d->device)
 		BUG();
 	ide_setup_pci_device(dev, d);
+	MOD_INC_USE_COUNT;
 	return 0;
 }
 
diff -Nru a/drivers/ide/pci/nvidia.h b/drivers/ide/pci/nvidia.h
--- a/drivers/ide/pci/nvidia.h	Mon Oct  7 11:25:35 2002
+++ b/drivers/ide/pci/nvidia.h	Mon Oct  7 11:25:35 2002
@@ -29,7 +29,7 @@
 static void init_hwif_nforce(ide_hwif_t *);
 static void init_dma_nforce(ide_hwif_t *, unsigned long);
 
-static ide_pci_device_t nvidia_chipsets[] __initdata = {
+static ide_pci_device_t nvidia_chipsets[] __devinitdata = {
 	{
 		vendor:		PCI_VENDOR_ID_NVIDIA,
 		device:		PCI_DEVICE_ID_NVIDIA_NFORCE_IDE,
diff -Nru a/drivers/ide/pci/opti621.c b/drivers/ide/pci/opti621.c
--- a/drivers/ide/pci/opti621.c	Mon Oct  7 11:25:32 2002
+++ b/drivers/ide/pci/opti621.c	Mon Oct  7 11:25:32 2002
@@ -370,6 +370,7 @@
 	if (dev->device != d->device)
 		BUG();
 	ide_setup_pci_device(dev, d);
+	MOD_INC_USE_COUNT;
 	return 0;
 }
 
diff -Nru a/drivers/ide/pci/opti621.h b/drivers/ide/pci/opti621.h
--- a/drivers/ide/pci/opti621.h	Mon Oct  7 11:25:38 2002
+++ b/drivers/ide/pci/opti621.h	Mon Oct  7 11:25:38 2002
@@ -9,7 +9,7 @@
 static void init_hwif_opti621(ide_hwif_t *);
 static void init_dma_opti621(ide_hwif_t *, unsigned long);
 
-static ide_pci_device_t opti621_chipsets[] __initdata = {
+static ide_pci_device_t opti621_chipsets[] __devinitdata = {
 	{	/* 0 */
 		vendor:		PCI_VENDOR_ID_OPTI,
 		device:		PCI_DEVICE_ID_OPTI_82C621,
diff -Nru a/drivers/ide/pci/pdc202xx_new.c b/drivers/ide/pci/pdc202xx_new.c
--- a/drivers/ide/pci/pdc202xx_new.c	Mon Oct  7 11:25:36 2002
+++ b/drivers/ide/pci/pdc202xx_new.c	Mon Oct  7 11:25:36 2002
@@ -663,6 +663,7 @@
 	if (dev->device != d->device)
 		BUG();
 	d->init_setup(dev, d);
+	MOD_INC_USE_COUNT;
 	return 0;
 }
 
diff -Nru a/drivers/ide/pci/pdc202xx_new.h b/drivers/ide/pci/pdc202xx_new.h
--- a/drivers/ide/pci/pdc202xx_new.h	Mon Oct  7 11:25:25 2002
+++ b/drivers/ide/pci/pdc202xx_new.h	Mon Oct  7 11:25:25 2002
@@ -225,7 +225,7 @@
 static void init_hwif_pdc202new(ide_hwif_t *);
 static void init_dma_pdc202new(ide_hwif_t *, unsigned long);
 
-static ide_pci_device_t pdcnew_chipsets[] __initdata = {
+static ide_pci_device_t pdcnew_chipsets[] __devinitdata = {
 	{	/* 0 */
 		vendor:		PCI_VENDOR_ID_PROMISE,
 		device:		PCI_DEVICE_ID_PROMISE_20268,
diff -Nru a/drivers/ide/pci/pdc202xx_old.c b/drivers/ide/pci/pdc202xx_old.c
--- a/drivers/ide/pci/pdc202xx_old.c	Mon Oct  7 11:25:39 2002
+++ b/drivers/ide/pci/pdc202xx_old.c	Mon Oct  7 11:25:39 2002
@@ -397,6 +397,9 @@
 				cable ? "40" : "80", cable);
 #endif /* PDC202_DEBUG_CABLE */
 			break;
+		case PCI_DEVICE_ID_PROMISE_20246:
+			ultra_66 = 0;
+			break;
 		default:
 			BUG();
 	}
@@ -544,16 +547,18 @@
 	if (drive->addressing == 1) {
 		struct request *rq	= HWGROUP(drive)->rq;
 		ide_hwif_t *hwif	= HWIF(drive);
-		struct pci_dev *dev	= hwif->pci_dev;
-		u32 high_16	= pci_resource_start(dev, 4);
+//		struct pci_dev *dev	= hwif->pci_dev;
+//		u32 high_16	= pci_resource_start(dev, 4);
+		u32 high_16	= hwif->dma_master;
 		u32 atapi_reg	= high_16 + (hwif->channel ? 0x24 : 0x20);
 		u32 word_count	= 0;
 		u8 clock = hwif->INB(high_16 + 0x11);
 
 		hwif->OUTB(clock|(hwif->channel ? 0x08 : 0x02), high_16+0x11);
 		word_count = (rq->nr_sectors << 8);
-		word_count = (rq->cmd == READ) ? word_count | 0x05000000 :
-						 word_count | 0x06000000;
+		word_count = (rq_data_dir(rq) == READ) ?
+					word_count | 0x05000000 :
+					word_count | 0x06000000;
 		hwif->OUTL(word_count, atapi_reg);
 	}
 	return __ide_dma_begin(drive);
@@ -563,7 +568,8 @@
 {
 	if (drive->addressing == 1) {
 		ide_hwif_t *hwif	= HWIF(drive);
-		u32 high_16	= pci_resource_start(hwif->pci_dev, 4);
+//		u32 high_16	= pci_resource_start(hwif->pci_dev, 4);
+		u32 high_16	= hwif->dma_master;
 		u32 atapi_reg	= high_16 + (hwif->channel ? 0x24 : 0x20);
 		u8 clock	= 0;
 
@@ -577,8 +583,9 @@
 static int pdc202xx_old_ide_dma_test_irq(ide_drive_t *drive)
 {
 	ide_hwif_t *hwif	= HWIF(drive);
-	struct pci_dev *dev	= hwif->pci_dev;
-	unsigned long high_16	= pci_resource_start(dev, 4);
+//	struct pci_dev *dev	= hwif->pci_dev;
+//	unsigned long high_16	= pci_resource_start(dev, 4);
+	u32 high_16		= hwif->dma_master;
 	u8 dma_stat		= hwif->INB(hwif->dma_status);
 	u8 sc1d			= hwif->INB((high_16 + 0x001d));
 
@@ -935,6 +942,7 @@
 	if (dev->device != d->device)
 		BUG();
 	d->init_setup(dev, d);
+	MOD_INC_USE_COUNT;
 	return 0;
 }
 
diff -Nru a/drivers/ide/pci/pdc202xx_old.h b/drivers/ide/pci/pdc202xx_old.h
--- a/drivers/ide/pci/pdc202xx_old.h	Mon Oct  7 11:25:25 2002
+++ b/drivers/ide/pci/pdc202xx_old.h	Mon Oct  7 11:25:25 2002
@@ -225,7 +225,7 @@
 static void init_hwif_pdc202xx(ide_hwif_t *);
 static void init_dma_pdc202xx(ide_hwif_t *, unsigned long);
 
-static ide_pci_device_t pdc202xx_chipsets[] __initdata = {
+static ide_pci_device_t pdc202xx_chipsets[] __devinitdata = {
 	{	/* 0 */
 		vendor:		PCI_VENDOR_ID_PROMISE,
 		device:		PCI_DEVICE_ID_PROMISE_20246,
diff -Nru a/drivers/ide/pci/pdcadma.c b/drivers/ide/pci/pdcadma.c
--- a/drivers/ide/pci/pdcadma.c	Mon Oct  7 11:25:36 2002
+++ b/drivers/ide/pci/pdcadma.c	Mon Oct  7 11:25:36 2002
@@ -133,6 +133,7 @@
 	if (dev->device != d->device)
 		BUG();
 	ide_setup_pci_device(dev, d);
+	MOD_INC_USE_COUNT;
 	return 1;
 }
 
diff -Nru a/drivers/ide/pci/pdcadma.h b/drivers/ide/pci/pdcadma.h
--- a/drivers/ide/pci/pdcadma.h	Mon Oct  7 11:25:25 2002
+++ b/drivers/ide/pci/pdcadma.h	Mon Oct  7 11:25:25 2002
@@ -30,7 +30,7 @@
 static void init_hwif_pdcadma(ide_hwif_t *);
 static void init_dma_pdcadma(ide_hwif_t *, unsigned long);
 
-static ide_pci_device_t pdcadma_chipsets[] __initdata = {
+static ide_pci_device_t pdcadma_chipsets[] __devinitdata = {
 	{	/* 0 */
 		vendor:		PCI_VENDOR_ID_PDC,
 		device:		PCI_DEVICE_ID_PDC_1841,
diff -Nru a/drivers/ide/pci/piix.c b/drivers/ide/pci/piix.c
--- a/drivers/ide/pci/piix.c	Mon Oct  7 11:25:25 2002
+++ b/drivers/ide/pci/piix.c	Mon Oct  7 11:25:25 2002
@@ -529,7 +529,7 @@
  *	out to be nice and simple
  */
  
-static unsigned int __init init_chipset_piix (struct pci_dev *dev, const char *name)
+static unsigned int __devinit init_chipset_piix (struct pci_dev *dev, const char *name)
 {
         switch(dev->device) {
 		case PCI_DEVICE_ID_INTEL_82801AA_1:
@@ -680,6 +680,7 @@
 	if (dev->device != d->device)
 		BUG();
 	d->init_setup(dev, d);
+	MOD_INC_USE_COUNT;
 	return 0;
 }
 
diff -Nru a/drivers/ide/pci/piix.h b/drivers/ide/pci/piix.h
--- a/drivers/ide/pci/piix.h	Mon Oct  7 11:25:26 2002
+++ b/drivers/ide/pci/piix.h	Mon Oct  7 11:25:26 2002
@@ -17,7 +17,7 @@
 
 static int piix_get_info(char *, char **, off_t, int);
 
-static ide_pci_host_proc_t piix_procs[] __initdata = {
+static ide_pci_host_proc_t piix_procs[] __devinitdata = {
 	{
 		name:		"piix",
 		set:		1,
@@ -28,7 +28,7 @@
 #endif  /* defined(DISPLAY_PIIX_TIMINGS) && defined(CONFIG_PROC_FS) */
 
 static void init_setup_piix(struct pci_dev *, ide_pci_device_t *);
-static unsigned int __init init_chipset_piix(struct pci_dev *, const char *);
+static unsigned int __devinit init_chipset_piix(struct pci_dev *, const char *);
 static void init_hwif_piix(ide_hwif_t *);
 static void init_dma_piix(ide_hwif_t *, unsigned long);
 
@@ -38,7 +38,7 @@
  *
  */
  
-static ide_pci_device_t piix_pci_info[] __initdata = {
+static ide_pci_device_t piix_pci_info[] __devinitdata = {
 	{	/* 0 */
 		vendor:		PCI_VENDOR_ID_INTEL,
 		device:		PCI_DEVICE_ID_INTEL_82371FB_0,
diff -Nru a/drivers/ide/pci/rz1000.c b/drivers/ide/pci/rz1000.c
--- a/drivers/ide/pci/rz1000.c	Mon Oct  7 11:25:31 2002
+++ b/drivers/ide/pci/rz1000.c	Mon Oct  7 11:25:31 2002
@@ -62,6 +62,7 @@
 	if (dev->device != d->device)
 		BUG();
 	ide_setup_pci_device(dev, d);
+	MOD_INC_USE_COUNT;
 	return 0;
 }
 
diff -Nru a/drivers/ide/pci/rz1000.h b/drivers/ide/pci/rz1000.h
--- a/drivers/ide/pci/rz1000.h	Mon Oct  7 11:25:29 2002
+++ b/drivers/ide/pci/rz1000.h	Mon Oct  7 11:25:29 2002
@@ -7,7 +7,7 @@
 
 static void init_hwif_rz1000(ide_hwif_t *);
 
-static ide_pci_device_t rz1000_chipsets[] __initdata = {
+static ide_pci_device_t rz1000_chipsets[] __devinitdata = {
 {
 		vendor:		PCI_VENDOR_ID_PCTECH,
 		device:		PCI_DEVICE_ID_PCTECH_RZ1000,
diff -Nru a/drivers/ide/pci/serverworks.c b/drivers/ide/pci/serverworks.c
--- a/drivers/ide/pci/serverworks.c	Mon Oct  7 11:25:25 2002
+++ b/drivers/ide/pci/serverworks.c	Mon Oct  7 11:25:25 2002
@@ -784,6 +784,7 @@
 	if (dev->device != d->device)
 		BUG();
 	d->init_setup(dev, d);
+	MOD_INC_USE_COUNT;
 	return 0;
 }
 
diff -Nru a/drivers/ide/pci/serverworks.h b/drivers/ide/pci/serverworks.h
--- a/drivers/ide/pci/serverworks.h	Mon Oct  7 11:25:31 2002
+++ b/drivers/ide/pci/serverworks.h	Mon Oct  7 11:25:31 2002
@@ -37,7 +37,7 @@
 static void init_hwif_svwks(ide_hwif_t *);
 static void init_dma_svwks(ide_hwif_t *, unsigned long);
 
-static ide_pci_device_t serverworks_chipsets[] __initdata = {
+static ide_pci_device_t serverworks_chipsets[] __devinitdata = {
 	{	/* 0 */
 		vendor:		PCI_VENDOR_ID_SERVERWORKS,
 		device:		PCI_DEVICE_ID_SERVERWORKS_OSB4IDE,
diff -Nru a/drivers/ide/pci/siimage.c b/drivers/ide/pci/siimage.c
--- a/drivers/ide/pci/siimage.c	Mon Oct  7 11:25:31 2002
+++ b/drivers/ide/pci/siimage.c	Mon Oct  7 11:25:31 2002
@@ -844,6 +844,7 @@
 	if (dev->device != d->device)
 		BUG();
 	ide_setup_pci_device(dev, d);
+	MOD_INC_USE_COUNT;
 	return 0;
 }
 
diff -Nru a/drivers/ide/pci/siimage.h b/drivers/ide/pci/siimage.h
--- a/drivers/ide/pci/siimage.h	Mon Oct  7 11:25:35 2002
+++ b/drivers/ide/pci/siimage.h	Mon Oct  7 11:25:35 2002
@@ -112,7 +112,7 @@
 static void init_hwif_siimage(ide_hwif_t *);
 static void init_dma_siimage(ide_hwif_t *, unsigned long);
 
-static ide_pci_device_t siimage_chipsets[] __initdata = {
+static ide_pci_device_t siimage_chipsets[] __devinitdata = {
 	{	/* 0 */
 		vendor:		PCI_VENDOR_ID_CMD,
 		device:		PCI_DEVICE_ID_SII_680,
diff -Nru a/drivers/ide/pci/sis5513.c b/drivers/ide/pci/sis5513.c
--- a/drivers/ide/pci/sis5513.c	Mon Oct  7 11:25:35 2002
+++ b/drivers/ide/pci/sis5513.c	Mon Oct  7 11:25:35 2002
@@ -1030,6 +1030,7 @@
 	if (dev->device != d->device)
 		BUG();
 	ide_setup_pci_device(dev, d);
+	MOD_INC_USE_COUNT;
 	return 0;
 }
 
diff -Nru a/drivers/ide/pci/sis5513.h b/drivers/ide/pci/sis5513.h
--- a/drivers/ide/pci/sis5513.h	Mon Oct  7 11:25:36 2002
+++ b/drivers/ide/pci/sis5513.h	Mon Oct  7 11:25:36 2002
@@ -29,7 +29,7 @@
 static void init_hwif_sis5513(ide_hwif_t *);
 static void init_dma_sis5513(ide_hwif_t *, unsigned long);
 
-static ide_pci_device_t sis5513_chipsets[] __initdata = {
+static ide_pci_device_t sis5513_chipsets[] __devinitdata = {
 	{	/* 0 */
 		vendor:		PCI_VENDOR_ID_SI,
 		device:		PCI_DEVICE_ID_SI_5513,
diff -Nru a/drivers/ide/pci/sl82c105.c b/drivers/ide/pci/sl82c105.c
--- a/drivers/ide/pci/sl82c105.c	Mon Oct  7 11:25:29 2002
+++ b/drivers/ide/pci/sl82c105.c	Mon Oct  7 11:25:29 2002
@@ -288,6 +288,7 @@
 	if (dev->device != d->device)
 		BUG();
 	ide_setup_pci_device(dev, d);
+	MOD_INC_USE_COUNT;
 	return 0;
 }
 
diff -Nru a/drivers/ide/pci/sl82c105.h b/drivers/ide/pci/sl82c105.h
--- a/drivers/ide/pci/sl82c105.h	Mon Oct  7 11:25:26 2002
+++ b/drivers/ide/pci/sl82c105.h	Mon Oct  7 11:25:26 2002
@@ -9,7 +9,7 @@
 static void init_hwif_sl82c105(ide_hwif_t *);
 static void init_dma_sl82c105(ide_hwif_t *, unsigned long);
 
-static ide_pci_device_t sl82c105_chipsets[] __initdata = {
+static ide_pci_device_t sl82c105_chipsets[] __devinitdata = {
 	{	/* 0 */
 		vendor:		PCI_VENDOR_ID_WINBOND,
 		device:		PCI_DEVICE_ID_WINBOND_82C105,
diff -Nru a/drivers/ide/pci/slc90e66.c b/drivers/ide/pci/slc90e66.c
--- a/drivers/ide/pci/slc90e66.c	Mon Oct  7 11:25:36 2002
+++ b/drivers/ide/pci/slc90e66.c	Mon Oct  7 11:25:36 2002
@@ -372,6 +372,7 @@
 	if (dev->device != d->device)
 		BUG();
 	ide_setup_pci_device(dev, d);
+	MOD_INC_USE_COUNT;
 	return 0;
 }
 
diff -Nru a/drivers/ide/pci/slc90e66.h b/drivers/ide/pci/slc90e66.h
--- a/drivers/ide/pci/slc90e66.h	Mon Oct  7 11:25:28 2002
+++ b/drivers/ide/pci/slc90e66.h	Mon Oct  7 11:25:28 2002
@@ -31,7 +31,7 @@
 static void init_hwif_slc90e66(ide_hwif_t *);
 static void init_dma_slc90e66(ide_hwif_t *, unsigned long);
 
-static ide_pci_device_t slc90e66_chipsets[] __initdata = {
+static ide_pci_device_t slc90e66_chipsets[] __devinitdata = {
 	{	/* 0 */
 		vendor:		PCI_VENDOR_ID_EFAR,
 		device:		PCI_DEVICE_ID_EFAR_SLC90E66_1,
diff -Nru a/drivers/ide/pci/trm290.c b/drivers/ide/pci/trm290.c
--- a/drivers/ide/pci/trm290.c	Mon Oct  7 11:25:27 2002
+++ b/drivers/ide/pci/trm290.c	Mon Oct  7 11:25:27 2002
@@ -403,6 +403,7 @@
 	if (dev->device != d->device)
 		BUG();
 	ide_setup_pci_device(dev, d);
+	MOD_INC_USE_COUNT;
 	return 0;
 }
 
diff -Nru a/drivers/ide/pci/trm290.h b/drivers/ide/pci/trm290.h
--- a/drivers/ide/pci/trm290.h	Mon Oct  7 11:25:35 2002
+++ b/drivers/ide/pci/trm290.h	Mon Oct  7 11:25:35 2002
@@ -7,7 +7,7 @@
 
 extern void init_hwif_trm290(ide_hwif_t *);
 
-static ide_pci_device_t trm290_chipsets[] __initdata = {
+static ide_pci_device_t trm290_chipsets[] __devinitdata = {
 	{	/* 0 */
 		vendor:		PCI_VENDOR_ID_TEKRAM,
 		device:		PCI_DEVICE_ID_TEKRAM_DC290,
diff -Nru a/drivers/ide/pci/via82cxxx.c b/drivers/ide/pci/via82cxxx.c
--- a/drivers/ide/pci/via82cxxx.c	Mon Oct  7 11:25:25 2002
+++ b/drivers/ide/pci/via82cxxx.c	Mon Oct  7 11:25:25 2002
@@ -641,6 +641,7 @@
 	if (dev->device != d->device)
 		BUG();
 	ide_setup_pci_device(dev, d);
+	MOD_INC_USE_COUNT;
 	return 0;
 }
 
diff -Nru a/drivers/ide/pci/via82cxxx.h b/drivers/ide/pci/via82cxxx.h
--- a/drivers/ide/pci/via82cxxx.h	Mon Oct  7 11:25:36 2002
+++ b/drivers/ide/pci/via82cxxx.h	Mon Oct  7 11:25:36 2002
@@ -29,7 +29,7 @@
 static void init_hwif_via82cxxx(ide_hwif_t *);
 static void init_dma_via82cxxx(ide_hwif_t *, unsigned long);
 
-static ide_pci_device_t via82cxxx_chipsets[] __initdata = {
+static ide_pci_device_t via82cxxx_chipsets[] __devinitdata = {
 	{	/* 0 */
 		vendor:		PCI_VENDOR_ID_VIA,
 		device:		PCI_DEVICE_ID_VIA_82C576_1,
diff -Nru a/drivers/ide/ppc/Makefile b/drivers/ide/ppc/Makefile
--- a/drivers/ide/ppc/Makefile	Mon Oct  7 11:25:36 2002
+++ b/drivers/ide/ppc/Makefile	Mon Oct  7 11:25:36 2002
@@ -3,6 +3,6 @@
 obj-$(CONFIG_BLK_DEV_IDE_PMAC)		+= pmac.o
 obj-$(CONFIG_BLK_DEV_IDE_SWARM)		+= swarm.o
 
-EXTRA_CFLAGS	:= -I../
+EXTRA_CFLAGS	:= -Idrivers/ide
 
 include $(TOPDIR)/Rules.make
diff -Nru a/drivers/ieee1394/amdtp.c b/drivers/ieee1394/amdtp.c
--- a/drivers/ieee1394/amdtp.c	Mon Oct  7 11:25:36 2002
+++ b/drivers/ieee1394/amdtp.c	Mon Oct  7 11:25:36 2002
@@ -196,7 +196,7 @@
 /* This implements a circular buffer for incoming samples. */
 
 struct buffer {
-	int head, tail, length, size;
+	size_t head, tail, length, size;
 	unsigned char data[0];
 };
 
@@ -623,9 +623,9 @@
 }
 
 static unsigned char *buffer_put_bytes(struct buffer *buffer,
-				       int max, int *actual)
+				       size_t max, size_t *actual)
 {
-	int length;
+	size_t length;
 	unsigned char *p;
 
 	p = &buffer->data[buffer->tail];
@@ -834,7 +834,7 @@
 
 	max_packet_size = max_nevents * s->dimension * 4 + 8;
 	s->packet_pool = pci_pool_create("packet pool", s->host->ohci->dev,
-					 max_packet_size, 0, 0, SLAB_KERNEL);
+					 max_packet_size, 0, 0);
 	if (s->packet_pool == NULL)
 		return -1;
 
@@ -1020,7 +1020,7 @@
 
 	s->descriptor_pool = pci_pool_create("descriptor pool", host->ohci->dev,
 					     sizeof(struct descriptor_block),
-					     16, 0, SLAB_KERNEL);
+					     16, 0);
 	if (s->descriptor_pool == NULL) {
 		kfree(s->input);
 		kfree(s);
@@ -1089,7 +1089,8 @@
 {
 	struct stream *s = file->private_data;
 	unsigned char *p;
-	int i, length;
+	int i;
+	size_t length;
 	
 	if (s->packet_pool == NULL)
 		return -EBADFD;
diff -Nru a/drivers/ieee1394/dv1394-private.h b/drivers/ieee1394/dv1394-private.h
--- a/drivers/ieee1394/dv1394-private.h	Mon Oct  7 11:25:36 2002
+++ b/drivers/ieee1394/dv1394-private.h	Mon Oct  7 11:25:36 2002
@@ -301,7 +301,8 @@
 	unsigned long data; 
 
 	/* Max # of packets per frame */
-        #define MAX_PACKETS 320
+	/* 320 is enough for NTSC, need to check what PAL is */
+        #define MAX_PACKETS 500
 
 
 	/* a PAGE_SIZE memory pool for allocating CIP headers
diff -Nru a/drivers/ieee1394/dv1394.c b/drivers/ieee1394/dv1394.c
--- a/drivers/ieee1394/dv1394.c	Mon Oct  7 11:25:36 2002
+++ b/drivers/ieee1394/dv1394.c	Mon Oct  7 11:25:36 2002
@@ -170,6 +170,15 @@
 
 static struct hpsb_highlevel *hl_handle; /* = NULL; */
 
+static LIST_HEAD(dv1394_devfs);
+struct dv1394_devfs_entry {
+	struct list_head list;
+    devfs_handle_t devfs;
+	char name[32];
+	struct dv1394_devfs_entry *parent;
+};
+static spinlock_t dv1394_devfs_lock = SPIN_LOCK_UNLOCKED;
+
 /* translate from a struct file* to the corresponding struct video_card* */
 
 static inline struct video_card* file_to_video_card(struct file *file)
@@ -2130,7 +2139,7 @@
 			const char *buffer, unsigned long count, void *data)
 {
 	int len = 0;
-	char new_value[64];
+	char new_value[65];
 	char *pos;
 	struct video_card *video = (struct video_card*) data;
 	
@@ -2142,11 +2151,12 @@
 	if (copy_from_user( new_value, buffer, len))
 		return -EFAULT;
 	
+	new_value[len] = 0;
 	pos = strchr(new_value, '=');
 	if (pos != NULL) {
 		int val_len = len - (pos-new_value) - 1;
-		char buf[64];
-		memset(buf, 0, 64);
+		char buf[65];
+		memset(buf, 0, 65);
 		strncpy(buf, pos+1, val_len);
 		if (buf[val_len-1] == '\n') buf[val_len-1] = 0;
 		
@@ -2555,17 +2565,6 @@
 
 /*** DEVFS HELPERS *********************************************************/
 
-#ifdef CONFIG_DEVFS_FS
-
-static LIST_HEAD(dv1394_devfs);
-struct dv1394_devfs_entry {
-	struct list_head list;
-    devfs_handle_t devfs;
-	char name[32];
-	struct dv1394_devfs_entry *parent;
-};
-static spinlock_t dv1394_devfs_lock = SPIN_LOCK_UNLOCKED;
-
 struct dv1394_devfs_entry *
 dv1394_devfs_find( char *name)
 {
@@ -2695,7 +2694,6 @@
 		kfree(p);
 	}
 }
-#endif /* CONFIG_DEVFS */
 
 /*** IEEE1394 HPSB CALLBACKS ***********************************************/
 
@@ -2854,6 +2852,7 @@
 {
 	struct ti_ohci *ohci;
 	char buf[16];
+	struct dv1394_devfs_entry *devfs_entry;
 
 	/* We only work with the OHCI-1394 driver */
 	if (strcmp(host->driver->name, OHCI1394_DRIVER_NAME))
@@ -2875,8 +2874,6 @@
 #endif
 
 #ifdef CONFIG_DEVFS_FS
-{
-	struct dv1394_devfs_entry *devfs_entry;
 	devfs_entry = dv1394_devfs_find("dv");
 	if (devfs_entry != NULL) {
 		snprintf(buf, sizeof(buf), "host%d", ohci->id);
@@ -2884,7 +2881,6 @@
 		dv1394_devfs_add_dir("NTSC", devfs_entry, NULL);
 		dv1394_devfs_add_dir("PAL", devfs_entry, NULL);
 	}
-}
 #endif
 	
 	dv1394_init(ohci, DV1394_NTSC, MODE_RECEIVE);
diff -Nru a/drivers/ieee1394/eth1394.h b/drivers/ieee1394/eth1394.h
--- a/drivers/ieee1394/eth1394.h	Mon Oct  7 11:25:29 2002
+++ b/drivers/ieee1394/eth1394.h	Mon Oct  7 11:25:29 2002
@@ -51,7 +51,7 @@
 	struct net_device *dev;
 };
 
-/* This is our task struct. It's used for the complete_tq callback.  */
+/* This is our task struct. It's used for the packet complete callback.  */
 struct packet_task {
 	struct sk_buff *skb;	/* Socket buffer we are sending */
 	nodeid_t dest_node;	/* Destination of the packet */
diff -Nru a/drivers/ieee1394/ieee1394_core.c b/drivers/ieee1394/ieee1394_core.c
--- a/drivers/ieee1394/ieee1394_core.c	Mon Oct  7 11:25:27 2002
+++ b/drivers/ieee1394/ieee1394_core.c	Mon Oct  7 11:25:27 2002
@@ -19,6 +19,7 @@
 #include <linux/interrupt.h>
 #include <linux/module.h>
 #include <linux/proc_fs.h>
+#include <linux/tqueue.h>
 #include <asm/bitops.h>
 #include <asm/byteorder.h>
 #include <asm/semaphore.h>
@@ -66,6 +67,29 @@
         printk("\n");
 }
 
+static void process_complete_tasks(struct hpsb_packet *packet)
+{
+	struct list_head *lh, *next;
+
+	list_for_each_safe(lh, next, &packet->complete_tq) {
+		struct tq_struct *tq = list_entry(lh, struct tq_struct, list);
+		list_del(&tq->list);
+		schedule_task(tq);
+	}
+
+	return;
+}
+
+/**
+ * hpsb_add_packet_complete_task - add a new task for when a packet completes
+ * @packet: the packet whose completion we want the task added to
+ * @tq: the tq_struct describing the task to add
+ */
+void hpsb_add_packet_complete_task(struct hpsb_packet *packet, struct tq_struct *tq)
+{
+	list_add_tail(&tq->list, &packet->complete_tq);
+	return;
+}
 
 /**
  * alloc_hpsb_packet - allocate new packet structure
@@ -160,7 +184,10 @@
         abort_requests(host);
         host->in_bus_reset = 1;
         host->irm_id = -1;
+	host->is_irm = 0;
         host->busmgr_id = -1;
+	host->is_busmgr = 0;
+	host->is_cycmst = 0;
         host->node_count = 0;
         host->selfid_count = 0;
 
@@ -354,7 +381,10 @@
         }
 
         host->reset_retries = 0;
-        if (isroot) host->driver->devctl(host, ACT_CYCLE_MASTER, 1);
+        if (isroot) {
+		host->driver->devctl(host, ACT_CYCLE_MASTER, 1);
+		host->is_cycmst = 1;
+	}
 	atomic_inc(&host->generation);
 	host->in_bus_reset = 0;
         highlevel_host_reset(host);
@@ -378,7 +408,7 @@
                 packet->state = hpsb_complete;
                 up(&packet->state_change);
                 up(&packet->state_change);
-                run_task_queue(&packet->complete_tq);
+                process_complete_tasks(packet);
                 return;
         }
 
@@ -390,7 +420,7 @@
         spin_unlock_irqrestore(&host->pending_pkt_lock, flags);
 
         up(&packet->state_change);
-        queue_task(&host->timeout_tq, &tq_timer);
+        schedule_task(&host->timeout_tq);
 }
 
 /**
@@ -528,7 +558,7 @@
 
         packet->state = hpsb_complete;
         up(&packet->state_change);
-        run_task_queue(&packet->complete_tq);
+	process_complete_tasks(packet);
 }
 
 
@@ -748,7 +778,7 @@
                 packet->state = hpsb_complete;
                 packet->ack_code = ACKX_ABORTED;
                 up(&packet->state_change);
-                run_task_queue(&packet->complete_tq);
+		process_complete_tasks(packet);
         }
 }
 
@@ -780,9 +810,9 @@
                 }
         }
 
-        if (!list_empty(&host->pending_packets)) {
-                queue_task(&host->timeout_tq, &tq_timer);
-        }
+        if (!list_empty(&host->pending_packets))
+		schedule_task(&host->timeout_tq);
+
         spin_unlock_irqrestore(&host->pending_pkt_lock, flags);
 
         list_for_each(lh, &expiredlist) {
@@ -790,7 +820,7 @@
                 packet->state = hpsb_complete;
                 packet->ack_code = ACKX_TIMEOUT;
                 up(&packet->state_change);
-                run_task_queue(&packet->complete_tq);
+		process_complete_tasks(packet);
         }
 }
 
@@ -1049,6 +1079,7 @@
 EXPORT_SYMBOL(hpsb_ref_host);
 EXPORT_SYMBOL(hpsb_unref_host);
 EXPORT_SYMBOL(hpsb_speedto_str);
+EXPORT_SYMBOL(hpsb_add_packet_complete_task);
 
 EXPORT_SYMBOL(alloc_hpsb_packet);
 EXPORT_SYMBOL(free_hpsb_packet);
diff -Nru a/drivers/ieee1394/ieee1394_core.h b/drivers/ieee1394/ieee1394_core.h
--- a/drivers/ieee1394/ieee1394_core.h	Mon Oct  7 11:25:26 2002
+++ b/drivers/ieee1394/ieee1394_core.h	Mon Oct  7 11:25:26 2002
@@ -69,13 +69,16 @@
         /* Very core internal, don't care. */
         struct semaphore state_change;
 
-        task_queue complete_tq;
+	struct list_head complete_tq;
 
         /* Store jiffies for implementing bus timeouts. */
         unsigned long sendtime;
 
         quadlet_t embedded_header[5];
 };
+
+/* add a new task for when a packet completes */
+void hpsb_add_packet_complete_task(struct hpsb_packet *packet, struct tq_struct *tq);
 
 static inline struct hpsb_packet *driver_packet(struct list_head *l)
 {
diff -Nru a/drivers/ieee1394/nodemgr.c b/drivers/ieee1394/nodemgr.c
--- a/drivers/ieee1394/nodemgr.c	Mon Oct  7 11:25:36 2002
+++ b/drivers/ieee1394/nodemgr.c	Mon Oct  7 11:25:36 2002
@@ -195,6 +195,10 @@
 		ret = hpsb_read(host, nodeid, generation, address, quad, 4);
 		if (!ret)
 			break;
+
+		set_current_state(TASK_INTERRUPTIBLE);
+		if (schedule_timeout (HZ/3))
+			return -1;
 	}
 	*quad = be32_to_cpu(*quad);
 
@@ -207,8 +211,10 @@
 {
 	quadlet_t quad;
 	int size = 0;
+
 	if (nodemgr_read_quadlet(host, nodeid, generation, address, &quad))
 		return -1;
+
 	if (CONFIG_ROM_KEY(quad) == CONFIG_ROM_DESCRIPTOR_LEAF) {
 		/* This is the offset.  */
 		address += 4 * CONFIG_ROM_VALUE(quad); 
@@ -217,6 +223,7 @@
 		/* Now we got the size of the text descriptor leaf. */
 		size = CONFIG_ROM_LEAF_LENGTH(quad);
 	}
+
 	return size;
 }
 
@@ -228,7 +235,7 @@
 	int i, size, ret;
 
 	if (nodemgr_read_quadlet(ne->host, ne->nodeid, ne->generation, address, &quad)
-	    && CONFIG_ROM_KEY(quad) != CONFIG_ROM_DESCRIPTOR_LEAF)
+	    || CONFIG_ROM_KEY(quad) != CONFIG_ROM_DESCRIPTOR_LEAF)
 		return -1;
 
 	/* This is the offset.  */
diff -Nru a/drivers/ieee1394/ohci1394.c b/drivers/ieee1394/ohci1394.c
--- a/drivers/ieee1394/ohci1394.c	Mon Oct  7 11:25:37 2002
+++ b/drivers/ieee1394/ohci1394.c	Mon Oct  7 11:25:37 2002
@@ -154,7 +154,7 @@
 printk(level "%s_%d: " fmt "\n" , OHCI1394_DRIVER_NAME, card , ## args)
 
 static char version[] __devinitdata =
-	"$Rev: 555 $ Ben Collins <bcollins@debian.org>";
+	"$Rev: 578 $ Ben Collins <bcollins@debian.org>";
 
 /* Module Parameters */
 MODULE_PARM(attempt_root,"i");
@@ -595,7 +595,7 @@
 	      ((((buf) >> 16) & 0xf) + (((buf) >> 20) & 0xf) * 10),
 	      ((((buf) >> 4) & 0xf) + ((buf) & 0xf) * 10), ohci->dev->irq,
 	      pci_resource_start(ohci->dev, 0),
-	      pci_resource_start(ohci->dev, 0) + OHCI1394_REGISTER_SIZE,
+	      pci_resource_start(ohci->dev, 0) + OHCI1394_REGISTER_SIZE - 1,
 	      ohci->max_packet_size);
 }
 
@@ -810,7 +810,6 @@
 {
 	struct ti_ohci *ohci = host->hostdata;
 	struct dma_trm_ctx *d;
-	unsigned char tcode;
 	unsigned long flags;
 
 	if (packet->data_size > ohci->max_packet_size) {
@@ -821,10 +820,14 @@
 	}
 
 	/* Decide wether we have an iso, a request, or a response packet */
-	tcode = (packet->header[0]>>4)&0xf;
-	if (tcode == TCODE_ISO_DATA) d = &ohci->it_context;
-	else if (tcode & 0x02) d = &ohci->at_resp_context;
-	else d = &ohci->at_req_context;
+	if (packet->type == hpsb_raw)
+		d = &ohci->at_req_context;
+	else if (packet->tcode == TCODE_ISO_DATA)
+		d = &ohci->it_context;
+	else if (packet->tcode & 0x02)
+		d = &ohci->at_resp_context;
+	else 
+		d = &ohci->at_req_context;
 
 	spin_lock_irqsave(&d->lock,flags);
 
diff -Nru a/drivers/ieee1394/pcilynx.c b/drivers/ieee1394/pcilynx.c
--- a/drivers/ieee1394/pcilynx.c	Mon Oct  7 11:25:31 2002
+++ b/drivers/ieee1394/pcilynx.c	Mon Oct  7 11:25:31 2002
@@ -887,13 +887,14 @@
         struct memdata *md = (struct memdata *)file->private_data;
         ssize_t bcount;
         size_t alignfix;
-        int off = (int)*offset; /* avoid useless 64bit-arithmetic */
+	loff_t off = *offset; /* avoid useless 64bit-arithmetic */
         ssize_t retval;
         void *membase;
 
 	if (*offset != off)	/* Check for EOF before we trust wrap */
 		return 0;
 	
+	/* FIXME: Signed wrap is undefined in C - wants fixing up */
 	if (off + count > off)
 		return 0;
 		
diff -Nru a/drivers/ieee1394/raw1394.c b/drivers/ieee1394/raw1394.c
--- a/drivers/ieee1394/raw1394.c	Mon Oct  7 11:25:25 2002
+++ b/drivers/ieee1394/raw1394.c	Mon Oct  7 11:25:25 2002
@@ -743,7 +743,7 @@
         }
 
         req->tq.data = req;
-        queue_task(&req->tq, &packet->complete_tq);
+        hpsb_add_packet_complete_task(packet, &req->tq);
 
         spin_lock_irq(&fi->reqlists_lock);
         list_add_tail(&req->list, &fi->req_pending);
@@ -786,7 +786,7 @@
         req->tq.data = req;
         req->tq.routine = (void (*)(void*))queue_complete_req;
         req->req.length = 0;
-        queue_task(&req->tq, &packet->complete_tq);
+	hpsb_add_packet_complete_task(packet, &req->tq);
 
         spin_lock_irq(&fi->reqlists_lock);
         list_add_tail(&req->list, &fi->req_pending);
diff -Nru a/drivers/ieee1394/sbp2.c b/drivers/ieee1394/sbp2.c
--- a/drivers/ieee1394/sbp2.c	Mon Oct  7 11:25:26 2002
+++ b/drivers/ieee1394/sbp2.c	Mon Oct  7 11:25:26 2002
@@ -327,6 +327,7 @@
 #include <asm/byteorder.h>
 #include <asm/atomic.h>
 #include <asm/system.h>
+#include <asm/io.h>
 #include <asm/scatterlist.h>
 
 #ifdef CONFIG_KBUILD_2_5
@@ -350,15 +351,15 @@
 #include "sbp2.h"
 
 static char version[] __devinitdata =
-	"$Rev: 545 $ James Goodwin <jamesg@filanet.com>";
+	"$Rev: 584 $ James Goodwin <jamesg@filanet.com>";
 
 /*
  * Module load parameter definitions
  */
 
 /*
- * Change sbp2_max_speed on module load if you have a bad IEEE-1394 controller
- * that has trouble running 2KB packets at 400mb.
+ * Change sbp2_max_speed on module load if you have a bad IEEE-1394
+ * controller that has trouble running 2KB packets at 400mb.
  *
  * NOTE: On certain OHCI parts I have seen short packets on async transmit
  * (probably due to PCI latency/throughput issues with the part). You can
@@ -374,48 +375,54 @@
 static int sbp2_max_speed = SPEED_400;
 
 /*
- * Set sbp2_serialize_io to 1 if you'd like only one scsi command sent down to
- * us at a time (debugging). This might be necessary for very badly behaved sbp2 devices.
+ * Set sbp2_serialize_io to 1 if you'd like only one scsi command sent
+ * down to us at a time (debugging). This might be necessary for very
+ * badly behaved sbp2 devices.
  */
 MODULE_PARM(sbp2_serialize_io,"i");
 MODULE_PARM_DESC(sbp2_serialize_io, "Serialize all I/O coming down from the scsi drivers (default = 0)");
 static int sbp2_serialize_io = 0;	/* serialize I/O - available for debugging purposes */
 
 /*
- * Bump up sbp2_max_sectors if you'd like to support very large sized transfers. Please note 
- * that some older sbp2 bridge chips are broken for transfers greater or equal to 128KB.
- * Default is a value of 255 sectors, or just under 128KB (at 512 byte sector size). I can note
- * that the Oxsemi sbp2 chipsets have no problems supporting very large transfer sizes.
+ * Bump up sbp2_max_sectors if you'd like to support very large sized
+ * transfers. Please note that some older sbp2 bridge chips are broken for
+ * transfers greater or equal to 128KB.  Default is a value of 255
+ * sectors, or just under 128KB (at 512 byte sector size). I can note that
+ * the Oxsemi sbp2 chipsets have no problems supporting very large
+ * transfer sizes.
  */
 MODULE_PARM(sbp2_max_sectors,"i");
 MODULE_PARM_DESC(sbp2_max_sectors, "Change max sectors per I/O supported (default = 255)");
 static int sbp2_max_sectors = SBP2_MAX_SECTORS;
 
 /*
- * Adjust sbp2_max_outstanding_cmds to tune performance if you have many sbp2 devices attached
- * (or if you need to do some debugging).
+ * Adjust sbp2_max_outstanding_cmds to tune performance if you have many
+ * sbp2 devices attached (or if you need to do some debugging).
  */
 MODULE_PARM(sbp2_max_outstanding_cmds,"i");
 MODULE_PARM_DESC(sbp2_max_outstanding_cmds, "Change max outstanding concurrent commands (default = 8)");
 static int sbp2_max_outstanding_cmds = SBP2SCSI_MAX_OUTSTANDING_CMDS;
 
 /*
- * Adjust sbp2_max_cmds_per_lun to tune performance. Enabling more than one concurrent/linked 
- * command per sbp2 device may allow some performance gains, but some older sbp2 devices have 
- * firmware bugs resulting in problems when linking commands... so, enable this with care. 
- * I can note that the Oxsemi OXFW911 sbp2 chipset works very well with large numbers of 
- * concurrent/linked commands.  =)
+ * Adjust sbp2_max_cmds_per_lun to tune performance. Enabling more than
+ * one concurrent/linked command per sbp2 device may allow some
+ * performance gains, but some older sbp2 devices have firmware bugs
+ * resulting in problems when linking commands... so, enable this with
+ * care.  I can note that the Oxsemi OXFW911 sbp2 chipset works very well
+ * with large numbers of concurrent/linked commands.  =)
  */
 MODULE_PARM(sbp2_max_cmds_per_lun,"i");
 MODULE_PARM_DESC(sbp2_max_cmds_per_lun, "Change max concurrent commands per sbp2 device (default = 1)");
 static int sbp2_max_cmds_per_lun = SBP2SCSI_MAX_CMDS_PER_LUN;
 
 /*
- * Exclusive login to sbp2 device? In most cases, the sbp2 driver should do an exclusive login, as it's
- * generally unsafe to have two hosts talking to a single sbp2 device at the same time (filesystem
- * coherency, etc.). If you're running an sbp2 device that supports multiple logins, and you're either
- * running read-only filesystems or some sort of special filesystem supporting multiple hosts, then
- * set sbp2_exclusive_login to zero. Note: The Oxsemi OXFW911 sbp2 chipset supports up to four
+ * Exclusive login to sbp2 device? In most cases, the sbp2 driver should
+ * do an exclusive login, as it's generally unsafe to have two hosts
+ * talking to a single sbp2 device at the same time (filesystem coherency,
+ * etc.). If you're running an sbp2 device that supports multiple logins,
+ * and you're either running read-only filesystems or some sort of special
+ * filesystem supporting multiple hosts, then set sbp2_exclusive_login to
+ * zero. Note: The Oxsemi OXFW911 sbp2 chipset supports up to four
  * concurrent logins.
  */
 MODULE_PARM(sbp2_exclusive_login,"i");
@@ -423,9 +430,14 @@
 static int sbp2_exclusive_login = 1;
 
 /*
- * SCSI inquiry hack for really badly behaved sbp2 devices. Turn this on if your sbp2 device
- * is not properly handling the SCSI inquiry command. This hack makes the inquiry look more
- * like a typical MS Windows inquiry.
+ * SCSI inquiry hack for really badly behaved sbp2 devices. Turn this on
+ * if your sbp2 device is not properly handling the SCSI inquiry command.
+ * This hack makes the inquiry look more like a typical MS Windows
+ * inquiry.
+ * 
+ * If sbp2_force_inquiry_hack=1 is required for your device to work,
+ * please submit the logged sbp2_firmware_revision value of this device to
+ * the linux1394-devel mailing list.
  */
 MODULE_PARM(sbp2_force_inquiry_hack,"i");
 MODULE_PARM_DESC(sbp2_force_inquiry_hack, "Force SCSI inquiry hack (default = 0)");
@@ -550,13 +562,17 @@
 	.update = 	sbp2_update
 };
 
-/* List of device firmware's that require a forced 36 byte inquiry. Note
- * the final 0x0 needs to be there for denoting end of list.  */
+/* List of device firmware's that require a forced 36 byte inquiry.  */
 static u32 sbp2_broken_inquiry_list[] = {
 	0x00002800,	/* Stefan Richter <richtest@bauwesen.tu-cottbus.de> */
-	0x0
+			/* DViCO Momobay CX-1 */
+	0x00000200	/* Andreas Plesch <plesch@fas.harvard.edu> */
+			/* QPS Fire DVDBurner */
 };
 
+#define NUM_BROKEN_INQUIRY_DEVS \
+	(sizeof(sbp2_broken_inquiry_list)/sizeof(*sbp2_broken_inquiry_list))
+
 /**************************************
  * General utility functions
  **************************************/
@@ -787,7 +803,7 @@
 		request_packet->tq.routine = (void (*)(void*))sbp2util_free_request_packet;
 		request_packet->tq.data = request_packet;
 		request_packet->hi_context = hi;
-		queue_task(&request_packet->tq, &packet->complete_tq);
+		hpsb_add_packet_complete_task(packet, &request_packet->tq);
 
 		/*
 		 * Now, put the packet on the in-use list.
@@ -1914,7 +1930,10 @@
 			/* Firmware revision */
 			scsi_id->sbp2_firmware_revision
 				= CONFIG_ROM_VALUE(ud->quadlets[i]);
-			SBP2_DEBUG("sbp2_firmware_revision = %x",
+			if (sbp2_force_inquiry_hack)
+				SBP2_INFO("sbp2_firmware_revision = %x",
+				   (unsigned int) scsi_id->sbp2_firmware_revision);
+			else	SBP2_DEBUG("sbp2_firmware_revision = %x",
 				   (unsigned int) scsi_id->sbp2_firmware_revision);
 			break;
 
@@ -1948,19 +1967,14 @@
 
 	/* Check for a blacklisted set of devices that require us to force
 	 * a 36 byte host inquiry. This can be overriden as a module param
-	 * (to force all hosts).
-	 *
-	 * XXX If this does not detect your firmware as being defective,
-	 * but using the sbp2_force_inquiry_hack allows your device to
-	 * work, please submit the value of your firmware revision to the
-	 * linux1394-devel mailing list.  */
-	for (i = 0; sbp2_broken_inquiry_list[i]; i++) {
+	 * (to force all hosts).  */
+	for (i = 0; i < NUM_BROKEN_INQUIRY_DEVS; i++) {
 		if ((scsi_id->sbp2_firmware_revision & 0xffff00) ==
 				sbp2_broken_inquiry_list[i]) {
 			SBP2_WARN("Node " NODE_BUS_FMT ": Using 36byte inquiry workaround",
 					NODE_BUS_ARGS(scsi_id->ne->nodeid));
 			scsi_id->workarounds |= SBP2_BREAKAGE_INQUIRY_HACK;
-			break; // No need to continue.
+			break; /* No need to continue. */
 		}
 	}
 }
diff -Nru a/drivers/input/joystick/iforce/Makefile b/drivers/input/joystick/iforce/Makefile
--- a/drivers/input/joystick/iforce/Makefile	Mon Oct  7 11:25:35 2002
+++ b/drivers/input/joystick/iforce/Makefile	Mon Oct  7 11:25:35 2002
@@ -5,7 +5,6 @@
 #
 
 # Goal definition
-list-multi	:= iforce.o
 iforce-objs	:= iforce-ff.o iforce-main.o iforce-packets.o
 
 obj-$(CONFIG_JOYSTICK_IFORCE)	+= iforce.o 
@@ -23,8 +22,3 @@
 # The global Rules.make.
 
 include $(TOPDIR)/Rules.make
-
-# Additional rules
-iforce.o: $(iforce-objs)
-	$(LD) -r -o $@ $(iforce-objs)
-
diff -Nru a/drivers/input/keyboard/atkbd.c b/drivers/input/keyboard/atkbd.c
--- a/drivers/input/keyboard/atkbd.c	Mon Oct  7 11:25:37 2002
+++ b/drivers/input/keyboard/atkbd.c	Mon Oct  7 11:25:37 2002
@@ -17,7 +17,7 @@
 #include <linux/init.h>
 #include <linux/input.h>
 #include <linux/serio.h>
-#include <linux/tqueue.h>
+#include <linux/workqueue.h>
 
 MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>");
 MODULE_DESCRIPTION("AT and PS/2 keyboard driver");
diff -Nru a/drivers/input/keyboard/sunkbd.c b/drivers/input/keyboard/sunkbd.c
--- a/drivers/input/keyboard/sunkbd.c	Mon Oct  7 11:25:25 2002
+++ b/drivers/input/keyboard/sunkbd.c	Mon Oct  7 11:25:25 2002
@@ -35,7 +35,7 @@
 #include <linux/init.h>
 #include <linux/input.h>
 #include <linux/serio.h>
-#include <linux/tqueue.h>
+#include <linux/workqueue.h>
 
 MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
 MODULE_DESCRIPTION("Sun keyboard driver");
@@ -76,7 +76,7 @@
 	unsigned char keycode[128];
 	struct input_dev dev;
 	struct serio *serio;
-	struct tq_struct tq;
+	struct work_struct tq;
 	char name[64];
 	char phys[32];
 	char type;
@@ -106,7 +106,7 @@
 	switch (data) {
 
 		case SUNKBD_RET_RESET:
-			schedule_task(&sunkbd->tq);
+			schedule_work(&sunkbd->tq);
 			sunkbd->reset = -1;
 			return;
 
@@ -240,8 +240,7 @@
 
 	sunkbd->serio = serio;
 
-	sunkbd->tq.routine = sunkbd_reinit;
-	sunkbd->tq.data = sunkbd;
+	INIT_WORK(&sunkbd->tq, sunkbd_reinit, sunkbd);
 
 	sunkbd->dev.keycode = sunkbd->keycode;
 	sunkbd->dev.keycodesize = sizeof(unsigned char);
diff -Nru a/drivers/input/mouse/Config.help b/drivers/input/mouse/Config.help
--- a/drivers/input/mouse/Config.help	Mon Oct  7 11:25:37 2002
+++ b/drivers/input/mouse/Config.help	Mon Oct  7 11:25:37 2002
@@ -52,7 +52,7 @@
 
 CONFIG_MOUSE_PC110PAD
   Say Y if you have the IBM PC-110 micro-notebook and want its
-  touchscreen supported.
+  touchpad supported.
 
   This driver is also available as a module ( = code which can be
   inserted in and removed from the running kernel whenever you want).
diff -Nru a/drivers/input/mouse/psmouse.c b/drivers/input/mouse/psmouse.c
--- a/drivers/input/mouse/psmouse.c	Mon Oct  7 11:25:27 2002
+++ b/drivers/input/mouse/psmouse.c	Mon Oct  7 11:25:27 2002
@@ -17,7 +17,6 @@
 #include <linux/input.h>
 #include <linux/serio.h>
 #include <linux/init.h>
-#include <linux/tqueue.h>
 
 MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>");
 MODULE_DESCRIPTION("PS/2 mouse driver");
@@ -43,7 +42,6 @@
 	struct serio *serio;
 	char *vendor;
 	char *name;
-	struct tq_struct tq;
 	unsigned char cmdbuf[8];
 	unsigned char packet[8];
 	unsigned char cmdcnt;
diff -Nru a/drivers/input/power.c b/drivers/input/power.c
--- a/drivers/input/power.c	Mon Oct  7 11:25:36 2002
+++ b/drivers/input/power.c	Mon Oct  7 11:25:36 2002
@@ -51,9 +51,7 @@
         suspend_button_pushed = 0;
 }
 
-static struct tq_struct suspend_button_task = {
-        routine: suspend_button_task_handler
-};
+static DECLARE_WORK(suspend_button_task, suspend_button_task_handler, NULL);
 
 static void power_event(struct input_handle *handle, unsigned int type, 
 		        unsigned int code, int down)
@@ -73,7 +71,7 @@
 
 				if (!suspend_button_pushed) {
                 			suspend_button_pushed = 1;
-                        		schedule_task(&suspend_button_task);
+                        		schedule_work(&suspend_button_task);
                 		}
 				break;
 			case KEY_POWER:
diff -Nru a/drivers/isdn/act2000/act2000.h b/drivers/isdn/act2000/act2000.h
--- a/drivers/isdn/act2000/act2000.h	Mon Oct  7 11:25:35 2002
+++ b/drivers/isdn/act2000/act2000.h	Mon Oct  7 11:25:35 2002
@@ -62,7 +62,7 @@
 
 #include <linux/sched.h>
 #include <linux/string.h>
-#include <linux/tqueue.h>
+#include <linux/workqueue.h>
 #include <linux/interrupt.h>
 #include <linux/skbuff.h>
 #include <linux/errno.h>
@@ -162,9 +162,9 @@
 	__u16 need_b3ack;                /* Flag: Need ACK for current skb   */
 	struct sk_buff *sbuf;            /* skb which is currently sent      */
 	struct timer_list ptimer;        /* Poll timer                       */
-	struct tq_struct snd_tq;         /* Task struct for xmit bh          */
-	struct tq_struct rcv_tq;         /* Task struct for rcv bh           */
-	struct tq_struct poll_tq;        /* Task struct for polled rcv bh    */
+	struct work_struct snd_tq;         /* Task struct for xmit bh          */
+	struct work_struct rcv_tq;         /* Task struct for rcv bh           */
+	struct work_struct poll_tq;        /* Task struct for polled rcv bh    */
 	msn_entry *msn_list;
 	unsigned short msgnum;           /* Message number fur sending       */
 	act2000_chan bch[ACT2000_BCH];   /* B-Channel status/control         */
@@ -179,20 +179,17 @@
 
 extern __inline__ void act2000_schedule_tx(act2000_card *card)
 {
-        queue_task(&card->snd_tq, &tq_immediate);
-        mark_bh(IMMEDIATE_BH);
+        schedule_work(&card->snd_tq);
 }
 
 extern __inline__ void act2000_schedule_rx(act2000_card *card)
 {
-        queue_task(&card->rcv_tq, &tq_immediate);
-        mark_bh(IMMEDIATE_BH);
+        schedule_work(&card->rcv_tq);
 }
 
 extern __inline__ void act2000_schedule_poll(act2000_card *card)
 {
-        queue_task(&card->poll_tq, &tq_immediate);
-        mark_bh(IMMEDIATE_BH);
+        schedule_work(&card->poll_tq);
 }
 
 extern char *act2000_find_eaz(act2000_card *, char);
diff -Nru a/drivers/isdn/act2000/module.c b/drivers/isdn/act2000/module.c
--- a/drivers/isdn/act2000/module.c	Mon Oct  7 11:25:26 2002
+++ b/drivers/isdn/act2000/module.c	Mon Oct  7 11:25:26 2002
@@ -616,12 +616,9 @@
 	skb_queue_head_init(&card->sndq);
 	skb_queue_head_init(&card->rcvq);
 	skb_queue_head_init(&card->ackq);
-	card->snd_tq.routine = (void *) (void *) act2000_transmit;
-	card->snd_tq.data = card;
-	card->rcv_tq.routine = (void *) (void *) actcapi_dispatch;
-	card->rcv_tq.data = card;
-	card->poll_tq.routine = (void *) (void *) act2000_receive;
-	card->poll_tq.data = card;
+	INIT_WORK(&card->snd_tq, (void *) (void *) act2000_transmit, card);
+	INIT_WORK(&card->rcv_tq, (void *) (void *) actcapi_dispatch, card);
+	INIT_WORK(&card->poll_tq, (void *) (void *) act2000_receive, card);
 	init_timer(&card->ptimer);
         card->interface.channels = ACT2000_BCH;
         card->interface.maxbufsize = 4000;
diff -Nru a/drivers/isdn/capi/kcapi.c b/drivers/isdn/capi/kcapi.c
--- a/drivers/isdn/capi/kcapi.c	Mon Oct  7 11:25:37 2002
+++ b/drivers/isdn/capi/kcapi.c	Mon Oct  7 11:25:37 2002
@@ -20,7 +20,7 @@
 #include <linux/proc_fs.h>
 #include <linux/seq_file.h>
 #include <linux/skbuff.h>
-#include <linux/tqueue.h>
+#include <linux/workqueue.h>
 #include <linux/capi.h>
 #include <linux/kernelcapi.h>
 #include <linux/init.h>
@@ -69,8 +69,8 @@
 static int ncards;
 static struct sk_buff_head recv_queue;
 
-static struct tq_struct tq_state_notify;
-static struct tq_struct tq_recv_notify;
+static struct work_struct tq_state_notify;
+static struct work_struct tq_recv_notify;
 
 /* -------- ref counting -------------------------------------- */
 
@@ -234,7 +234,7 @@
 	 * user process, not in bh.
 	 */
 	MOD_INC_USE_COUNT;
-	if (schedule_task(&tq_state_notify) == 0)
+	if (schedule_work(&tq_state_notify) == 0)
 		MOD_DEC_USE_COUNT;
 	return 0;
 }
@@ -359,8 +359,7 @@
 
 	}
 	skb_queue_tail(&recv_queue, skb);
-	queue_task(&tq_recv_notify, &tq_immediate);
-	mark_bh(IMMEDIATE_BH);
+	schedule_work(&tq_recv_notify);
 	return;
 
 error:
@@ -875,11 +874,8 @@
 
 	skb_queue_head_init(&recv_queue);
 
-	tq_state_notify.routine = notify_handler;
-	tq_state_notify.data = 0;
-
-	tq_recv_notify.routine = recv_handler;
-	tq_recv_notify.data = 0;
+	INIT_WORK(&tq_state_notify, notify_handler, NULL);
+	INIT_WORK(&tq_recv_notify, recv_handler, NULL);
 
         kcapi_proc_init();
 
diff -Nru a/drivers/isdn/eicon/Divas_mod.c b/drivers/isdn/eicon/Divas_mod.c
--- a/drivers/isdn/eicon/Divas_mod.c	Mon Oct  7 11:25:25 2002
+++ b/drivers/isdn/eicon/Divas_mod.c	Mon Oct  7 11:25:25 2002
@@ -10,7 +10,7 @@
 #undef N_DATA
 
 #include <linux/kernel.h>
-
+#include <linux/tqueue.h>
 #include <linux/module.h>
 #include <linux/pci.h>
 #include <linux/ioport.h>
@@ -25,6 +25,8 @@
 MODULE_AUTHOR("Armin Schindler");
 MODULE_LICENSE("GPL");
 
+void DivasInitDpc(void);
+
 #ifdef MODULE
 #include "idi.h"
 void DIVA_DIDD_Write(DESCRIPTOR *, int);
@@ -46,6 +48,8 @@
 	printk(KERN_WARNING "CONFIG_PCI is not defined!\n");
 	return -ENODEV;
 #endif
+
+	DivasInitDpc();
 
 	if (pci_present())
 	{
diff -Nru a/drivers/isdn/eicon/adapter.h b/drivers/isdn/eicon/adapter.h
--- a/drivers/isdn/eicon/adapter.h	Mon Oct  7 11:25:31 2002
+++ b/drivers/isdn/eicon/adapter.h	Mon Oct  7 11:25:31 2002
@@ -176,8 +176,7 @@
 /*------------------------------------------------------------------*/
 
 int		DivasDpcSchedule(void);
-void		DivasDoDpc(void *);
-void		DivasDoRequestDpc(void *pData);
+void		DivasDoDpc(unsigned long);
 int		DivasScheduleRequestDpc(void);
 
 /* table of IDI request functions */
diff -Nru a/drivers/isdn/eicon/common.c b/drivers/isdn/eicon/common.c
--- a/drivers/isdn/eicon/common.c	Mon Oct  7 11:25:36 2002
+++ b/drivers/isdn/eicon/common.c	Mon Oct  7 11:25:36 2002
@@ -801,7 +801,7 @@
 			
 }
 
-void	DivasDoDpc(void *pData)
+void	DivasDoDpc(unsigned long dummy)
 {
 	card_t	*card = DivasCards;
 	int 	i = DivasCardNext;
@@ -812,11 +812,6 @@
 		DivaDoCardDpc(card);
             card++;
 	}
-}
-
-void	DivasDoRequestDpc(void *pData)
-{
-	DivasDoDpc(pData);
 }
 
 /*
diff -Nru a/drivers/isdn/eicon/eicon.h b/drivers/isdn/eicon/eicon.h
--- a/drivers/isdn/eicon/eicon.h	Mon Oct  7 11:25:28 2002
+++ b/drivers/isdn/eicon/eicon.h	Mon Oct  7 11:25:28 2002
@@ -1,4 +1,4 @@
-/* $Id: eicon.h,v 1.23.6.5 2001/09/23 22:24:37 kai Exp $
+/* $Id: eicon.h,v 1.1.4.1.2.3 2002/10/01 11:29:13 armin Exp $
  *
  * ISDN low-level module for Eicon active ISDN-Cards.
  *
@@ -117,7 +117,7 @@
 #include <linux/config.h>
 #include <linux/sched.h>
 #include <linux/string.h>
-#include <linux/tqueue.h>
+#include <linux/workqueue.h>
 #include <linux/interrupt.h>
 #include <linux/skbuff.h>
 #include <linux/errno.h>
@@ -321,9 +321,9 @@
 	struct sk_buff_head sackq;       /* Data-Ack-Message queue           */
 	struct sk_buff_head statq;       /* Status-Message queue             */
 	int statq_entries;
-	struct tq_struct snd_tq;         /* Task struct for xmit bh          */
-	struct tq_struct rcv_tq;         /* Task struct for rcv bh           */
-	struct tq_struct ack_tq;         /* Task struct for ack bh           */
+	struct tasklet_struct snd_tq;    /* Task struct for xmit bh          */
+	struct tasklet_struct rcv_tq;    /* Task struct for rcv bh           */
+	struct tasklet_struct ack_tq;    /* Task struct for ack bh           */
 	eicon_chan*	IdTable[256];	 /* Table to find entity   */
 	__u16  ref_in;
 	__u16  ref_out;
@@ -349,20 +349,17 @@
 
 extern __inline__ void eicon_schedule_tx(eicon_card *card)
 {
-        queue_task(&card->snd_tq, &tq_immediate);
-        mark_bh(IMMEDIATE_BH);
+	tasklet_schedule(&card->snd_tq);
 }
 
 extern __inline__ void eicon_schedule_rx(eicon_card *card)
 {
-        queue_task(&card->rcv_tq, &tq_immediate);
-        mark_bh(IMMEDIATE_BH);
+	tasklet_schedule(&card->rcv_tq);
 }
 
 extern __inline__ void eicon_schedule_ack(eicon_card *card)
 {
-        queue_task(&card->ack_tq, &tq_immediate);
-        mark_bh(IMMEDIATE_BH);
+	tasklet_schedule(&card->ack_tq);
 }
 
 extern int eicon_addcard(int, int, int, char *, int);
diff -Nru a/drivers/isdn/eicon/eicon_dsp.h b/drivers/isdn/eicon/eicon_dsp.h
--- a/drivers/isdn/eicon/eicon_dsp.h	Mon Oct  7 11:25:35 2002
+++ b/drivers/isdn/eicon/eicon_dsp.h	Mon Oct  7 11:25:35 2002
@@ -1,4 +1,4 @@
-/* $Id: eicon_dsp.h,v 1.7.6.1 2001/09/23 22:24:37 kai Exp $
+/* $Id: eicon_dsp.h,v 1.1.4.1.2.2 2002/10/01 11:29:13 armin Exp $
  *
  * ISDN lowlevel-module for Eicon active cards.
  * DSP definitions
diff -Nru a/drivers/isdn/eicon/eicon_idi.c b/drivers/isdn/eicon/eicon_idi.c
--- a/drivers/isdn/eicon/eicon_idi.c	Mon Oct  7 11:25:25 2002
+++ b/drivers/isdn/eicon/eicon_idi.c	Mon Oct  7 11:25:25 2002
@@ -1,4 +1,4 @@
-/* $Id: eicon_idi.c,v 1.41.6.4 2001/11/06 20:58:29 kai Exp $
+/* $Id: eicon_idi.c,v 1.1.4.1.2.4 2002/10/01 11:29:13 armin Exp $
  *
  * ISDN lowlevel-module for Eicon active cards.
  * IDI interface 
@@ -24,7 +24,7 @@
 
 #undef EICON_FULL_SERVICE_OKTETT
 
-char *eicon_idi_revision = "$Revision: 1.41.6.4 $";
+char *eicon_idi_revision = "$Revision: 1.1.4.1.2.4 $";
 
 eicon_manifbuf *manbuf;
 
diff -Nru a/drivers/isdn/eicon/eicon_idi.h b/drivers/isdn/eicon/eicon_idi.h
--- a/drivers/isdn/eicon/eicon_idi.h	Mon Oct  7 11:25:25 2002
+++ b/drivers/isdn/eicon/eicon_idi.h	Mon Oct  7 11:25:25 2002
@@ -1,4 +1,4 @@
-/* $Id: eicon_idi.h,v 1.11.6.1 2001/09/23 22:24:37 kai Exp $
+/* $Id: eicon_idi.h,v 1.1.4.1.2.2 2002/10/01 11:29:13 armin Exp $
  *
  * ISDN lowlevel-module for the Eicon active cards.
  * IDI-Interface
diff -Nru a/drivers/isdn/eicon/eicon_io.c b/drivers/isdn/eicon/eicon_io.c
--- a/drivers/isdn/eicon/eicon_io.c	Mon Oct  7 11:25:28 2002
+++ b/drivers/isdn/eicon/eicon_io.c	Mon Oct  7 11:25:28 2002
@@ -1,4 +1,4 @@
-/* $Id: eicon_io.c,v 1.13.6.2 2001/09/23 22:24:37 kai Exp $
+/* $Id: eicon_io.c,v 1.1.4.1.2.2 2002/10/01 11:29:13 armin Exp $
  *
  * ISDN low-level module for Eicon active ISDN-Cards.
  * Code for communicating with hardware.
diff -Nru a/drivers/isdn/eicon/eicon_isa.c b/drivers/isdn/eicon/eicon_isa.c
--- a/drivers/isdn/eicon/eicon_isa.c	Mon Oct  7 11:25:26 2002
+++ b/drivers/isdn/eicon/eicon_isa.c	Mon Oct  7 11:25:26 2002
@@ -1,4 +1,4 @@
-/* $Id: eicon_isa.c,v 1.16.6.2 2001/11/06 20:58:29 kai Exp $
+/* $Id: eicon_isa.c,v 1.1.4.1.2.3 2002/10/01 11:29:13 armin Exp $
  *
  * ISDN low-level module for Eicon active ISDN-Cards.
  * Hardware-specific code for old ISA cards.
@@ -20,7 +20,7 @@
 #define release_shmem release_region
 #define request_shmem request_region
 
-char *eicon_isa_revision = "$Revision: 1.16.6.2 $";
+char *eicon_isa_revision = "$Revision: 1.1.4.1.2.3 $";
 
 #undef EICON_MCA_DEBUG
 
diff -Nru a/drivers/isdn/eicon/eicon_isa.h b/drivers/isdn/eicon/eicon_isa.h
--- a/drivers/isdn/eicon/eicon_isa.h	Mon Oct  7 11:25:39 2002
+++ b/drivers/isdn/eicon/eicon_isa.h	Mon Oct  7 11:25:39 2002
@@ -1,4 +1,4 @@
-/* $Id: eicon_isa.h,v 1.10.6.1 2001/09/23 22:24:37 kai Exp $
+/* $Id: eicon_isa.h,v 1.1.4.1.2.2 2002/10/01 11:29:13 armin Exp $
  *
  * ISDN low-level module for Eicon active ISDN-Cards.
  *
diff -Nru a/drivers/isdn/eicon/eicon_mod.c b/drivers/isdn/eicon/eicon_mod.c
--- a/drivers/isdn/eicon/eicon_mod.c	Mon Oct  7 11:25:26 2002
+++ b/drivers/isdn/eicon/eicon_mod.c	Mon Oct  7 11:25:26 2002
@@ -1,4 +1,4 @@
-/* $Id: eicon_mod.c,v 1.37.6.6 2001/09/23 22:24:37 kai Exp $
+/* $Id: eicon_mod.c,v 1.1.4.1.2.4 2002/10/01 11:29:13 armin Exp $
  *
  * ISDN lowlevel-module for Eicon active cards.
  * 
@@ -44,7 +44,7 @@
 static eicon_card *cards = (eicon_card *) NULL;   /* glob. var , contains
                                                      start of card-list   */
 
-static char *eicon_revision = "$Revision: 1.37.6.6 $";
+static char *eicon_revision = "$Revision: 1.1.4.1.2.4 $";
 
 extern char *eicon_pci_revision;
 extern char *eicon_isa_revision;
@@ -142,8 +142,10 @@
 #endif /* CONFIG_PCI */
 
 static void
-eicon_rcv_dispatch(struct eicon_card *card)
+eicon_rcv_dispatch(unsigned long context)
 {
+	struct eicon_card *card = (struct eicon_card *)context;
+
 	switch (card->bus) {
 		case EICON_BUS_ISA:
 		case EICON_BUS_MCA:
@@ -152,13 +154,15 @@
 			break;
 		default:
 			eicon_log(card, 1,
-			       "eicon_ack_dispatch: Illegal bustype %d\n", card->bus);
+			       "eicon_rcv_dispatch: Illegal bustype %d\n", card->bus);
 	}
 }
 
 static void
-eicon_ack_dispatch(struct eicon_card *card)
+eicon_ack_dispatch(unsigned long context)
 {
+	struct eicon_card *card = (struct eicon_card *)context;
+
 	switch (card->bus) {
 		case EICON_BUS_ISA:
 		case EICON_BUS_MCA:
@@ -172,8 +176,10 @@
 }
 
 static void
-eicon_transmit(struct eicon_card *card)
+eicon_transmit(unsigned long context)
 {
+	struct eicon_card *card = (struct eicon_card *)context;
+
 	switch (card->bus) {
 		case EICON_BUS_ISA:
 		case EICON_BUS_MCA:
@@ -868,12 +874,9 @@
 		skb_queue_head_init(&card->sackq);
 		skb_queue_head_init(&card->statq);
 		card->statq_entries = 0;
-		card->snd_tq.routine = (void *) (void *) eicon_transmit;
-		card->snd_tq.data = card;
-		card->rcv_tq.routine = (void *) (void *) eicon_rcv_dispatch;
-		card->rcv_tq.data = card;
-		card->ack_tq.routine = (void *) (void *) eicon_ack_dispatch;
-		card->ack_tq.data = card;
+		tasklet_init(&card->snd_tq, eicon_transmit, (unsigned long)card);
+		tasklet_init(&card->rcv_tq, eicon_rcv_dispatch, (unsigned long)card);
+		tasklet_init(&card->ack_tq, eicon_ack_dispatch, (unsigned long)card);
 		card->interface.maxbufsize = 4000;
 		card->interface.command = if_command;
 		card->interface.writebuf_skb = if_sendbuf;
diff -Nru a/drivers/isdn/eicon/eicon_pci.c b/drivers/isdn/eicon/eicon_pci.c
--- a/drivers/isdn/eicon/eicon_pci.c	Mon Oct  7 11:25:39 2002
+++ b/drivers/isdn/eicon/eicon_pci.c	Mon Oct  7 11:25:39 2002
@@ -1,4 +1,4 @@
-/* $Id: eicon_pci.c,v 1.15.6.3 2001/09/23 22:24:37 kai Exp $
+/* $Id: eicon_pci.c,v 1.1.4.1.2.3 2002/10/01 11:29:13 armin Exp $
  *
  * ISDN low-level module for Eicon active ISDN-Cards.
  * Hardware-specific code for PCI cards.
@@ -24,7 +24,7 @@
 #include "adapter.h"
 #include "uxio.h"
 
-char *eicon_pci_revision = "$Revision: 1.15.6.3 $";
+char *eicon_pci_revision = "$Revision: 1.1.4.1.2.3 $";
 
 #if CONFIG_PCI	         /* intire stuff is only for PCI */
 #ifdef CONFIG_ISDN_DRV_EICON_PCI
diff -Nru a/drivers/isdn/eicon/eicon_pci.h b/drivers/isdn/eicon/eicon_pci.h
--- a/drivers/isdn/eicon/eicon_pci.h	Mon Oct  7 11:25:36 2002
+++ b/drivers/isdn/eicon/eicon_pci.h	Mon Oct  7 11:25:36 2002
@@ -1,4 +1,4 @@
-/* $Id: eicon_pci.h,v 1.6.6.1 2001/09/23 22:24:37 kai Exp $
+/* $Id: eicon_pci.h,v 1.1.4.1.2.2 2002/10/01 11:29:13 armin Exp $
  *
  * ISDN low-level module for Eicon active ISDN-Cards (PCI part).
  *
diff -Nru a/drivers/isdn/eicon/linio.c b/drivers/isdn/eicon/linio.c
--- a/drivers/isdn/eicon/linio.c	Mon Oct  7 11:25:29 2002
+++ b/drivers/isdn/eicon/linio.c	Mon Oct  7 11:25:29 2002
@@ -10,6 +10,10 @@
 
 #define N_DATA
 
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/smp_lock.h>
 #include <asm/io.h>
 #include <asm/system.h>
 #include <linux/slab.h>
@@ -24,7 +28,7 @@
 
 int		Divasdevflag = 0;
 
-//spinlock_t diva_lock = SPIN_LOCK_UNLOCKED;
+spinlock_t diva_lock = SPIN_LOCK_UNLOCKED;
 
 static
 ux_diva_card_t card_pool[MAX_CARDS];
@@ -673,20 +677,14 @@
 {
 	unsigned long flags;
 
- 	//spin_lock_irqsave(&diva_lock, flags);
+ 	spin_lock_irqsave(&diva_lock, flags);
 	
-	save_flags(flags);
-	cli();
 	return flags;
-	
 }
 
 void UxCardUnlock(ux_diva_card_t *card, long ipl)
 {
-	//spin_unlock_irqrestore(&diva_lock, ipl);
-
-	restore_flags(ipl);
-
+	spin_unlock_irqrestore(&diva_lock, ipl);
 }
 
 dword UxTimeGet(void)
diff -Nru a/drivers/isdn/eicon/linsys.c b/drivers/isdn/eicon/linsys.c
--- a/drivers/isdn/eicon/linsys.c	Mon Oct  7 11:25:33 2002
+++ b/drivers/isdn/eicon/linsys.c	Mon Oct  7 11:25:33 2002
@@ -10,7 +10,7 @@
 
 #include <linux/sched.h>
 #undef N_DATA
-#include <linux/tqueue.h>
+#include <linux/workqueue.h>
 
 #include <linux/smp.h>
 struct pt_regs;
@@ -24,6 +24,8 @@
 
 #include "uxio.h"
 
+static struct tasklet_struct DivasTask;
+
 int Divas4BRIInitPCI(card_t *card, dia_card_t *cfg)
 {
 	/* Use UxPciConfigWrite	routines to initialise PCI config space */
@@ -79,28 +81,21 @@
 
 int	DivasDpcSchedule(void)
 {
-	static	struct tq_struct DivasTask;
-
-	DivasTask.routine = DivasDoDpc;
-	DivasTask.data = (void *) 0;
-
-	queue_task(&DivasTask, &tq_immediate);
-	mark_bh(IMMEDIATE_BH);
+	tasklet_schedule(&DivasTask);
 
 	return 0;
 }
 
 int	DivasScheduleRequestDpc(void)
 {
-	static	struct tq_struct DivasTask;
-
-	DivasTask.routine = DivasDoRequestDpc;
-	DivasTask.data = (void *) 0;
-
-	queue_task(&DivasTask, &tq_immediate);
-	mark_bh(IMMEDIATE_BH);
+	tasklet_schedule(&DivasTask);
 
 	return 0;
+}
+
+void DivasInitDpc(void)
+{
+	tasklet_init(&DivasTask, DivasDoDpc, 0);
 }
 
 void    DivasLogAdd(void *buffer, int length)
diff -Nru a/drivers/isdn/hisax/Makefile b/drivers/isdn/hisax/Makefile
--- a/drivers/isdn/hisax/Makefile	Mon Oct  7 11:25:32 2002
+++ b/drivers/isdn/hisax/Makefile	Mon Oct  7 11:25:32 2002
@@ -63,7 +63,7 @@
 
 hisax-objs += $(hisax-objs-y)
 
-CERT := $(shell md5sum -c md5sums.asc >> /dev/null;echo $$?)
+CERT := $(shell cd $(src); md5sum -c md5sums.asc > /dev/null 2> /dev/null ;echo $$?)
 CFLAGS_cert.o := -DCERTIFICATION=$(CERT)
 
 include $(TOPDIR)/Rules.make
diff -Nru a/drivers/isdn/hisax/amd7930.c b/drivers/isdn/hisax/amd7930.c
--- a/drivers/isdn/hisax/amd7930.c	Mon Oct  7 11:25:32 2002
+++ b/drivers/isdn/hisax/amd7930.c	Mon Oct  7 11:25:32 2002
@@ -120,16 +120,14 @@
 	} else {
 		clear_bit(BC_FLG_BUSY, &bcs->Flag);
 		bcs->event |= 1 << B_XMTBUFREADY;
-		queue_task(&bcs->tqueue, &tq_immediate);
-		mark_bh(IMMEDIATE_BH);
+		schedule_work(&bcs->tqueue);
 	}
 }
 
 static void
 Bchan_xmit_callback(struct BCState *bcs)
 {
-	queue_task(&bcs->hw.amd7930.tq_xmt, &tq_immediate);
-	mark_bh(IMMEDIATE_BH);
+	schedule_work(&bcs->hw.amd7930.tq_xmt);
 }
 
 /* B channel transmission: two modes (three, if you count L1_MODE_NULL)
@@ -261,8 +259,7 @@
 			      (void *) &Bchan_recv_callback, (void *) bcs);
 	}
 
-	queue_task(&hw->tq_rcv, &tq_immediate);
-	mark_bh(IMMEDIATE_BH);
+	schedule_work(&hw->tq_rcv);
 }
 
 static void
@@ -308,7 +305,7 @@
 						skb_queue_tail(&bcs->rqueue, hw->rv_skb);
 						hw->rv_skb = skb;
 						bcs->event |= 1 << B_RCVBUFREADY;
-						queue_task(&bcs->tqueue, &tq_immediate);
+						schedule_work(&bcs->tqueue);
 					}
 				} else if (len > 0) {
 					/* Small packet received */
@@ -319,8 +316,7 @@
 						memcpy(skb_put(skb, len), hw->rv_skb->tail, len);
 						skb_queue_tail(&bcs->rqueue, skb);
 						bcs->event |= 1 << B_RCVBUFREADY;
-						queue_task(&bcs->tqueue, &tq_immediate);
-						mark_bh(IMMEDIATE_BH);
+						schedule_work(&bcs->tqueue);
 					}
 				} else {
 					/* Reception Error */
@@ -336,8 +332,7 @@
 				       RCV_BUFSIZE/RCV_BUFBLKS);
 				skb_queue_tail(&bcs->rqueue, skb);
 				bcs->event |= 1 << B_RCVBUFREADY;
-				queue_task(&bcs->tqueue, &tq_immediate);
-				mark_bh(IMMEDIATE_BH);
+				schedule_work(&bcs->tqueue);
 			}
 		}
 
@@ -422,12 +417,11 @@
 	}
 
 	bcs->hw.amd7930.tq_rcv.sync = 0;
-	bcs->hw.amd7930.tq_rcv.routine = (void (*)(void *)) &Bchan_rcv_bh;
-	bcs->hw.amd7930.tq_rcv.data = (void *) bcs;
+	INIT_WORK(&bcs->hw.amd7930.tq_rcv, (void (*)(void *)) &Bchan_rcv_bh,
+			(void *) bcs);
 
-	bcs->hw.amd7930.tq_xmt.sync = 0;
-	bcs->hw.amd7930.tq_xmt.routine = (void (*)(void *)) &Bchan_xmt_bh;
-	bcs->hw.amd7930.tq_xmt.data = (void *) bcs;
+	INIT_WORK(&bcs->hw.amd7930.tq_xmt, (void (*)(void *)) &Bchan_xmt_bh,
+			(void *) bcs);
 }
 
 static void
@@ -466,7 +460,7 @@
 amd7930_drecv_callback(void *arg, int error, unsigned int count)
 {
 	struct IsdnCardState *cs = (struct IsdnCardState *) arg;
-	static struct tq_struct task;
+	static struct work_struct task;
 	struct sk_buff *skb;
 
         /* NOTE: This function is called directly from an interrupt handler */
@@ -479,10 +473,8 @@
 			skb_queue_tail(&cs->rq, skb);
 		}
 
-		task.routine = (void *) DChannel_proc_rcv;
-		task.data = (void *) cs;
-		queue_task(&task, &tq_immediate);
-		mark_bh(IMMEDIATE_BH);
+		INIT_WORK(&task, (void *) DChannel_proc_rcv, (void *) cs);
+		schedule_work(&task);
 	}
 
 	if (cs->debug & L1_DEB_ISAC_FIFO) {
@@ -503,7 +495,7 @@
 amd7930_dxmit_callback(void *arg, int error)
 {
 	struct IsdnCardState *cs = (struct IsdnCardState *) arg;
-	static struct tq_struct task;
+	static struct work_struct task;
 
         /* NOTE: This function is called directly from an interrupt handler */
 
@@ -521,10 +513,8 @@
 
 	cs->tx_skb = NULL;
 
-	task.routine = (void *) DChannel_proc_xmt;
-	task.data = (void *) cs;
-	queue_task(&task, &tq_immediate);
-	mark_bh(IMMEDIATE_BH);
+	INIT_WORK(&task, (void *) DChannel_proc_xmt, (void *) cs);
+	schedule_work(&task);
 }
 
 static void
@@ -643,7 +633,7 @@
 static void
 amd7930_liu_callback(struct IsdnCardState *cs)
 {
-	static struct tq_struct task;
+	static struct work_struct task;
 
 	if (!cs)
 		return;
@@ -654,11 +644,8 @@
 		debugl1(cs, tmp);
 	}
 
-	task.sync = 0;
-	task.routine = (void *) &amd7930_new_ph;
-	task.data = (void *) cs;
-	queue_task(&task, &tq_immediate);
-	mark_bh(IMMEDIATE_BH);
+	INIT_WORK(&task, (void *) &amd7930_new_ph, (void *) cs);
+	schedule_work(&task);
 }
 
 void
diff -Nru a/drivers/isdn/hisax/amd7930_fn.c b/drivers/isdn/hisax/amd7930_fn.c
--- a/drivers/isdn/hisax/amd7930_fn.c	Mon Oct  7 11:25:36 2002
+++ b/drivers/isdn/hisax/amd7930_fn.c	Mon Oct  7 11:25:36 2002
@@ -277,8 +277,7 @@
         }
 
         test_and_set_bit(event, &cs->event);
-	queue_task(&cs->tqueue, &tq_immediate);
-	mark_bh(IMMEDIATE_BH);
+	schedule_work(&cs->tqueue);
 }
 
 static void
@@ -791,7 +790,7 @@
         cs->dc.amd7930.old_state = 0;
         cs->dc.amd7930.lmr1 = 0x40;
         cs->dc.amd7930.ph_command = Amd7930_ph_command;
-        cs->tqueue.routine = (void *) (void *) Amd7930_bh;
+	INIT_WORK(&cs->tqueue, (void *) (void *) Amd7930_bh, NULL);
 	cs->setstack_d = setstack_Amd7930;
 	cs->DC_Close = DC_Close_Amd7930;
 	cs->dbusytimer.function = (void *) dbusy_timer_handler;
diff -Nru a/drivers/isdn/hisax/avm_pci.c b/drivers/isdn/hisax/avm_pci.c
--- a/drivers/isdn/hisax/avm_pci.c	Mon Oct  7 11:25:30 2002
+++ b/drivers/isdn/hisax/avm_pci.c	Mon Oct  7 11:25:30 2002
@@ -200,8 +200,7 @@
 hdlc_sched_event(struct BCState *bcs, int event)
 {
 	bcs->event |= 1 << event;
-	queue_task(&bcs->tqueue, &tq_immediate);
-	mark_bh(IMMEDIATE_BH);
+	schedule_work(&bcs->tqueue);
 }
 
 void
diff -Nru a/drivers/isdn/hisax/config.c b/drivers/isdn/hisax/config.c
--- a/drivers/isdn/hisax/config.c	Mon Oct  7 11:25:38 2002
+++ b/drivers/isdn/hisax/config.c	Mon Oct  7 11:25:38 2002
@@ -22,7 +22,7 @@
 #include "hisax.h"
 #include <linux/module.h>
 #include <linux/kernel_stat.h>
-#include <linux/tqueue.h>
+#include <linux/workqueue.h>
 #include <linux/interrupt.h>
 #define HISAX_STATUS_BUFSIZE 4096
 #define INCLUDE_INLINE_FUNCS
@@ -1789,7 +1789,7 @@
 	hisax_d_if->cs = cs;
 	cs->hw.hisax_d_if = hisax_d_if;
 	cs->cardmsg = hisax_cardmsg;
-	cs->tqueue.routine = (void *) (void *) hisax_bh;
+	INIT_WORK(&cs->tqueue, (void *) (void *) hisax_bh, NULL);
 	cs->channel[0].d_st->l1.l2l1 = hisax_d_l2l1;
 	for (i = 0; i < 2; i++) {
 		cs->bcs[i].BC_SetStack = hisax_bc_setstack;
@@ -1818,8 +1818,7 @@
 static void hisax_sched_event(struct IsdnCardState *cs, int event)
 {
 	cs->event |= 1 << event;
-	queue_task(&cs->tqueue, &tq_immediate);
-	mark_bh(IMMEDIATE_BH);
+	schedule_work(&cs->tqueue);
 }
 
 static void hisax_bh(struct IsdnCardState *cs)
@@ -1845,8 +1844,7 @@
 static void hisax_b_sched_event(struct BCState *bcs, int event)
 {
 	bcs->event |= 1 << event;
-	queue_task(&bcs->tqueue, &tq_immediate);
-	mark_bh(IMMEDIATE_BH);
+	schedule_work(&bcs->tqueue);
 }
 
 static inline void D_L2L1(struct hisax_d_if *d_if, int pr, void *arg)
diff -Nru a/drivers/isdn/hisax/hfc_2bds0.c b/drivers/isdn/hisax/hfc_2bds0.c
--- a/drivers/isdn/hisax/hfc_2bds0.c	Mon Oct  7 11:25:25 2002
+++ b/drivers/isdn/hisax/hfc_2bds0.c	Mon Oct  7 11:25:25 2002
@@ -202,8 +202,7 @@
 hfc_sched_event(struct BCState *bcs, int event)
 {
 	bcs->event |= 1 << event;
-	queue_task(&bcs->tqueue, &tq_immediate);
-	mark_bh(IMMEDIATE_BH);
+	schedule_work(&bcs->tqueue);
 }
 
 static struct sk_buff
@@ -646,8 +645,7 @@
 sched_event_D(struct IsdnCardState *cs, int event)
 {
 	test_and_set_bit(event, &cs->event);
-	queue_task(&cs->tqueue, &tq_immediate);
-	mark_bh(IMMEDIATE_BH);
+	schedule_work(&cs->tqueue);
 }
 
 static
@@ -1129,7 +1127,7 @@
 	cs->dbusytimer.function = (void *) hfc_dbusy_timer;
 	cs->dbusytimer.data = (long) cs;
 	init_timer(&cs->dbusytimer);
-	cs->tqueue.routine = (void *) (void *) hfcd_bh;
+	INIT_WORK(&cs->tqueue, (void *) (void *) hfcd_bh, NULL);
 	if (!cs->hw.hfcD.send)
 		cs->hw.hfcD.send = init_send_hfcd(16);
 	if (!cs->bcs[0].hw.hfc.send)
diff -Nru a/drivers/isdn/hisax/hfc_2bs0.c b/drivers/isdn/hisax/hfc_2bs0.c
--- a/drivers/isdn/hisax/hfc_2bs0.c	Mon Oct  7 11:25:25 2002
+++ b/drivers/isdn/hisax/hfc_2bs0.c	Mon Oct  7 11:25:25 2002
@@ -86,8 +86,7 @@
 hfc_sched_event(struct BCState *bcs, int event)
 {
 	bcs->event |= 1 << event;
-	queue_task(&bcs->tqueue, &tq_immediate);
-	mark_bh(IMMEDIATE_BH);
+	schedule_work(&bcs->tqueue);
 }
 
 static void
diff -Nru a/drivers/isdn/hisax/hfc_pci.c b/drivers/isdn/hisax/hfc_pci.c
--- a/drivers/isdn/hisax/hfc_pci.c	Mon Oct  7 11:25:33 2002
+++ b/drivers/isdn/hisax/hfc_pci.c	Mon Oct  7 11:25:33 2002
@@ -194,8 +194,7 @@
 sched_event_D_pci(struct IsdnCardState *cs, int event)
 {
 	test_and_set_bit(event, &cs->event);
-	queue_task(&cs->tqueue, &tq_immediate);
-	mark_bh(IMMEDIATE_BH);
+	schedule_work(&cs->tqueue);
 }
 
 /*********************************/
@@ -205,8 +204,7 @@
 hfcpci_sched_event(struct BCState *bcs, int event)
 {
 	bcs->event |= 1 << event;
-	queue_task(&bcs->tqueue, &tq_immediate);
-	mark_bh(IMMEDIATE_BH);
+	schedule_work(&bcs->tqueue);
 }
 
 /************************************************/
@@ -1624,7 +1622,7 @@
 	cs->dbusytimer.function = (void *) hfcpci_dbusy_timer;
 	cs->dbusytimer.data = (long) cs;
 	init_timer(&cs->dbusytimer);
-	cs->tqueue.routine = (void *) (void *) hfcpci_bh;
+	INIT_WORK(&cs->tqueue, (void *) (void *) hfcpci_bh, NULL);
 	cs->BC_Send_Data = &hfcpci_send_data;
 	cs->bcs[0].BC_SetStack = setstack_2b;
 	cs->bcs[1].BC_SetStack = setstack_2b;
diff -Nru a/drivers/isdn/hisax/hfc_sx.c b/drivers/isdn/hisax/hfc_sx.c
--- a/drivers/isdn/hisax/hfc_sx.c	Mon Oct  7 11:25:25 2002
+++ b/drivers/isdn/hisax/hfc_sx.c	Mon Oct  7 11:25:25 2002
@@ -464,8 +464,7 @@
 sched_event_D_sx(struct IsdnCardState *cs, int event)
 {
 	test_and_set_bit(event, &cs->event);
-	queue_task(&cs->tqueue, &tq_immediate);
-	mark_bh(IMMEDIATE_BH);
+	schedule_work(&cs->tqueue);
 }
 
 /*********************************/
@@ -475,8 +474,7 @@
 hfcsx_sched_event(struct BCState *bcs, int event)
 {
 	bcs->event |= 1 << event;
-	queue_task(&bcs->tqueue, &tq_immediate);
-	mark_bh(IMMEDIATE_BH);
+	schedule_work(&bcs->tqueue);
 }
 
 /************************************************/
@@ -1412,7 +1410,7 @@
 	cs->dbusytimer.function = (void *) hfcsx_dbusy_timer;
 	cs->dbusytimer.data = (long) cs;
 	init_timer(&cs->dbusytimer);
-	cs->tqueue.routine = (void *) (void *) hfcsx_bh;
+	INIT_WORK(&cs->tqueue, (void *) (void *) hfcsx_bh, NULL);
 	cs->BC_Send_Data = &hfcsx_send_data;
 	cs->bcs[0].BC_SetStack = setstack_2b;
 	cs->bcs[1].BC_SetStack = setstack_2b;
diff -Nru a/drivers/isdn/hisax/hisax.h b/drivers/isdn/hisax/hisax.h
--- a/drivers/isdn/hisax/hisax.h	Mon Oct  7 11:25:36 2002
+++ b/drivers/isdn/hisax/hisax.h	Mon Oct  7 11:25:36 2002
@@ -453,8 +453,8 @@
 	int rv_buff_out;
 	struct sk_buff *rv_skb;
 	struct hdlc_state *hdlc_state;
-	struct tq_struct tq_rcv;
-	struct tq_struct tq_xmt;
+	struct work_struct tq_rcv;
+	struct work_struct tq_xmt;
 };
 
 #define BC_FLG_INIT	1
@@ -495,7 +495,7 @@
 	u_char *blog;
 	u_char *conmsg;
 	struct timer_list transbusy;
-	struct tq_struct tqueue;
+	struct work_struct tqueue;
 	unsigned long event;
 	int  (*BC_SetStack) (struct PStack *, struct BCState *);
 	void (*BC_Close) (struct BCState *);
@@ -954,7 +954,7 @@
 	struct sk_buff *tx_skb;
 	int tx_cnt;
 	long event;
-	struct tq_struct tqueue;
+	struct work_struct tqueue;
 	struct timer_list dbusytimer;
 #ifdef ERROR_STATISTIC
 	int err_crc;
diff -Nru a/drivers/isdn/hisax/hscx.c b/drivers/isdn/hisax/hscx.c
--- a/drivers/isdn/hisax/hscx.c	Mon Oct  7 11:25:25 2002
+++ b/drivers/isdn/hisax/hscx.c	Mon Oct  7 11:25:25 2002
@@ -95,8 +95,7 @@
 hscx_sched_event(struct BCState *bcs, int event)
 {
 	bcs->event |= 1 << event;
-	queue_task(&bcs->tqueue, &tq_immediate);
-	mark_bh(IMMEDIATE_BH);
+	schedule_work(&bcs->tqueue);
 }
 
 void
diff -Nru a/drivers/isdn/hisax/icc.c b/drivers/isdn/hisax/icc.c
--- a/drivers/isdn/hisax/icc.c	Mon Oct  7 11:25:25 2002
+++ b/drivers/isdn/hisax/icc.c	Mon Oct  7 11:25:25 2002
@@ -191,8 +191,7 @@
 icc_sched_event(struct IsdnCardState *cs, int event)
 {
 	test_and_set_bit(event, &cs->event);
-	queue_task(&cs->tqueue, &tq_immediate);
-	mark_bh(IMMEDIATE_BH);
+	schedule_work(&cs->tqueue);
 }
 
 void
@@ -625,7 +624,7 @@
 void __init
 initicc(struct IsdnCardState *cs)
 {
-	cs->tqueue.routine = (void *) (void *) icc_bh;
+	INIT_WORK(&cs->tqueue, (void *) (void *) icc_bh, NULL);
 	cs->setstack_d = setstack_icc;
 	cs->DC_Close = DC_Close_icc;
 	cs->dc.icc.mon_tx = NULL;
diff -Nru a/drivers/isdn/hisax/ipacx.c b/drivers/isdn/hisax/ipacx.c
--- a/drivers/isdn/hisax/ipacx.c	Mon Oct  7 11:25:35 2002
+++ b/drivers/isdn/hisax/ipacx.c	Mon Oct  7 11:25:35 2002
@@ -304,8 +304,7 @@
 dch_sched_event(struct IsdnCardState *cs, int event)
 {
 	set_bit(event, &cs->event);
-	queue_task(&cs->tqueue, &tq_immediate);
-	mark_bh(IMMEDIATE_BH);
+	schedule_work(&cs->tqueue);
 }
 
 //----------------------------------------------------------
@@ -510,7 +509,7 @@
 {
 	printk(KERN_INFO "HiSax: IPACX ISDN driver v0.1.0\n");
 
-	cs->tqueue.routine  = (void *)(void *) dch_bh;
+	INIT_WORK(&cs->tqueue, (void *)(void *) dch_bh);
 	cs->setstack_d      = dch_setstack;
   
 	cs->dbusytimer.function = (void *) dbusy_timer_handler;
@@ -593,8 +592,7 @@
 bch_sched_event(struct BCState *bcs, int event)
 {
 	bcs->event |= 1 << event;
-	queue_task(&bcs->tqueue, &tq_immediate);
-	mark_bh(IMMEDIATE_BH);
+	schedule_work(&bcs->tqueue);
 }
 
 //----------------------------------------------------------
diff -Nru a/drivers/isdn/hisax/isac.c b/drivers/isdn/hisax/isac.c
--- a/drivers/isdn/hisax/isac.c	Mon Oct  7 11:25:26 2002
+++ b/drivers/isdn/hisax/isac.c	Mon Oct  7 11:25:26 2002
@@ -195,8 +195,7 @@
 isac_sched_event(struct IsdnCardState *cs, int event)
 {
 	test_and_set_bit(event, &cs->event);
-	queue_task(&cs->tqueue, &tq_immediate);
-	mark_bh(IMMEDIATE_BH);
+	schedule_work(&cs->tqueue);
 }
 
 void
@@ -627,7 +626,7 @@
 void __devinit
 initisac(struct IsdnCardState *cs)
 {
-	cs->tqueue.routine = (void *) (void *) isac_bh;
+	INIT_WORK(&cs->tqueue, (void *) (void *) isac_bh, NULL);
 	cs->setstack_d = setstack_isac;
 	cs->DC_Close = DC_Close_isac;
 	cs->dc.isac.mon_tx = NULL;
diff -Nru a/drivers/isdn/hisax/isar.c b/drivers/isdn/hisax/isar.c
--- a/drivers/isdn/hisax/isar.c	Mon Oct  7 11:25:33 2002
+++ b/drivers/isdn/hisax/isar.c	Mon Oct  7 11:25:33 2002
@@ -447,8 +447,7 @@
 isar_sched_event(struct BCState *bcs, int event)
 {
 	bcs->event |= 1 << event;
-	queue_task(&bcs->tqueue, &tq_immediate);
-	mark_bh(IMMEDIATE_BH);
+	schedule_work(&bcs->tqueue);
 }
 
 static inline void
@@ -1561,7 +1560,7 @@
 		cs->bcs[i].mode = 0;
 		cs->bcs[i].hw.isar.dpath = i + 1;
 		modeisar(&cs->bcs[i], 0, 0);
-		cs->bcs[i].tqueue.routine = (void *) (void *) isar_bh;
+		INIT_WORK(&cs->bcs[i].tqueue, (void *) (void *) isar_bh, NULL);
 	}
 }
 
diff -Nru a/drivers/isdn/hisax/isdnl1.c b/drivers/isdn/hisax/isdnl1.c
--- a/drivers/isdn/hisax/isdnl1.c	Mon Oct  7 11:25:28 2002
+++ b/drivers/isdn/hisax/isdnl1.c	Mon Oct  7 11:25:28 2002
@@ -345,9 +345,7 @@
 
 	bcs->cs = cs;
 	bcs->channel = bc;
-	bcs->tqueue.sync = 0;
-	bcs->tqueue.routine = (void *) (void *) BChannel_bh;
-	bcs->tqueue.data = bcs;
+	INIT_WORK(&bcs->tqueue, (void *) (void *) BChannel_bh, bcs);
 	bcs->BC_SetStack = NULL;
 	bcs->BC_Close = NULL;
 	bcs->Flag = 0;
diff -Nru a/drivers/isdn/hisax/jade.c b/drivers/isdn/hisax/jade.c
--- a/drivers/isdn/hisax/jade.c	Mon Oct  7 11:25:29 2002
+++ b/drivers/isdn/hisax/jade.c	Mon Oct  7 11:25:29 2002
@@ -138,8 +138,7 @@
 jade_sched_event(struct BCState *bcs, int event)
 {
     bcs->event |= 1 << event;
-    queue_task(&bcs->tqueue, &tq_immediate);
-    mark_bh(IMMEDIATE_BH);
+    schedule_work(&bcs->tqueue);
 }
 
 static void
diff -Nru a/drivers/isdn/hisax/netjet.c b/drivers/isdn/hisax/netjet.c
--- a/drivers/isdn/hisax/netjet.c	Mon Oct  7 11:25:26 2002
+++ b/drivers/isdn/hisax/netjet.c	Mon Oct  7 11:25:26 2002
@@ -434,8 +434,7 @@
 		skb_queue_tail(&bcs->rqueue, skb);
 	}
 	bcs->event |= 1 << B_RCVBUFREADY;
-	queue_task(&bcs->tqueue, &tq_immediate);
-	mark_bh(IMMEDIATE_BH);
+	schedule_work(&bcs->tqueue);
 	
 	if (bcs->cs->debug & L1_DEB_RECEIVE_FRAME)
 		printframe(bcs->cs, bcs->hw.tiger.rcvbuf, count, "rec");
@@ -791,8 +790,7 @@
 							cnt - s_cnt);
 				}
 				bcs->event |= 1 << B_XMTBUFREADY;
-				queue_task(&bcs->tqueue, &tq_immediate);
-				mark_bh(IMMEDIATE_BH);
+				schedule_work(&bcs->tqueue);
 			}
 		}
 	} else if (test_and_clear_bit(BC_FLG_NOFRAME, &bcs->Flag)) {
diff -Nru a/drivers/isdn/hisax/w6692.c b/drivers/isdn/hisax/w6692.c
--- a/drivers/isdn/hisax/w6692.c	Mon Oct  7 11:25:38 2002
+++ b/drivers/isdn/hisax/w6692.c	Mon Oct  7 11:25:38 2002
@@ -135,16 +135,14 @@
 W6692_sched_event(struct IsdnCardState *cs, int event)
 {
 	test_and_set_bit(event, &cs->event);
-	queue_task(&cs->tqueue, &tq_immediate);
-	mark_bh(IMMEDIATE_BH);
+	schedule_work(&cs->tqueue);
 }
 
 static void
 W6692B_sched_event(struct BCState *bcs, int event)
 {
 	bcs->event |= 1 << event;
-	queue_task(&bcs->tqueue, &tq_immediate);
-	mark_bh(IMMEDIATE_BH);
+	schedule_work(&bcs->tqueue);
 }
 
 static void
@@ -889,7 +887,7 @@
 void __init initW6692(struct IsdnCardState *cs, int part)
 {
 	if (part & 1) {
-		cs->tqueue.routine = (void *) (void *) W6692_bh;
+		INIT_WORK(&cs->tqueue, (void *) (void *) W6692_bh, NULL);
 		cs->setstack_d = setstack_W6692;
 		cs->DC_Close = DC_Close_W6692;
 		cs->dbusytimer.function = (void *) dbusy_timer_handler;
diff -Nru a/drivers/isdn/hysdn/boardergo.c b/drivers/isdn/hysdn/boardergo.c
--- a/drivers/isdn/hysdn/boardergo.c	Mon Oct  7 11:25:35 2002
+++ b/drivers/isdn/hysdn/boardergo.c	Mon Oct  7 11:25:35 2002
@@ -59,10 +59,8 @@
 	b |= dpr->ToHyInt;	/* and for champ */
 
 	/* start kernel task immediately after leaving all interrupts */
-	if (!card->hw_lock) {
-		queue_task(&card->irq_queue, &tq_immediate);
-		mark_bh(IMMEDIATE_BH);
-	}
+	if (!card->hw_lock)
+		schedule_work(&card->irq_queue);
 	restore_flags(flags);
 }				/* ergo_interrupt */
 
@@ -177,8 +175,7 @@
 		card->err_log_state = ERRLOG_STATE_STOP;	/* request stop */
 
 	restore_flags(flags);
-	queue_task(&card->irq_queue, &tq_immediate);
-	mark_bh(IMMEDIATE_BH);
+	schedule_work(&card->irq_queue);
 }				/* ergo_set_errlog_state */
 
 /******************************************/
@@ -450,9 +447,7 @@
 	card->writebootseq = ergo_writebootseq;
 	card->waitpofready = ergo_waitpofready;
 	card->set_errlog_state = ergo_set_errlog_state;
-	card->irq_queue.sync = 0;
-	card->irq_queue.data = card;	/* init task queue for interrupt */
-	card->irq_queue.routine = (void *) (void *) ergo_irq_bh;
+	INIT_WORK(&card->irq_queue, (void *) (void *) ergo_irq_bh, card);
 
 	return (0);
 }				/* ergo_inithardware */
diff -Nru a/drivers/isdn/hysdn/hycapi.c b/drivers/isdn/hysdn/hycapi.c
--- a/drivers/isdn/hysdn/hycapi.c	Mon Oct  7 11:25:25 2002
+++ b/drivers/isdn/hysdn/hycapi.c	Mon Oct  7 11:25:25 2002
@@ -131,8 +131,7 @@
 	}
 	cinfo->tx_skb = skb;
 	spin_unlock_irq(&cinfo->lock);
-	queue_task(&card->irq_queue, &tq_immediate);
-	mark_bh(IMMEDIATE_BH);
+	schedule_work(&card->irq_queue);
 }
 
 /***********************************************************
diff -Nru a/drivers/isdn/hysdn/hysdn_defs.h b/drivers/isdn/hysdn/hysdn_defs.h
--- a/drivers/isdn/hysdn/hysdn_defs.h	Mon Oct  7 11:25:36 2002
+++ b/drivers/isdn/hysdn/hysdn_defs.h	Mon Oct  7 11:25:36 2002
@@ -17,7 +17,7 @@
 #include <linux/config.h>
 #include <linux/hysdn_if.h>
 #include <linux/interrupt.h>
-#include <linux/tqueue.h>
+#include <linux/workqueue.h>
 #include <linux/skbuff.h>
 
 /****************************/
@@ -173,7 +173,7 @@
 	void (*set_errlog_state) (struct HYSDN_CARD *, int);
 
 	/* interrupt handler + interrupt synchronisation */
-	struct tq_struct irq_queue;	/* interrupt task queue */
+	struct work_struct irq_queue;	/* interrupt task queue */
 	uchar volatile irq_enabled;	/* interrupt enabled if != 0 */
 	uchar volatile hw_lock;	/* hardware is currently locked -> no access */
 
diff -Nru a/drivers/isdn/hysdn/hysdn_net.c b/drivers/isdn/hysdn/hysdn_net.c
--- a/drivers/isdn/hysdn/hysdn_net.c	Mon Oct  7 11:25:25 2002
+++ b/drivers/isdn/hysdn/hysdn_net.c	Mon Oct  7 11:25:25 2002
@@ -169,8 +169,7 @@
 	spin_unlock_irq(&lp->lock);
 
 	if (lp->sk_count <= 3) {
-		queue_task(&((hysdn_card *) dev->priv)->irq_queue, &tq_immediate);
-		mark_bh(IMMEDIATE_BH);
+		schedule_work(&((hysdn_card *) dev->priv)->irq_queue);
 	}
 	return (0);		/* success */
 }				/* net_send_packet */
diff -Nru a/drivers/isdn/hysdn/hysdn_sched.c b/drivers/isdn/hysdn/hysdn_sched.c
--- a/drivers/isdn/hysdn/hysdn_sched.c	Mon Oct  7 11:25:26 2002
+++ b/drivers/isdn/hysdn/hysdn_sched.c	Mon Oct  7 11:25:26 2002
@@ -175,8 +175,7 @@
 	card->async_busy = 1;	/* request transfer */
 
 	/* now queue the task */
-	queue_task(&card->irq_queue, &tq_immediate);
-	mark_bh(IMMEDIATE_BH);
+	schedule_work(&card->irq_queue);
 	sti();
 
 	if (card->debug_flags & LOG_SCHED_ASYN)
diff -Nru a/drivers/isdn/i4l/Makefile b/drivers/isdn/i4l/Makefile
--- a/drivers/isdn/i4l/Makefile	Mon Oct  7 11:25:29 2002
+++ b/drivers/isdn/i4l/Makefile	Mon Oct  7 11:25:29 2002
@@ -11,9 +11,11 @@
 
 # Multipart objects.
 
-isdn-objs				:= isdn_net.o isdn_tty.o \
-					   isdn_v110.o isdn_common.o \
-					   isdn_ciscohdlck.o
+isdn-objs				:= isdn_net.o isdn_net_lib.o \
+					   isdn_fsm.o \
+					   isdn_ciscohdlck.o \
+					   isdn_tty.o isdn_v110.o \
+					   isdn_common.o \
 
 # Optional parts of multipart objects.
 
diff -Nru a/drivers/isdn/i4l/isdn_ciscohdlck.c b/drivers/isdn/i4l/isdn_ciscohdlck.c
--- a/drivers/isdn/i4l/isdn_ciscohdlck.c	Mon Oct  7 11:25:25 2002
+++ b/drivers/isdn/i4l/isdn_ciscohdlck.c	Mon Oct  7 11:25:25 2002
@@ -24,9 +24,8 @@
  * CISCO HDLC keepalive specific stuff
  */
 static struct sk_buff*
-isdn_net_ciscohdlck_alloc_skb(isdn_net_local *lp, int len)
+isdn_net_ciscohdlck_alloc_skb(isdn_net_dev *idev, int len)
 {
-	isdn_net_dev *idev = lp->netdev;
 	unsigned short hl = isdn_slot_hdrlen(idev->isdn_slot);
 	struct sk_buff *skb;
 
@@ -43,62 +42,59 @@
 static int
 isdn_ciscohdlck_dev_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
 {
-	isdn_net_local *lp = (isdn_net_local *) dev->priv;
-	isdn_net_dev *idev = lp->netdev;
+	isdn_net_local *mlp = dev->priv;
 	unsigned long len = 0;
-	unsigned long expires = 0;
-	int tmp = 0;
-	int period = lp->cisco_keepalive_period;
-	char debserint = lp->cisco_debserint;
+	int period;
+	char debserint;
 	int rc = 0;
 
-	if (lp->p_encap != ISDN_NET_ENCAP_CISCOHDLCK)
+	if (mlp->p_encap != ISDN_NET_ENCAP_CISCOHDLCK)
 		return -EINVAL;
 
 	switch (cmd) {
 		/* get/set keepalive period */
 		case SIOCGKEEPPERIOD:
-			len = (unsigned long)sizeof(lp->cisco_keepalive_period);
+			len = sizeof(mlp->cisco_keepalive_period);
 			if (copy_to_user((char *)ifr->ifr_ifru.ifru_data,
-				(int *)&lp->cisco_keepalive_period, len))
+					 (char *)&mlp->cisco_keepalive_period, len))
 				rc = -EFAULT;
 			break;
 		case SIOCSKEEPPERIOD:
-			tmp = lp->cisco_keepalive_period;
-			len = (unsigned long)sizeof(lp->cisco_keepalive_period);
-			if (copy_from_user((int *)&period,
-				(char *)ifr->ifr_ifru.ifru_data, len))
+			len = sizeof(mlp->cisco_keepalive_period);
+			if (copy_from_user((char *)&period,
+					   (char *)ifr->ifr_ifru.ifru_data, len)) {
 				rc = -EFAULT;
-			if ((period > 0) && (period <= 32767))
-				lp->cisco_keepalive_period = period;
-			else
+				break;
+			}
+			if (period <= 0 || period > 32767) {
 				rc = -EINVAL;
-			if (!rc && (tmp != lp->cisco_keepalive_period)) {
-				expires = (unsigned long)(jiffies +
-					lp->cisco_keepalive_period * HZ);
-				mod_timer(&lp->cisco_timer, expires);
-				printk(KERN_INFO "%s: Keepalive period set "
-					"to %d seconds.\n",
-					idev->name, lp->cisco_keepalive_period);
+				break;
 			}
+			mod_timer(&mlp->cisco_timer, jiffies + period * HZ);
+			printk(KERN_INFO "%s: Keepalive period set "
+			       "to %d seconds.\n", dev->name, period);
+			mlp->cisco_keepalive_period = period;
 			break;
 
 		/* get/set debugging */
 		case SIOCGDEBSERINT:
-			len = (unsigned long)sizeof(lp->cisco_debserint);
+			len = sizeof(mlp->cisco_debserint);
 			if (copy_to_user((char *)ifr->ifr_ifru.ifru_data,
-				(char *)&lp->cisco_debserint, len))
+					 (char *)&mlp->cisco_debserint, len))
 				rc = -EFAULT;
 			break;
 		case SIOCSDEBSERINT:
-			len = (unsigned long)sizeof(lp->cisco_debserint);
+			len = sizeof(mlp->cisco_debserint);
 			if (copy_from_user((char *)&debserint,
-				(char *)ifr->ifr_ifru.ifru_data, len))
+					   (char *)ifr->ifr_ifru.ifru_data, len)) {
 				rc = -EFAULT;
-			if ((debserint >= 0) && (debserint <= 64))
-				lp->cisco_debserint = debserint;
-			else
+				break;
+			}
+			if (debserint < 0 || debserint > 64) {
 				rc = -EINVAL;
+				break;
+			}
+			mlp->cisco_debserint = debserint;
 			break;
 
 		default:
@@ -112,42 +108,47 @@
 static void
 isdn_net_ciscohdlck_slarp_send_keepalive(unsigned long data)
 {
-	isdn_net_local *lp = (isdn_net_local *) data;
-	isdn_net_dev *idev = lp->netdev;
+	isdn_net_local *mlp = (isdn_net_local *) data;
+	isdn_net_dev *idev;
 	struct sk_buff *skb;
 	unsigned char *p;
-	unsigned long last_cisco_myseq = lp->cisco_myseq;
+	unsigned long last_cisco_myseq = mlp->cisco_myseq;
 	int myseq_diff = 0;
 
-	lp->cisco_myseq++;
+	if (list_empty(&mlp->online)) {
+		isdn_BUG();
+		return;
+	}
+	idev = list_entry(mlp->online.next, isdn_net_dev, online);
+	mlp->cisco_myseq++;
 
-	myseq_diff = (lp->cisco_myseq - lp->cisco_mineseen);
-	if ((lp->cisco_line_state) && ((myseq_diff >= 3)||(myseq_diff <= -3))) {
+	myseq_diff = (mlp->cisco_myseq - mlp->cisco_mineseen);
+	if (mlp->cisco_line_state && (myseq_diff >= 3 || myseq_diff <= -3)) {
 		/* line up -> down */
-		lp->cisco_line_state = 0;
+		mlp->cisco_line_state = 0;
 		printk (KERN_WARNING
 				"UPDOWN: Line protocol on Interface %s,"
 				" changed state to down\n", idev->name);
 		/* should stop routing higher-level data accross */
-	} else if ((!lp->cisco_line_state) &&
-		(myseq_diff >= 0) && (myseq_diff <= 2)) {
+	} else if (!mlp->cisco_line_state &&
+		myseq_diff >= 0 && myseq_diff <= 2) {
 		/* line down -> up */
-		lp->cisco_line_state = 1;
+		mlp->cisco_line_state = 1;
 		printk (KERN_WARNING
 				"UPDOWN: Line protocol on Interface %s,"
 				" changed state to up\n", idev->name);
 		/* restart routing higher-level data accross */
 	}
 
-	if (lp->cisco_debserint)
+	if (mlp->cisco_debserint)
 		printk (KERN_DEBUG "%s: HDLC "
 			"myseq %lu, mineseen %lu%c, yourseen %lu, %s\n",
-			idev->name, last_cisco_myseq, lp->cisco_mineseen,
-			(last_cisco_myseq == lp->cisco_mineseen) ? '*' : 040,
-			lp->cisco_yourseq,
-			(lp->cisco_line_state) ? "line up" : "line down");
+			idev->name, last_cisco_myseq, mlp->cisco_mineseen,
+			(last_cisco_myseq == mlp->cisco_mineseen) ? '*' : 040,
+			mlp->cisco_yourseq,
+			(mlp->cisco_line_state) ? "line up" : "line down");
 
-	skb = isdn_net_ciscohdlck_alloc_skb(lp, 4 + 14);
+	skb = isdn_net_ciscohdlck_alloc_skb(idev, 4 + 14);
 	if (!skb)
 		return;
 
@@ -160,24 +161,29 @@
 
 	/* slarp keepalive */
 	p += put_u32(p, CISCO_SLARP_KEEPALIVE);
-	p += put_u32(p, lp->cisco_myseq);
-	p += put_u32(p, lp->cisco_yourseq);
+	p += put_u32(p, mlp->cisco_myseq);
+	p += put_u32(p, mlp->cisco_yourseq);
 	p += put_u16(p, 0xffff); // reliablity, always 0xffff
 
-	isdn_net_write_super(lp, skb);
+	isdn_net_write_super(idev, skb);
 
-	lp->cisco_timer.expires = jiffies + lp->cisco_keepalive_period * HZ;
-	
-	add_timer(&lp->cisco_timer);
+	mod_timer(&mlp->cisco_timer, jiffies + mlp->cisco_keepalive_period * HZ);
 }
 
 static void
-isdn_net_ciscohdlck_slarp_send_request(isdn_net_local *lp)
+isdn_net_ciscohdlck_slarp_send_request(isdn_net_local *mlp)
 {
+	isdn_net_dev *idev;
 	struct sk_buff *skb;
 	unsigned char *p;
 
-	skb = isdn_net_ciscohdlck_alloc_skb(lp, 4 + 14);
+	if (list_empty(&mlp->online)) {
+		isdn_BUG();
+		return;
+	}
+	idev = list_entry(mlp->online.next, isdn_net_dev, online);
+
+	skb = isdn_net_ciscohdlck_alloc_skb(idev, 4 + 14);
 	if (!skb)
 		return;
 
@@ -194,12 +200,14 @@
 	p += put_u32(p, 0); // netmask
 	p += put_u16(p, 0); // unused
 
-	isdn_net_write_super(lp, skb);
+	isdn_net_write_super(idev, skb);
 }
 
 static void 
-isdn_ciscohdlck_connected(isdn_net_local *lp)
+isdn_ciscohdlck_connected(isdn_net_dev *idev)
 {
+	isdn_net_local *lp = idev->mlp;
+
 	lp->cisco_myseq = 0;
 	lp->cisco_mineseen = 0;
 	lp->cisco_yourseq = 0;
@@ -218,27 +226,30 @@
 		lp->cisco_timer.expires = jiffies + lp->cisco_keepalive_period * HZ;
 		add_timer(&lp->cisco_timer);
 	}
-	isdn_net_device_wake_queue(lp);
+	netif_wake_queue(&lp->dev);
 }
 
 static void 
-isdn_ciscohdlck_disconnected(isdn_net_local *lp)
+isdn_ciscohdlck_disconnected(isdn_net_dev *idev)
 {
+	isdn_net_local *lp = idev->mlp;
+
 	if (lp->p_encap == ISDN_NET_ENCAP_CISCOHDLCK) {
 		del_timer(&lp->cisco_timer);
 	}
 }
 
 static void
-isdn_net_ciscohdlck_slarp_send_reply(isdn_net_local *lp)
+isdn_net_ciscohdlck_slarp_send_reply(isdn_net_dev *idev)
 {
+	isdn_net_local *mlp = idev->mlp;
 	struct sk_buff *skb;
 	unsigned char *p;
 	struct in_device *in_dev = NULL;
 	u32 addr = 0;		/* local ipv4 address */
 	u32 mask = 0;		/* local netmask */
 
-	if ((in_dev = lp->netdev->dev.ip_ptr) != NULL) {
+	if ((in_dev = mlp->dev.ip_ptr) != NULL) {
 		/* take primary(first) address of interface */
 		struct in_ifaddr *ifa = in_dev->ifa_list;
 		if (ifa != NULL) {
@@ -247,7 +258,7 @@
 		}
 	}
 
-	skb = isdn_net_ciscohdlck_alloc_skb(lp, 4 + 14);
+	skb = isdn_net_ciscohdlck_alloc_skb(idev, 4 + 14);
 	if (!skb)
 		return;
 
@@ -265,13 +276,13 @@
 	p += put_u32(p, mask);	// netmask
 	p += put_u16(p, 0);	// unused
 
-	isdn_net_write_super(lp, skb);
+	isdn_net_write_super(idev, skb);
 }
 
 static void
-isdn_net_ciscohdlck_slarp_in(isdn_net_local *lp, struct sk_buff *skb)
+isdn_net_ciscohdlck_slarp_in(isdn_net_dev *idev, struct sk_buff *skb)
 {
-	isdn_net_dev *idev = lp->netdev;
+	isdn_net_local *mlp = idev->mlp;
 	unsigned char *p;
 	int period;
 	u32 code;
@@ -288,8 +299,8 @@
 	
 	switch (code) {
 	case CISCO_SLARP_REQUEST:
-		lp->cisco_yourseq = 0;
-		isdn_net_ciscohdlck_slarp_send_reply(lp);
+		mlp->cisco_yourseq = 0;
+		isdn_net_ciscohdlck_slarp_send_reply(idev);
 		break;
 	case CISCO_SLARP_REPLY:
 		addr = ntohl(*(u32 *)p);
@@ -315,30 +326,29 @@
 				 HIPQUAD(addr), HIPQUAD(mask));
 		break;
 	case CISCO_SLARP_KEEPALIVE:
-		period = (int)((jiffies - lp->cisco_last_slarp_in
+		period = (int)((jiffies - mlp->cisco_last_slarp_in
 				+ HZ/2 - 1) / HZ);
-		if (lp->cisco_debserint &&
-				(period != lp->cisco_keepalive_period) &&
-				lp->cisco_last_slarp_in) {
+		if (mlp->cisco_debserint &&
+				(period != mlp->cisco_keepalive_period) &&
+				mlp->cisco_last_slarp_in) {
 			printk(KERN_DEBUG "%s: Keepalive period mismatch - "
 				"is %d but should be %d.\n",
-				idev->name, period, lp->cisco_keepalive_period);
+				idev->name, period, mlp->cisco_keepalive_period);
 		}
-		lp->cisco_last_slarp_in = jiffies;
+		mlp->cisco_last_slarp_in = jiffies;
 		p += get_u32(p, &my_seq);
 		p += get_u32(p, &your_seq);
 		p += get_u16(p, &unused);
-		lp->cisco_yourseq = my_seq;
-		lp->cisco_mineseen = your_seq;
+		mlp->cisco_yourseq = my_seq;
+		mlp->cisco_mineseen = your_seq;
 		break;
 	}
 }
 
 static void 
-isdn_ciscohdlck_receive(isdn_net_dev *idev, isdn_net_local *olp,
+isdn_ciscohdlck_receive(isdn_net_local *lp, isdn_net_dev *idev,
 			struct sk_buff *skb)
 {
-	isdn_net_local *lp = &idev->local;
 	unsigned char *p;
  	u8 addr;
  	u8 ctrl;
@@ -362,7 +372,7 @@
 
 	switch (type) {
 	case CISCO_TYPE_SLARP:
-		isdn_net_ciscohdlck_slarp_in(lp, skb);
+		isdn_net_ciscohdlck_slarp_in(idev, skb);
 		goto out_free;
 	case CISCO_TYPE_CDP:
 		if (lp->cisco_debserint)
@@ -371,7 +381,7 @@
 		goto out_free;
 	default:
 		/* no special cisco protocol */
-		isdn_net_reset_huptimer(idev, olp->netdev);
+		idev->huptimer = 0;
 		skb->protocol = htons(type);
 		netif_rx(skb);
 		return;
@@ -396,6 +406,7 @@
 }
 
 struct isdn_netif_ops ciscohdlck_ops = {
+	.hard_start_xmit     = isdn_net_start_xmit,
 	.hard_header         = isdn_ciscohdlck_header,
 	.do_ioctl            = isdn_ciscohdlck_dev_ioctl,
 	.flags               = IFF_NOARP | IFF_POINTOPOINT,
diff -Nru a/drivers/isdn/i4l/isdn_common.c b/drivers/isdn/i4l/isdn_common.c
--- a/drivers/isdn/i4l/isdn_common.c	Mon Oct  7 11:25:30 2002
+++ b/drivers/isdn/i4l/isdn_common.c	Mon Oct  7 11:25:30 2002
@@ -46,8 +46,7 @@
 	unsigned long     obytes;              /* Statistics outgoing bytes  */
 	struct isdn_v110  iv110;               /* For V.110                  */
 	int               m_idx;               /* Index for mdm....          */
-	isdn_net_dev      *rx_netdev;          /* rx netdev-pointers         */
-	isdn_net_dev      *st_netdev;          /* stat netdev-pointers       */
+	isdn_net_dev      *idev;               /* pointer to isdn_net_dev    */
 };
 
 static struct isdn_slot slot[ISDN_MAX_CHANNELS]; 
@@ -508,7 +507,7 @@
 					list_for_each(l, &isdn_net_devs) {
 						isdn_net_dev *p = list_entry(l, isdn_net_dev, global_list);
 						if (p->isdn_slot == i) {
-							strcpy(cmd.parm.setup.eazmsn, p->local.msn);
+							strcpy(cmd.parm.setup.eazmsn, p->mlp->msn);
 							isdn_slot_command(i, ISDN_CMD_ACCEPTD, &cmd);
 							retval = 1;
 							break;
@@ -1010,19 +1009,6 @@
 isdn_status_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg)
 {
 	int ret;
-	union iocpar {
-		char name[10];
-		char bname[22];
-		isdn_ioctl_struct iocts;
-		isdn_net_ioctl_phone phone;
-		isdn_net_ioctl_cfg cfg;
-	} iocpar;
-
-#define name  iocpar.name
-#define bname iocpar.bname
-#define iocts iocpar.iocts
-#define phone iocpar.phone
-#define cfg   iocpar.cfg
 
 	switch (cmd) {
 	case IIOCGETDVR:
@@ -1044,26 +1030,11 @@
 		} else
 			return -EINVAL;
 		break;
-#ifdef CONFIG_NETDEVICES
 	case IIOCNETGPN:
-		/* Get peer phone number of a connected 
-		 * isdn network interface */
-		if (arg) {
-			if (copy_from_user((char *) &phone, (char *) arg, sizeof(phone)))
-				return -EFAULT;
-			return isdn_net_getpeer(&phone, (isdn_net_ioctl_phone *) arg);
-		} else
-			return -EINVAL;
-#endif
+		return isdn_net_ioctl(inode, file, cmd, arg);
 	default:
 		return -EINVAL;
 	}
-
-#undef name
-#undef bname
-#undef iocts
-#undef phone
-#undef cfg
 }
 
 static struct file_operations isdn_status_fops =
@@ -1221,19 +1192,15 @@
 	int ret;
 	int i;
 	char *p;
-	union iocpar {
-		char name[10];
+	/* save stack space */
+	union {
 		char bname[20];
 		isdn_ioctl_struct iocts;
-		isdn_net_ioctl_phone phone;
-		isdn_net_ioctl_cfg cfg;
 	} iocpar;
 
-#define name  iocpar.name
-#define bname iocpar.bname
 #define iocts iocpar.iocts
-#define phone iocpar.phone
-#define cfg   iocpar.cfg
+#define bname iocpar.bname
+
 /*
  * isdn net devices manage lots of configuration variables as linked lists.
  * Those lists must only be manipulated from user space. Some of the ioctl's
@@ -1242,134 +1209,19 @@
  * are serialized by means of a semaphore.
  */
 	switch (cmd) {
-	case IIOCNETDWRSET:
-		printk(KERN_INFO "INFO: ISDN_DW_ABC_EXTENSION not enabled\n");
-		return(-EINVAL);
-	case IIOCNETLCR:
-		printk(KERN_INFO "INFO: ISDN_ABC_LCR_SUPPORT not enabled\n");
-		return -ENODEV;
-#ifdef CONFIG_NETDEVICES
 	case IIOCNETAIF:
-		/* Add a network-interface */
-		if (copy_from_user(name, (char *) arg, sizeof(name) - 1))
-			return -EFAULT;
-		name[sizeof(name)-1] = 0;
-		ret = down_interruptible(&dev->sem);
-		if (ret)
-			return ret;
-		ret = isdn_net_new(name, NULL);
-		up(&dev->sem);
-		return ret;
 	case IIOCNETASL:
-		/* Add a slave to a network-interface */
-		if (copy_from_user(bname, (char *) arg, sizeof(bname) - 1))
-			return -EFAULT;
-		bname[sizeof(bname)-1] = 0;
-		ret = down_interruptible(&dev->sem);
-		if (ret)
-			return ret;
-		ret = isdn_net_newslave(bname);
-		up(&dev->sem);
-		return ret;
 	case IIOCNETDIF:
-		/* Delete a network-interface */
-		if (arg) {
-			if (copy_from_user(name, (char *) arg, sizeof(name)))
-				return -EFAULT;
-			ret = down_interruptible(&dev->sem);
-			if( ret ) return ret;
-			ret = isdn_net_rm(name);
-			up(&dev->sem);
-			return ret;
-		} else
-			return -EINVAL;
 	case IIOCNETSCF:
-		/* Set configurable parameters of a network-interface */
-		if (arg) {
-			if (copy_from_user((char *) &cfg, (char *) arg, sizeof(cfg)))
-				return -EFAULT;
-			return isdn_net_setcfg(&cfg);
-		} else
-			return -EINVAL;
 	case IIOCNETGCF:
-		/* Get configurable parameters of a network-interface */
-		if (arg) {
-			if (copy_from_user((char *) &cfg, (char *) arg, sizeof(cfg)))
-				return -EFAULT;
-			if (!(ret = isdn_net_getcfg(&cfg))) {
-				if (copy_to_user((char *) arg, (char *) &cfg, sizeof(cfg)))
-					return -EFAULT;
-			}
-			return ret;
-		} else
-			return -EINVAL;
 	case IIOCNETANM:
-		/* Add a phone-number to a network-interface */
-		if (arg) {
-			if (copy_from_user((char *) &phone, (char *) arg, sizeof(phone)))
-				return -EFAULT;
-			ret = down_interruptible(&dev->sem);
-			if( ret ) return ret;
-			ret = isdn_net_addphone(&phone);
-			up(&dev->sem);
-			return ret;
-		} else
-			return -EINVAL;
 	case IIOCNETGNM:
-		/* Get list of phone-numbers of a network-interface */
-		if (arg) {
-			if (copy_from_user((char *) &phone, (char *) arg, sizeof(phone)))
-				return -EFAULT;
-			ret = down_interruptible(&dev->sem);
-			if( ret ) return ret;
-			ret = isdn_net_getphones(&phone, (char *) arg);
-			up(&dev->sem);
-			return ret;
-		} else
-			return -EINVAL;
 	case IIOCNETDNM:
-		/* Delete a phone-number of a network-interface */
-		if (arg) {
-			if (copy_from_user((char *) &phone, (char *) arg, sizeof(phone)))
-				return -EFAULT;
-			ret = down_interruptible(&dev->sem);
-			if( ret ) return ret;
-			ret = isdn_net_delphone(&phone);
-			up(&dev->sem);
-			return ret;
-		} else
-			return -EINVAL;
 	case IIOCNETDIL:
-		/* Force dialing of a network-interface */
-		if (arg) {
-			if (copy_from_user(name, (char *) arg, sizeof(name)))
-				return -EFAULT;
-			return isdn_net_force_dial(name);
-		} else
-			return -EINVAL;
-#ifdef CONFIG_ISDN_PPP
 	case IIOCNETALN:
-		if (!arg)
-			return -EINVAL;
-		if (copy_from_user(name, (char *) arg, sizeof(name)))
-			return -EFAULT;
-		return isdn_ppp_dial_slave(name);
 	case IIOCNETDLN:
-		if (!arg)
-			return -EINVAL;
-		if (copy_from_user(name, (char *) arg, sizeof(name)))
-			return -EFAULT;
-		return isdn_ppp_hangup_slave(name);
-#endif
 	case IIOCNETHUP:
-		/* Force hangup of a network-interface */
-		if (!arg)
-			return -EINVAL;
-		if (copy_from_user(name, (char *) arg, sizeof(name)))
-			return -EFAULT;
-		return isdn_net_force_hangup(name);
-		break;
-#endif                          /* CONFIG_NETDEVICES */
+		return isdn_net_ioctl(inode, file, cmd, arg);
 	case IIOCSETVER:
 		dev->net_verbose = arg;
 		printk(KERN_INFO "isdn: Verbose-Level is %d\n", dev->net_verbose);
@@ -1577,12 +1429,8 @@
 		} else
 			return -EINVAL;
 	}
-
-#undef name
-#undef bname
 #undef iocts
-#undef phone
-#undef cfg
+#undef bname
 }
 
 static struct file_operations isdn_ctrl_fops =
@@ -2218,35 +2066,19 @@
 }
 
 void
-isdn_slot_set_rx_netdev(int sl, isdn_net_dev *nd)
-{
-	BUG_ON(sl < 0);
-
-	slot[sl].rx_netdev = nd;
-}
-
-isdn_net_dev *
-isdn_slot_rx_netdev(int sl)
-{
-	BUG_ON(sl < 0);
-
-	return slot[sl].rx_netdev;
-}
-
-void
-isdn_slot_set_st_netdev(int sl, isdn_net_dev *nd)
+isdn_slot_set_idev(int sl, isdn_net_dev *idev)
 {
 	BUG_ON(sl < 0);
 
-	slot[sl].st_netdev = nd;
+	slot[sl].idev = idev;
 }
 
 isdn_net_dev *
-isdn_slot_st_netdev(int sl)
+isdn_slot_idev(int sl)
 {
 	BUG_ON(sl < 0);
 
-	return slot[sl].st_netdev;
+	return slot[sl].idev;
 }
 
 int
@@ -2430,7 +2262,7 @@
 	printk("\n");
 #endif
 	isdn_info_update();
-	isdn_net_init_module();
+	isdn_net_init();
 	return 0;
 
  err_tty_modem:
@@ -2456,8 +2288,7 @@
 #endif
 	save_flags(flags);
 	cli();
-	if (isdn_net_rmall() < 0)
-		BUG();
+	isdn_net_exit();
 
 	isdn_tty_exit();
 	if (unregister_chrdev(ISDN_MAJOR, "isdn"))
diff -Nru a/drivers/isdn/i4l/isdn_common.h b/drivers/isdn/i4l/isdn_common.h
--- a/drivers/isdn/i4l/isdn_common.h	Mon Oct  7 11:25:25 2002
+++ b/drivers/isdn/i4l/isdn_common.h	Mon Oct  7 11:25:25 2002
@@ -22,11 +22,11 @@
 #undef  ISDN_DEBUG_MODEM_DUMP
 #undef  ISDN_DEBUG_MODEM_VOICE
 #undef  ISDN_DEBUG_AT
-#undef  ISDN_DEBUG_NET_DUMP
-#undef  ISDN_DEBUG_NET_DIAL
-#undef  ISDN_DEBUG_NET_ICALL
-#undef  ISDN_DEBUG_STATCALLB
-#undef  ISDN_DEBUG_COMMAND
+#define  ISDN_DEBUG_NET_DUMP
+#define  ISDN_DEBUG_NET_DIAL
+#define  ISDN_DEBUG_NET_ICALL
+#define  ISDN_DEBUG_STATCALLB
+#define  ISDN_DEBUG_COMMAND
 
 #ifdef ISDN_DEBUG_NET_DIAL
 #define dbg_net_dial(arg...) printk(KERN_DEBUG arg)
@@ -52,6 +52,8 @@
 
 #define HERE printk("%s:%d (%s)\n", __FILE__, __LINE__, __FUNCTION__)
 
+extern struct list_head isdn_net_devs;
+
 /* Prototypes */
 extern void isdn_MOD_INC_USE_COUNT(void);
 extern void isdn_MOD_DEC_USE_COUNT(void);
@@ -82,8 +84,6 @@
 	unsigned char *phone;
 };
 
-extern struct list_head isdn_net_devs;
-
 extern int   isdn_get_free_slot(int, int, int, int, int, char *);
 extern void  isdn_slot_free(int slot, int usage);
 extern void  isdn_slot_all_eaz(int slot);
@@ -100,8 +100,6 @@
 extern char *isdn_slot_num(int slot);
 extern int   isdn_slot_m_idx(int slot);
 extern void  isdn_slot_set_m_idx(int slot, int midx);
-extern void  isdn_slot_set_rx_netdev(int sl, isdn_net_dev *nd);
-extern void  isdn_slot_set_st_netdev(int sl, isdn_net_dev *nd);
-extern isdn_net_dev *isdn_slot_rx_netdev(int sl);
-extern isdn_net_dev *isdn_slot_st_netdev(int sl);
+extern void  isdn_slot_set_idev(int sl, isdn_net_dev *);
+extern isdn_net_dev *isdn_slot_idev(int sl);
 extern int   isdn_hard_header_len(void);
diff -Nru a/drivers/isdn/i4l/isdn_concap.c b/drivers/isdn/i4l/isdn_concap.c
--- a/drivers/isdn/i4l/isdn_concap.c	Mon Oct  7 11:25:29 2002
+++ b/drivers/isdn/i4l/isdn_concap.c	Mon Oct  7 11:25:29 2002
@@ -39,7 +39,8 @@
    */
 
 
-int isdn_concap_dl_data_req(struct concap_proto *concap, struct sk_buff *skb)
+static int
+isdn_concap_dl_data_req(struct concap_proto *concap, struct sk_buff *skb)
 {
 	struct net_device *ndev = concap -> net_dev;
 	isdn_net_dev *nd = ((isdn_net_local *) ndev->priv)->netdev;
@@ -58,7 +59,8 @@
 }
 
 
-int isdn_concap_dl_connect_req(struct concap_proto *concap)
+static int
+isdn_concap_dl_connect_req(struct concap_proto *concap)
 {
 	struct net_device *ndev = concap -> net_dev;
 	isdn_net_local *lp = (isdn_net_local *) ndev->priv;
@@ -71,7 +73,8 @@
 	return ret;
 }
 
-int isdn_concap_dl_disconn_req(struct concap_proto *concap)
+static int
+isdn_concap_dl_disconn_req(struct concap_proto *concap)
 {
 	IX25DEBUG( "isdn_concap_dl_disconn_req: %s \n", concap -> net_dev -> name);
 
@@ -98,7 +101,8 @@
    this sourcefile does not need to include any protocol specific header
    files. For now:
    */
-struct concap_proto * isdn_concap_new( int encap )
+struct concap_proto *
+isdn_concap_new( int encap )
 {
 	switch ( encap ) {
 	case ISDN_NET_ENCAP_X25IFACE:
@@ -158,7 +162,7 @@
 		pops -> disconn_ind(cprot);
 }
 
-int
+static int
 isdn_x25_start_xmit(struct sk_buff *skb, struct net_device *dev)
 {
 /* At this point hard_start_xmit() passes control to the encapsulation
@@ -237,13 +241,8 @@
 	restore_flags(flags);
 }
 
-void isdn_x25_realrm(isdn_net_dev *p)
-{
-	if( p -> cprot && p -> cprot -> pops )
-		p -> cprot -> pops -> proto_del ( p -> cprot );
-}
-
 struct isdn_netif_ops isdn_x25_ops = {
+	.hard_start_xmit     = isdn_x25_start_xmit,
 	.flags               = IFF_NOARP | IFF_POINTOPOINT,
 	.type                = ARPHRD_X25,
 	.receive             = isdn_x25_receive,
diff -Nru a/drivers/isdn/i4l/isdn_concap.h b/drivers/isdn/i4l/isdn_concap.h
--- a/drivers/isdn/i4l/isdn_concap.h	Mon Oct  7 11:25:36 2002
+++ b/drivers/isdn/i4l/isdn_concap.h	Mon Oct  7 11:25:36 2002
@@ -12,18 +12,4 @@
 
 struct concap_proto *isdn_concap_new(int);
 
-#ifdef CONFIG_ISDN_X25
-
 extern struct isdn_netif_ops isdn_x25_ops;
-
-int  isdn_x25_start_xmit(struct sk_buff *skb, struct net_device *dev);
-
-#else
-
-static inline int
-isdn_x25_start_xmit(struct sk_buff *skb, struct net_device *dev)
-{
-	return 0;
-}
-
-#endif
diff -Nru a/drivers/isdn/i4l/isdn_fsm.c b/drivers/isdn/i4l/isdn_fsm.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/isdn/i4l/isdn_fsm.c	Mon Oct  7 11:26:06 2002
@@ -0,0 +1,169 @@
+/* $Id: fsm.c,v 1.14.6.4 2001/09/23 22:24:47 kai Exp $
+ *
+ * Finite state machine
+ *
+ * Author       Karsten Keil
+ * Copyright    by Karsten Keil      <keil@isdn4linux.de>
+ *              by Kai Germaschewski <kai.germaschewski@gmx.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * Thanks to    Jan den Ouden
+ *              Fritz Elfert
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include "isdn_fsm.h"
+
+int
+fsm_new(struct fsm *fsm)
+{
+	int i;
+	int size = sizeof(fsm_fn) * fsm->st_cnt * fsm->ev_cnt;
+
+	fsm->jumpmatrix = kmalloc(size, GFP_KERNEL);
+	if (!fsm->jumpmatrix)
+		return -ENOMEM;
+
+	memset(fsm->jumpmatrix, 0, size);
+
+	for (i = 0; i < fsm->fn_cnt; i++) {
+		if (fsm->fn_tbl[i].st >= fsm->st_cnt || 
+		    fsm->fn_tbl[i].ev >= fsm->ev_cnt) {
+			printk(KERN_ERR "FsmNew Error line %d st(%d/%d) ev(%d/%d)\n", i,
+			       fsm->fn_tbl[i].st, fsm->st_cnt, 
+			       fsm->fn_tbl[i].ev, fsm->ev_cnt);
+			continue;
+		}
+		fsm->jumpmatrix[fsm->st_cnt * fsm->fn_tbl[i].ev + fsm->fn_tbl[i].st] = fsm->fn_tbl[i].fn;
+	}
+	return 0;
+}
+
+void
+fsm_free(struct fsm *fsm)
+{
+	kfree(fsm->jumpmatrix);
+}
+
+int
+fsm_event(struct fsm_inst *fi, int event, void *arg)
+{
+	fsm_fn fn;
+
+	if (fi->state >= fi->fsm->st_cnt || 
+	    event >= fi->fsm->ev_cnt) {
+		printk(KERN_ERR "FsmEvent Error st(%d/%d) ev(%d/%d)\n",
+		       fi->state, fi->fsm->st_cnt,event, 
+		       fi->fsm->ev_cnt);
+		return -EINVAL;
+	}
+	fn = fi->fsm->jumpmatrix[fi->fsm->st_cnt * event + fi->state];
+	if (!fn) {
+		if (fi->debug)
+			fi->printdebug(fi, "State %s Event %s no routine",
+				       fi->fsm->st_str[fi->state],
+				       fi->fsm->ev_str[event]);
+		return -ESRCH;
+	}
+	if (fi->debug)
+		fi->printdebug(fi, "State %s Event %s",
+			       fi->fsm->st_str[fi->state],
+			       fi->fsm->ev_str[event]);
+
+	return fn(fi, event, arg);
+}
+
+void
+fsm_change_state(struct fsm_inst *fi, int newstate)
+{
+	fi->state = newstate;
+	if (fi->debug)
+		fi->printdebug(fi, "ChangeState %s",
+			       fi->fsm->st_str[newstate]);
+}
+
+#if 0
+static void
+FsmExpireTimer(struct FsmTimer *ft)
+{
+#if FSM_TIMER_DEBUG
+	if (ft->fi->debug)
+		ft->fi->printdebug(ft->fi, "FsmExpireTimer %lx", (long) ft);
+#endif
+	FsmEvent(ft->fi, ft->event, ft->arg);
+}
+
+void
+FsmInitTimer(struct FsmInst *fi, struct FsmTimer *ft)
+{
+	ft->fi = fi;
+	ft->tl.function = (void *) FsmExpireTimer;
+	ft->tl.data = (long) ft;
+#if FSM_TIMER_DEBUG
+	if (ft->fi->debug)
+		ft->fi->printdebug(ft->fi, "FsmInitTimer %lx", (long) ft);
+#endif
+	init_timer(&ft->tl);
+}
+
+void
+FsmDelTimer(struct FsmTimer *ft, int where)
+{
+#if FSM_TIMER_DEBUG
+	if (ft->fi->debug)
+		ft->fi->printdebug(ft->fi, "FsmDelTimer %lx %d", (long) ft, where);
+#endif
+	del_timer(&ft->tl);
+}
+
+int
+FsmAddTimer(struct FsmTimer *ft,
+	    int millisec, int event, void *arg, int where)
+{
+
+#if FSM_TIMER_DEBUG
+	if (ft->fi->debug)
+		ft->fi->printdebug(ft->fi, "FsmAddTimer %lx %d %d",
+			(long) ft, millisec, where);
+#endif
+
+	if (timer_pending(&ft->tl)) {
+		printk(KERN_WARNING "FsmAddTimer: timer already active!\n");
+		ft->fi->printdebug(ft->fi, "FsmAddTimer already active!");
+		return -1;
+	}
+	init_timer(&ft->tl);
+	ft->event = event;
+	ft->arg = arg;
+	ft->tl.expires = jiffies + (millisec * HZ) / 1000;
+	add_timer(&ft->tl);
+	return 0;
+}
+
+void
+FsmRestartTimer(struct FsmTimer *ft,
+	    int millisec, int event, void *arg, int where)
+{
+
+#if FSM_TIMER_DEBUG
+	if (ft->fi->debug)
+		ft->fi->printdebug(ft->fi, "FsmRestartTimer %lx %d %d",
+			(long) ft, millisec, where);
+#endif
+
+	if (timer_pending(&ft->tl))
+		del_timer(&ft->tl);
+	init_timer(&ft->tl);
+	ft->event = event;
+	ft->arg = arg;
+	ft->tl.expires = jiffies + (millisec * HZ) / 1000;
+	add_timer(&ft->tl);
+}
+#endif
diff -Nru a/drivers/isdn/i4l/isdn_fsm.h b/drivers/isdn/i4l/isdn_fsm.h
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/isdn/i4l/isdn_fsm.h	Mon Oct  7 11:26:06 2002
@@ -0,0 +1,61 @@
+/* $Id: fsm.h,v 1.3.2.2 2001/09/23 22:24:47 kai Exp $
+ *
+ * Finite state machine
+ *
+ * Author       Karsten Keil
+ * Copyright    by Karsten Keil      <keil@isdn4linux.de>
+ *              by Kai Germaschewski <kai.germaschewski@gmx.de>
+ * 
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ */
+
+#ifndef __ISDN_FSM_H__
+#define __ISDN_FSM_H__
+
+#include <linux/kernel.h>
+#include <linux/timer.h>
+
+struct fsm_inst;
+
+typedef int (*fsm_fn)(struct fsm_inst *, int, void *);
+
+struct fsm {
+	fsm_fn *jumpmatrix;
+	int st_cnt, ev_cnt, fn_cnt;
+	char **st_str, **ev_str;
+	struct fsm_node *fn_tbl;
+};
+
+struct fsm_inst {
+	struct fsm *fsm;
+	int state;
+	int debug;
+	void *userdata;
+	int userint;
+	void (*printdebug) (struct fsm_inst *, char *, ...);
+};
+
+struct fsm_node {
+	int st, ev;
+	fsm_fn fn;
+};
+
+struct fsm_timer {
+	struct fsm_inst *fi;
+	struct timer_list tl;
+	int ev;
+	void *arg;
+};
+
+int  fsm_new(struct fsm *fsm);
+void fsm_free(struct fsm *fsm);
+int  fsm_event(struct fsm_inst *fi, int event, void *arg);
+void fsm_change_state(struct fsm_inst *fi, int newstate);
+void fsm_init_timer(struct fsm_inst *fi, struct fsm_timer *ft);
+int  fsm_add_timer(struct fsm_timer *ft, int timeout, int event);
+void fsm_mod_timer(struct fsm_timer *ft, int timeout, int event);
+void fsm_del_timer(struct fsm_timer *ft);
+
+#endif
diff -Nru a/drivers/isdn/i4l/isdn_net.c b/drivers/isdn/i4l/isdn_net.c
--- a/drivers/isdn/i4l/isdn_net.c	Mon Oct  7 11:25:40 2002
+++ b/drivers/isdn/i4l/isdn_net.c	Mon Oct  7 11:25:40 2002
@@ -33,34 +33,6 @@
 #include "isdn_concap.h"
 #include "isdn_ciscohdlck.h"
 
-enum {
-	ST_NULL,
-	ST_OUT_WAIT_DCONN,
-	ST_OUT_WAIT_BCONN,
-	ST_IN_WAIT_DCONN,
-	ST_IN_WAIT_BCONN,
-	ST_ACTIVE,
-	ST_WAIT_BEFORE_CB,
-};
-
-enum {
-	ST_CHARGE_NULL,
-	ST_CHARGE_GOT_CINF,  /* got a first charge info */
-	ST_CHARGE_HAVE_CINT, /* got a second chare info and thus the timing */
-};
-
-/* keep clear of ISDN_CMD_* and ISDN_STAT_* */
-enum {
-	EV_NET_DIAL            = 0x200,
-	EV_NET_TIMER_IN_DCONN  = 0x201,
-	EV_NET_TIMER_IN_BCONN  = 0x202,
-	EV_NET_TIMER_OUT_DCONN = 0x203,
-	EV_NET_TIMER_OUT_BCONN = 0x204,
-	EV_NET_TIMER_CB        = 0x205,
-};
-
-LIST_HEAD(isdn_net_devs); /* Linked list of isdn_net_dev's */
-
 /*
  * Outline of new tbusy handling: 
  *
@@ -90,30 +62,20 @@
 
 /* 
  * Find out if the netdevice has been ifup-ed yet.
- * For slaves, look at the corresponding master.
  */
-static __inline__ int isdn_net_device_started(isdn_net_dev *n)
+static inline int
+isdn_net_device_started(isdn_net_dev *idev)
 {
-	isdn_net_local *lp = &n->local;
-	struct net_device *dev;
-	
-	if (lp->master) 
-		dev = lp->master;
-	else
-		dev = &n->dev;
-	return netif_running(dev);
+	return netif_running(&idev->mlp->dev);
 }
 
 /*
  * stop the network -> net_device queue.
- * For slaves, stop the corresponding master interface.
  */
-static __inline__ void isdn_net_device_stop_queue(isdn_net_local *lp)
+static inline void
+isdn_net_dev_stop_queue(isdn_net_dev *idev)
 {
-	if (lp->master)
-		netif_stop_queue(lp->master);
-	else
-		netif_stop_queue(&lp->netdev->dev);
+	netif_stop_queue(&idev->mlp->dev);
 }
 
 /*
@@ -121,105 +83,73 @@
  * master or slave) is busy. It's busy iff all (master and slave) 
  * queues are busy
  */
-static __inline__ int isdn_net_device_busy(isdn_net_local *lp)
+static inline int
+isdn_net_device_busy(isdn_net_dev *idev)
 {
-	isdn_net_local *nlp;
-	isdn_net_dev *nd;
+	isdn_net_local *mlp = idev->mlp;
 	unsigned long flags;
+	int retval = 1;
 
-	if (!isdn_net_lp_busy(lp))
+	if (!isdn_net_dev_busy(idev))
 		return 0;
 
-	if (lp->master)
-		nd = ((isdn_net_local *) lp->master->priv)->netdev;
-	else
-		nd = lp->netdev;
-	
-	spin_lock_irqsave(&nd->queue_lock, flags);
-	nlp = lp->next;
-	while (nlp != lp) {
-		if (!isdn_net_lp_busy(nlp)) {
-			spin_unlock_irqrestore(&nd->queue_lock, flags);
-			return 0;
+	spin_lock_irqsave(&mlp->online_lock, flags);
+	list_for_each_entry(idev, &mlp->online, online) {
+		if (!isdn_net_dev_busy(idev)) {
+			retval = 0;
+			break;
 		}
-		nlp = nlp->next;
 	}
-	spin_unlock_irqrestore(&nd->queue_lock, flags);
-	return 1;
+	spin_unlock_irqrestore(&mlp->online_lock, flags);
+	return retval;
 }
 
-static __inline__ void isdn_net_inc_frame_cnt(isdn_net_local *lp)
+static inline
+void isdn_net_inc_frame_cnt(isdn_net_dev *idev)
 {
-	atomic_inc(&lp->frame_cnt);
-	if (isdn_net_device_busy(lp))
-		isdn_net_device_stop_queue(lp);
+	atomic_inc(&idev->frame_cnt);
+	if (isdn_net_device_busy(idev))
+		isdn_net_dev_stop_queue(idev);
 }
 
-static __inline__ void isdn_net_dec_frame_cnt(isdn_net_local *lp)
+static inline void
+isdn_net_dec_frame_cnt(isdn_net_dev *idev)
 {
-	atomic_dec(&lp->frame_cnt);
+	atomic_dec(&idev->frame_cnt);
 
-	if (!(isdn_net_device_busy(lp))) {
-		if (!skb_queue_empty(&lp->super_tx_queue)) {
-			queue_task(&lp->tqueue, &tq_immediate);
-			mark_bh(IMMEDIATE_BH);
-		} else {
-			isdn_net_device_wake_queue(lp);
-		}
+	if (!isdn_net_device_busy(idev)) {
+		if (!skb_queue_empty(&idev->super_tx_queue))
+			tasklet_schedule(&idev->tlet);
+		else
+			isdn_net_dev_wake_queue(idev);
        }                                                                      
 }
 
-static __inline__ void isdn_net_zero_frame_cnt(isdn_net_local *lp)
+static inline
+void isdn_net_zero_frame_cnt(isdn_net_dev *idev)
 {
-	atomic_set(&lp->frame_cnt, 0);
+	atomic_set(&idev->frame_cnt, 0);
 }
 
-/* For 2.2.x we leave the transmitter busy timeout at 2 secs, just 
- * to be safe.
- * For 2.3.x we push it up to 20 secs, because call establishment
- * (in particular callback) may take such a long time, and we 
- * don't want confusing messages in the log. However, there is a slight
- * possibility that this large timeout will break other things like MPPP,
- * which might rely on the tx timeout. If so, we'll find out this way...
- */
+/* Prototypes */
 
-#define ISDN_NET_TX_TIMEOUT (20*HZ) 
+int isdn_net_handle_event(isdn_net_dev *idev, int pr, void *arg);
+
+char *isdn_net_revision = "$Revision: 1.140.6.11 $";
 
-static struct isdn_netif_ops *netif_ops[ISDN_NET_ENCAP_NR];
+/* A packet has successfully been sent out. */
 
 int
-register_isdn_netif(int encap, struct isdn_netif_ops *ops)
+isdn_net_bsent(isdn_net_dev *idev, isdn_ctrl *c)
 {
-	if (encap < 0 || encap >= ISDN_NET_ENCAP_NR)
-		return -EINVAL;
+	isdn_net_local *mlp = idev->mlp;
 
-	if (netif_ops[encap])
-		return -EBUSY;
-
-	netif_ops[encap] = ops;
-
-	return 0;
-}
-
-int isdn_net_online(isdn_net_dev *idev)
-{
-	return idev->dialstate == ST_ACTIVE;
+	isdn_net_dec_frame_cnt(idev);
+	mlp->stats.tx_packets++;
+	mlp->stats.tx_bytes += c->parm.length;
+	return 1;
 }
 
-/* Prototypes */
-
-static int isdn_net_force_dial_lp(isdn_net_local *);
-static int isdn_net_start_xmit(struct sk_buff *, struct net_device *);
-static void do_dialout(isdn_net_local *lp);
-static int isdn_net_set_encap(isdn_net_dev *p, int encap);
-static int isdn_net_handle_event(isdn_net_local *lp, int pr, void *arg);
-
-char *isdn_net_revision = "$Revision: 1.140.6.11 $";
-
- /*
-  * Code for raw-networking over ISDN
-  */
-
 static void
 isdn_net_unreachable(struct net_device *dev, struct sk_buff *skb, char *reason)
 {
@@ -233,611 +163,48 @@
 	dst_link_failure(skb);
 }
 
-/* Open/initialize the board. */
-static int
-isdn_net_open(struct net_device *dev)
-{
-	isdn_net_local *lp = dev->priv;
-	int retval = 0;
-
-	if (!lp->ops)
-		return -ENODEV;
-
-	if (lp->ops->open)
-		retval = lp->ops->open(lp);
-
-	if (!retval)
-		return retval;
-	
-	netif_start_queue(dev);
-	isdn_MOD_INC_USE_COUNT();
-	return 0;
-}
-
-/*
- * unbind a net-interface (resets interface after an error)
- */
-static void
-isdn_net_unbind_channel(isdn_net_local * lp)
-{
-	isdn_net_dev *idev = lp->netdev;
-	ulong flags;
-
-	save_flags(flags);
-	cli();
-
-	if (lp->ops->unbind)
-		lp->ops->unbind(lp);
-
-	skb_queue_purge(&lp->super_tx_queue);
-
-	if (!lp->master) {	/* reset only master device */
-		/* Moral equivalent of dev_purge_queues():
-		   BEWARE! This chunk of code cannot be called from hardware
-		   interrupt handler. I hope it is true. --ANK
-		 */
-		qdisc_reset(lp->netdev->dev.qdisc);
-	}
-	idev->dialstate = ST_NULL;
-	if (idev->isdn_slot >= 0) {
-		isdn_slot_set_rx_netdev(idev->isdn_slot, NULL);
-		isdn_slot_set_st_netdev(idev->isdn_slot, NULL);
-		isdn_slot_free(idev->isdn_slot, ISDN_USAGE_NET);
-	}
-	idev->isdn_slot = -1;
-
-	restore_flags(flags);
-}
-
-/*
- * Assign an ISDN-channel to a net-interface
- */
-static int
-isdn_net_bind_channel(isdn_net_local *lp, int idx)
-{
-	isdn_net_dev *idev = lp->netdev;
-	int retval = 0;
-	unsigned long flags;
-
-	save_flags(flags);
-	cli();
-
-	idev->isdn_slot = idx;
-	isdn_slot_set_rx_netdev(idev->isdn_slot, lp->netdev);
-	isdn_slot_set_st_netdev(idev->isdn_slot, lp->netdev);
-
-	if (lp->ops->bind)
-		retval = lp->ops->bind(lp);
-
-	if (retval < 0)
-		isdn_net_unbind_channel(lp);
-
-	restore_flags(flags);
-	return retval;
-}
-
-/*
- * Perform auto-hangup for net-interfaces.
- *
- * auto-hangup:
- * Increment idle-counter (this counter is reset on any incoming or
- * outgoing packet), if counter exceeds configured limit either do a
- * hangup immediately or - if configured - wait until just before the next
- * charge-info.
- */
-
-static void isdn_net_hup_timer(unsigned long data)
-{
-	isdn_net_dev *idev = (isdn_net_dev *) data;
-	isdn_net_local *lp = &idev->local;
-
-	if (!isdn_net_online(idev)) {
-		isdn_BUG();
-		return;
-	}
-	
-	dbg_net_dial("%s: huptimer %d, onhtime %d, chargetime %ld, chargeint %d\n",
-		     l->name, l->huptimer, l->onhtime, l->chargetime, l->chargeint);
-
-	if (lp->onhtime == 0)
-		return;
-	
-	if (idev->huptimer++ <= lp->onhtime)
-		goto mod_timer;
-
-	if ((lp->hupflags & (ISDN_MANCHARGE | ISDN_CHARGEHUP)) == (ISDN_MANCHARGE | ISDN_CHARGEHUP)) {
-		while (time_after(jiffies, idev->chargetime + idev->chargeint))
-			idev->chargetime += idev->chargeint;
-		
-		if (time_after(jiffies, idev->chargetime + idev->chargeint - 2 * HZ)) {
-			if (idev->outgoing || lp->hupflags & ISDN_INHUP) {
-				isdn_net_hangup(idev);
-				return;
-			}
-		}
-	} else if (idev->outgoing) {
-		if (lp->hupflags & ISDN_CHARGEHUP) {
-			if (idev->charge_state != ST_CHARGE_HAVE_CINT) {
-				dbg_net_dial("%s: did not get CINT\n", lp->name);
-				isdn_net_hangup(idev);
-				return;
-			} else if (time_after(jiffies, idev->chargetime + idev->chargeint)) {
-				dbg_net_dial("%s: chtime = %lu, chint = %d\n",
-					     lp->name, lp->chargetime, lp->chargeint);
-				isdn_net_hangup(idev);
-				return;
-			}
-		}
-	} else if (lp->hupflags & ISDN_INHUP) {
-		isdn_net_hangup(idev);
-		return;
-	}
- mod_timer:
-	mod_timer(&idev->hup_timer, idev->hup_timer.expires + HZ);
-}
-
-static void isdn_net_lp_disconnected(isdn_net_local *lp)
-{
-	isdn_net_rm_from_bundle(lp);
-}
-
-static void isdn_net_connected(isdn_net_local *lp)
-{
-	isdn_net_dev *idev = lp->netdev;
-
-	idev->dialstate = ST_ACTIVE;
-	idev->hup_timer.expires = jiffies + HZ;
-	add_timer(&idev->hup_timer);
-
-	if (lp->p_encap != ISDN_NET_ENCAP_SYNCPPP) {
-		if (lp->master) { /* is lp a slave? */
-			isdn_net_dev *nd = ((isdn_net_local *)lp->master->priv)->netdev;
-			isdn_net_add_to_bundle(nd, lp);
-		}
-	}
-	printk(KERN_INFO "isdn_net: %s connected\n", idev->name);
-	/* If first Chargeinfo comes before B-Channel connect,
-	 * we correct the timestamp here.
-	 */
-	idev->chargetime = jiffies;
-	
-	/* reset dial-timeout */
-	idev->dialstarted = 0;
-	idev->dialwait_timer = 0;
-	
-	idev->transcount = 0;
-	idev->cps = 0;
-	idev->last_jiffies = jiffies;
-
-	if (lp->ops->connected)
-		lp->ops->connected(lp);
-	else
-		isdn_net_device_wake_queue(lp);
-}
-
-/*
- * Handle status-messages from ISDN-interfacecard.
- * This function is called from within the main-status-dispatcher
- * isdn_status_callback, which itself is called from the low-level driver.
- * Return: 1 = Event handled, 0 = not for us or unknown Event.
- */
-int
-isdn_net_stat_callback(int idx, isdn_ctrl *c)
-{
-	isdn_net_dev *p = isdn_slot_st_netdev(idx);
-	isdn_net_local *lp;
-	int cmd = c->command;
-
-	if (!p) {
-		HERE;
-		return 0;
-	}
-	lp = &p->local;
-
-	return isdn_net_handle_event(lp, cmd, c);
-}
-
-static void
-isdn_net_dial_timer(unsigned long data)
-{
-	isdn_net_dev *idev = (isdn_net_dev *) data;
-
-	isdn_net_handle_event(&idev->local, idev->dial_event, NULL);
-}
-
-/* Initiate dialout. Set phone-number-pointer to first number
- * of interface.
- */
-static void
-init_dialout(isdn_net_local *lp)
-{
-	isdn_net_dev *idev = lp->netdev;
-
-	idev->dial = 0;
-
-	if (lp->dialtimeout > 0 &&
-	    (idev->dialstarted == 0 || 
-	     time_after(jiffies, idev->dialstarted + lp->dialtimeout + lp->dialwait))) {
-		idev->dialstarted = jiffies;
-		idev->dialwait_timer = 0;
-	}
-	lp->dialretry = 0;
-	do_dialout(lp);
-}
-
-/* Setup interface, dial current phone-number, switch to next number.
- * If list of phone-numbers is exhausted, increment
- * retry-counter.
- */
 static void
-do_dialout(isdn_net_local *lp)
+isdn_net_log_skb(struct sk_buff *skb, isdn_net_dev *idev)
 {
-	isdn_net_dev *idev = lp->netdev;
-	int i;
-	unsigned long flags;
-	struct isdn_net_phone *phone;
-	struct dial_info dial = {
-		.l2_proto = lp->l2_proto,
-		.l3_proto = lp->l3_proto,
-		.si1      = 7,
-		.si2      = 0,
-		.msn      = lp->msn,
-	};
-
-	if (ISDN_NET_DIALMODE(*lp) == ISDN_NET_DM_OFF)
-		return;
-	
-	spin_lock_irqsave(&lp->lock, flags);
-	if (list_empty(&lp->phone[1])) {
-		spin_unlock_irqrestore(&lp->lock, flags);
-		return;
-	}
-	i = 0;
-	list_for_each_entry(phone, &lp->phone[1], list) {
-		if (i++ == idev->dial)
-			goto found;
-	}
-	/* otherwise start in front */
-	phone = list_entry(lp->phone[1].next, struct isdn_net_phone, list);
-	idev->dial = 0;
-	lp->dialretry++;
-	
- found:
-	idev->dial++;
-	dial.phone = phone->num;
-	spin_unlock_irqrestore(&lp->lock, flags);
-
-	if (lp->dialretry > lp->dialmax) {
-		if (lp->dialtimeout == 0) {
-			idev->dialwait_timer = jiffies + lp->dialwait;
-			idev->dialstarted = 0;
-		}
-		isdn_net_hangup(idev);
-		return;
-	}
-	if(lp->dialtimeout > 0 &&
-	   time_after(jiffies, idev->dialstarted + lp->dialtimeout)) {
-		idev->dialwait_timer = jiffies + lp->dialwait;
-		idev->dialstarted = 0;
-		isdn_net_hangup(idev);
-		return;
-	}
-	/*
-	 * Switch to next number or back to start if at end of list.
-	 */
-	isdn_slot_dial(idev->isdn_slot, &dial);
-
-	idev->huptimer = 0;
-	idev->outgoing = 1;
-	if (idev->chargeint)
-		idev->charge_state = ST_CHARGE_HAVE_CINT;
-	else
-		idev->charge_state = ST_CHARGE_NULL;
-
-	if (lp->cbdelay && (lp->flags & ISDN_NET_CBOUT)) {
-		idev->dial_timer.expires = jiffies + lp->cbdelay;
-		idev->dial_event = EV_NET_TIMER_CB;
-	} else {
-		idev->dial_timer.expires = jiffies + 10 * HZ;
-		idev->dial_event = EV_NET_TIMER_OUT_DCONN;
-	}
-	idev->dialstate = ST_OUT_WAIT_DCONN;
-	add_timer(&idev->dial_timer);
-}
-
-/* For EV_NET_DIAL, returns 1 if timer callback is needed 
- * For ISDN_STAT_*, returns 1 if event was for us 
- */
-static int
-isdn_net_handle_event(isdn_net_local *lp, int pr, void *arg)
-{
-	isdn_net_dev *idev = lp->netdev;
-	isdn_ctrl *c = arg;
-	isdn_ctrl cmd;
-
-	dbg_net_dial("%s: dialstate=%d pr=%#x\n", lp->name, lp->dialstate,pr);
-
-	switch (idev->dialstate) {
-	case ST_ACTIVE:
-		switch (pr) {
-		case ISDN_STAT_BSENT:
-			/* A packet has successfully been sent out */
-			isdn_net_dec_frame_cnt(lp);
-			lp->stats.tx_packets++;
-			lp->stats.tx_bytes += c->parm.length;
-			return 1;
-		case ISDN_STAT_DHUP:
-			if (lp->ops->disconnected)
-				lp->ops->disconnected(lp);
-
-			isdn_net_lp_disconnected(lp);
-			isdn_slot_all_eaz(idev->isdn_slot);
-			printk(KERN_INFO "%s: remote hangup\n", idev->name);
-			printk(KERN_INFO "%s: Chargesum is %d\n", idev->name,
-			       idev->charge);
-			isdn_net_unbind_channel(lp);
-			return 1;
-		case ISDN_STAT_CINF:
-			/* Charge-info from TelCo. Calculate interval between
-			 * charge-infos and set timestamp for last info for
-			 * usage by isdn_net_autohup()
-			 */
-			idev->charge++;
-			switch (idev->charge_state) {
-			case ST_CHARGE_NULL:
-				idev->charge_state = ST_CHARGE_GOT_CINF;
-				break;
-			case ST_CHARGE_GOT_CINF:
-				idev->charge_state = ST_CHARGE_HAVE_CINT;
-				/* fall through */
-			case ST_CHARGE_HAVE_CINT:
-				idev->chargeint = jiffies - idev->chargetime - 2 * HZ;
-				break;
-			}
-			idev->chargetime = jiffies;
-			dbg_net_dial("%s: got CINF\n", lp->name);
-			return 1;
-		}
-		break;
-	case ST_OUT_WAIT_DCONN:
-		switch (pr) {
-		case EV_NET_TIMER_OUT_DCONN:
-			/* try again */
-			do_dialout(lp);
-			return 1;
-		case EV_NET_TIMER_CB:
-			/* Remote does callback. Hangup after cbdelay, 
-			 * then wait for incoming call */
-			printk(KERN_INFO "%s: hangup waiting for callback ...\n", idev->name);
-			isdn_net_hangup(idev);
-			return 1;
-		case ISDN_STAT_DCONN:
-			/* Got D-Channel-Connect, send B-Channel-request */
-			del_timer(&idev->dial_timer);
-			idev->dialstate = ST_OUT_WAIT_BCONN;
-			isdn_slot_command(idev->isdn_slot, ISDN_CMD_ACCEPTB, &cmd);
-			idev->dial_timer.expires = jiffies + 10 * HZ;
-			idev->dial_event = EV_NET_TIMER_OUT_BCONN;
-			add_timer(&idev->dial_timer);
-			return 1;
-		case ISDN_STAT_DHUP:
-			del_timer(&idev->dial_timer);
-			isdn_slot_all_eaz(idev->isdn_slot);
-			printk(KERN_INFO "%s: remote hangup\n", idev->name);
-			isdn_net_unbind_channel(lp);
-			return 1;
-		}
-		break;
-	case ST_OUT_WAIT_BCONN:
-		switch (pr) {
-		case EV_NET_TIMER_OUT_BCONN:
-			/* try again */
-			do_dialout(lp);
-			return 1;
-		case ISDN_STAT_BCONN:
-			del_timer(&idev->dial_timer);
-			isdn_slot_set_usage(idev->isdn_slot, isdn_slot_usage(idev->isdn_slot) | ISDN_USAGE_OUTGOING);
-			isdn_net_connected(lp);
-			return 1;
-		case ISDN_STAT_DHUP:
-			del_timer(&idev->dial_timer);
-			isdn_slot_all_eaz(idev->isdn_slot);
-			printk(KERN_INFO "%s: remote hangup\n", idev->name);
-			isdn_net_unbind_channel(lp);
-			return 1;
-		}
-		break;
-	case ST_IN_WAIT_DCONN:
-		switch (pr) {
-		case EV_NET_TIMER_IN_DCONN:
-			isdn_net_hangup(idev);
-			return 1;
-		case ISDN_STAT_DCONN:
-			del_timer(&idev->dial_timer);
-			idev->dialstate = ST_IN_WAIT_BCONN;
-			isdn_slot_command(idev->isdn_slot, ISDN_CMD_ACCEPTB, &cmd);
-			idev->dial_timer.expires = jiffies + 10 * HZ;
-			idev->dial_event = EV_NET_TIMER_IN_BCONN;
-			add_timer(&idev->dial_timer);
-			return 1;
-		case ISDN_STAT_DHUP:
-			del_timer(&idev->dial_timer);
-			isdn_slot_all_eaz(idev->isdn_slot);
-			printk(KERN_INFO "%s: remote hangup\n", idev->name);
-			isdn_net_unbind_channel(lp);
-			return 1;
-		}
-		break;
-	case ST_IN_WAIT_BCONN:
-		switch (pr) {
-		case EV_NET_TIMER_IN_BCONN:
-			isdn_net_hangup(idev);
-			break;
-		case ISDN_STAT_BCONN:
-			del_timer(&idev->dial_timer);
-			isdn_slot_set_rx_netdev(idev->isdn_slot, idev);
-			isdn_net_connected(lp);
-			return 1;
-		case ISDN_STAT_DHUP:
-			del_timer(&idev->dial_timer);
-			isdn_slot_all_eaz(idev->isdn_slot);
-			printk(KERN_INFO "%s: remote hangup\n", idev->name);
-			isdn_net_unbind_channel(lp);
-			return 1;
-		}
-		break;
-	case ST_WAIT_BEFORE_CB:
-		switch (pr) {
-		case EV_NET_TIMER_CB:
-			/* Callback Delay */
-			init_dialout(lp);
-			return 1;
-		}
-		break;
-	default:
-		isdn_BUG();
-		break;
-	}
-	printk("NOT HANDLED?\n");
-	return 0;
-}
-
-/*
- * Perform hangup for a net-interface.
- */
-void
-isdn_net_hangup(isdn_net_dev *idev)
-{
-	isdn_net_local *lp = &idev->local;
-	isdn_ctrl cmd;
-
-	del_timer_sync(&idev->hup_timer);
-	if (!isdn_net_bound(idev))
-		return;
-
-	// FIXME ugly and recursive
-	if (lp->slave != NULL) {
-		isdn_net_local *slp = (isdn_net_local *)lp->slave->priv;
-		isdn_net_dev *sidev = slp->netdev;
-		if (isdn_net_bound(sidev)) {
-			printk(KERN_INFO
-			       "isdn_net: hang up slave %s before %s\n",
-			       sidev->name, idev->name);
-			isdn_net_hangup(sidev);
-		}
-	}
-	printk(KERN_INFO "isdn_net: local hangup %s\n", idev->name);
-	if (lp->ops->disconnected)
-		lp->ops->disconnected(lp);
-	
-	isdn_net_lp_disconnected(lp);
-	
-	isdn_slot_command(idev->isdn_slot, ISDN_CMD_HANGUP, &cmd);
-	printk(KERN_INFO "%s: Chargesum is %d\n", idev->name, idev->charge);
-	isdn_slot_all_eaz(idev->isdn_slot);
-	isdn_net_unbind_channel(lp);
-}
-
-void
-isdn_net_hangup_all()
-{
-	struct list_head *l;
-
-	list_for_each(l, &isdn_net_devs) {
-		isdn_net_dev *p = list_entry(l, isdn_net_dev, global_list);
-		isdn_net_hangup(p);
-	}
-}
-
-typedef struct {
-	unsigned short source;
-	unsigned short dest;
-} ip_ports;
-
-static void
-isdn_net_log_skb(struct sk_buff * skb, isdn_net_local * lp)
-{
-	isdn_net_dev *idev = lp->netdev;
-	u_char *p = skb->nh.raw; /* hopefully, this was set correctly */
+	unsigned char *p = skb->nh.raw; /* hopefully, this was set correctly */
 	unsigned short proto = ntohs(skb->protocol);
 	int data_ofs;
-	ip_ports *ipp;
+	struct ip_ports {
+		unsigned short source;
+		unsigned short dest;
+	} *ipp;
 	char addinfo[100];
 
-	addinfo[0] = '\0';
-	/* This check stolen from 2.1.72 dev_queue_xmit_nit() */
-	if (skb->nh.raw < skb->data || skb->nh.raw >= skb->tail) {
-		/* fall back to old isdn_net_log_packet method() */
-		char * buf = skb->data;
-
-		printk(KERN_DEBUG "isdn_net: protocol %04x is buggy, dev %s\n", skb->protocol, idev->name);
-		p = buf;
-		proto = ETH_P_IP;
-		switch (lp->p_encap) {
-			case ISDN_NET_ENCAP_IPTYP:
-				proto = ntohs(*(unsigned short *) &buf[0]);
-				p = &buf[2];
-				break;
-			case ISDN_NET_ENCAP_ETHER:
-				proto = ntohs(*(unsigned short *) &buf[12]);
-				p = &buf[14];
-				break;
-			case ISDN_NET_ENCAP_CISCOHDLC:
-				proto = ntohs(*(unsigned short *) &buf[2]);
-				p = &buf[4];
-				break;
-			case ISDN_NET_ENCAP_SYNCPPP:
-				proto = ntohs(skb->protocol);
-				p = &buf[IPPP_MAX_HEADER];
-				break;
-		}
-	}
 	data_ofs = ((p[0] & 15) * 4);
 	switch (proto) {
-		case ETH_P_IP:
-			switch (p[9]) {
-				case 1:
-					strcpy(addinfo, " ICMP");
-					break;
-				case 2:
-					strcpy(addinfo, " IGMP");
-					break;
-				case 4:
-					strcpy(addinfo, " IPIP");
-					break;
-				case 6:
-					ipp = (ip_ports *) (&p[data_ofs]);
-					sprintf(addinfo, " TCP, port: %d -> %d", ntohs(ipp->source),
-						ntohs(ipp->dest));
-					break;
-				case 8:
-					strcpy(addinfo, " EGP");
-					break;
-				case 12:
-					strcpy(addinfo, " PUP");
-					break;
-				case 17:
-					ipp = (ip_ports *) (&p[data_ofs]);
-					sprintf(addinfo, " UDP, port: %d -> %d", ntohs(ipp->source),
-						ntohs(ipp->dest));
-					break;
-				case 22:
-					strcpy(addinfo, " IDP");
-					break;
-			}
-			printk(KERN_INFO
-				"OPEN: %d.%d.%d.%d -> %d.%d.%d.%d%s\n",
-
-			       p[12], p[13], p[14], p[15],
-			       p[16], p[17], p[18], p[19],
-			       addinfo);
+	case ETH_P_IP:
+		switch (p[9]) {
+		case IPPROTO_ICMP:
+			strcpy(addinfo, "ICMP");
 			break;
-		case ETH_P_ARP:
-			printk(KERN_INFO
-				"OPEN: ARP %d.%d.%d.%d -> *.*.*.* ?%d.%d.%d.%d\n",
-			       p[14], p[15], p[16], p[17],
-			       p[24], p[25], p[26], p[27]);
+		case IPPROTO_TCP:
+		case IPPROTO_UDP:
+			ipp = (struct ip_ports *) (&p[data_ofs]);
+			sprintf(addinfo, "%s, port: %d -> %d",
+				p[9] == IPPROTO_TCP ? "TCP" : "UDP",
+				ntohs(ipp->source), ntohs(ipp->dest));
 			break;
+		default:
+			sprintf(addinfo, "type %d", p[9]);
+		}
+		printk(KERN_INFO
+		       "OPEN: %u.%u.%u.%u -> %u.%u.%u.%u %s\n",
+		       
+		       NIPQUAD(*(u32 *)(p + 12)), NIPQUAD(*(u32 *)(p + 16)),
+		       addinfo);
+		break;
+	case ETH_P_ARP:
+		printk(KERN_INFO
+		       "OPEN: ARP %d.%d.%d.%d -> *.*.*.* ?%d.%d.%d.%d\n",
+		       NIPQUAD(*(u32 *)(p + 14)), NIPQUAD(*(u32 *)(p + 24)));
+		break;
+	default:
+		printk(KERN_INFO "OPEN: unknown proto %#x\n", proto);
 	}
 }
 
@@ -846,57 +213,40 @@
  * not received from the network layer, but e.g. frames from ipppd, CCP
  * reset frames etc.
  */
-void isdn_net_write_super(isdn_net_local *lp, struct sk_buff *skb)
+void
+isdn_net_write_super(isdn_net_dev *idev, struct sk_buff *skb)
 {
 	if (in_irq()) {
 		// we can't grab the lock from irq context, 
 		// so we just queue the packet
-		skb_queue_tail(&lp->super_tx_queue, skb); 
-		queue_task(&lp->tqueue, &tq_immediate);
-		mark_bh(IMMEDIATE_BH);
+		skb_queue_tail(&idev->super_tx_queue, skb); 
+
+		tasklet_schedule(&idev->tlet);
 		return;
 	}
 
-	spin_lock_bh(&lp->xmit_lock);
-	if (!isdn_net_lp_busy(lp)) {
-		isdn_net_writebuf_skb(lp, skb);
+	spin_lock_bh(&idev->xmit_lock);
+	if (!isdn_net_dev_busy(idev)) {
+		isdn_net_writebuf_skb(idev, skb);
 	} else {
-		skb_queue_tail(&lp->super_tx_queue, skb);
+		skb_queue_tail(&idev->super_tx_queue, skb);
 	}
-	spin_unlock_bh(&lp->xmit_lock);
-}
-
-/*
- * called from tq_immediate
- */
-static void isdn_net_softint(void *private)
-{
-	isdn_net_local *lp = private;
-	struct sk_buff *skb;
-
-	spin_lock_bh(&lp->xmit_lock);
-	while (!isdn_net_lp_busy(lp)) {
-		skb = skb_dequeue(&lp->super_tx_queue);
-		if (!skb)
-			break;
-		isdn_net_writebuf_skb(lp, skb);                                
-	}
-	spin_unlock_bh(&lp->xmit_lock);
+	spin_unlock_bh(&idev->xmit_lock);
 }
 
 /* 
  * all frames sent from the (net) LL to a HL driver should go via this function
- * it's serialized by the caller holding the lp->xmit_lock spinlock
+ * it's serialized by the caller holding the idev->xmit_lock spinlock
  */
-void isdn_net_writebuf_skb(isdn_net_local *lp, struct sk_buff *skb)
+void isdn_net_writebuf_skb(isdn_net_dev *idev, struct sk_buff *skb)
 {
-	isdn_net_dev *idev = lp->netdev;
+	isdn_net_local *mlp = idev->mlp;
 	int ret;
 	int len = skb->len;     /* save len */
 
 	/* before obtaining the lock the caller should have checked that
 	   the lp isn't busy */
-	if (isdn_net_lp_busy(lp)) {
+	if (isdn_net_dev_busy(idev)) {
 		isdn_BUG();
 		goto error;
 	}
@@ -913,18 +263,28 @@
 	}
 	
 	idev->transcount += len;
-	isdn_net_inc_frame_cnt(lp);
+	isdn_net_inc_frame_cnt(idev);
 	return;
 
  error:
 	dev_kfree_skb(skb);
-	lp->stats.tx_errors++;
+	mlp->stats.tx_errors++;
 }
 
+static void
+isdn_net_dial_slave(isdn_net_local *mlp)
+{
+	isdn_net_dev *idev;
+
+	list_for_each_entry(idev, &mlp->slaves, slaves) {
+		if (!isdn_net_bound(idev)) {
+			isdn_net_dial(idev);
+			break;
+		}
+	}
+}
 
 /*
- *  Helper function for isdn_net_start_xmit.
- *  When called, the connection is already established.
  *  Based on cps-calculation, check if device is overloaded.
  *  If so, and if a slave exists, trigger dialing for it.
  *  If any slave is online, deliver packets using a simple round robin
@@ -933,37 +293,27 @@
  *  Return: 0 on success, !0 on failure.
  */
 
-static int
-isdn_net_xmit(struct net_device *ndev, struct sk_buff *skb)
+int
+isdn_net_start_xmit(struct sk_buff *skb, struct net_device *ndev)
 {
-	isdn_net_dev *nd, *idev;
-	isdn_net_local *slp;
-	isdn_net_local *lp = ndev->priv;
-	int retv = 0;
+	isdn_net_dev *idev;
+	isdn_net_local *mlp = ndev->priv;
 
-	if (lp->master) {
-		isdn_BUG();
-		dev_kfree_skb(skb);
-		return 0;
-	}
+	ndev->trans_start = jiffies;
 
-	/* For the other encaps the header has already been built */
-	if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP) {
-		return isdn_ppp_xmit(skb, ndev);
-	}
-	nd = ((isdn_net_local *) ndev->priv)->netdev;
-	lp = isdn_net_get_locked_lp(nd);
-	if (!lp) {
+	if (list_empty(&mlp->online))
+		return isdn_net_autodial(skb, ndev);
+
+	idev = isdn_net_get_locked_dev(mlp);
+	if (!idev) {
 		printk(KERN_WARNING "%s: all channels busy - requeuing!\n", ndev->name);
+		netif_stop_queue(ndev);
 		return 1;
 	}
-	idev = lp->netdev;
-	/* we have our lp locked from now on */
+	/* we have our idev locked from now on */
 
-	/* Reset hangup-timeout */
-	idev->huptimer = 0; // FIXME?
-	isdn_net_writebuf_skb(lp, skb);
-	spin_unlock_bh(&lp->xmit_lock);
+	isdn_net_writebuf_skb(idev, skb);
+	spin_unlock_bh(&idev->xmit_lock);
 
 	/* the following stuff is here for backwards compatibility.
 	 * in future, start-up and hangup of slaves (based on current load)
@@ -978,88 +328,50 @@
 	if (dev->net_verbose > 3)
 		printk(KERN_DEBUG "%s: %d bogocps\n", idev->name, idev->cps);
 
-	if (idev->cps > lp->triggercps) {
-		if (lp->slave) {
-			if (!lp->sqfull) {
-				/* First time overload: set timestamp only */
-				lp->sqfull = 1;
-				lp->sqfull_stamp = jiffies;
-			} else {
-				/* subsequent overload: if slavedelay exceeded, start dialing */
-				if (time_after(jiffies, lp->sqfull_stamp + lp->slavedelay)) {
-					slp = lp->slave->priv;
-					if (!isdn_net_bound(slp->netdev)) {
-						isdn_net_force_dial_lp((isdn_net_local *) lp->slave->priv);
-					}
-				}
+	if (idev->cps > mlp->triggercps) {
+		if (!idev->sqfull) {
+			/* First time overload: set timestamp only */
+			idev->sqfull = 1;
+			idev->sqfull_stamp = jiffies;
+		} else {
+			/* subsequent overload: if slavedelay exceeded, start dialing */
+			if (time_after(jiffies, idev->sqfull_stamp + mlp->slavedelay)) {
+				isdn_net_dial_slave(mlp);
 			}
 		}
 	} else {
-		if (lp->sqfull && time_after(jiffies, lp->sqfull_stamp + lp->slavedelay + (10 * HZ))) {
-			lp->sqfull = 0;
+		if (idev->sqfull && time_after(jiffies, idev->sqfull_stamp + mlp->slavedelay + 10 * HZ)) {
+			idev->sqfull = 0;
 		}
 		/* this is a hack to allow auto-hangup for slaves on moderate loads */
-		nd->queue = &nd->local;
-	}
-
-	return retv;
-
-}
-
-static void
-isdn_net_adjust_hdr(struct sk_buff *skb, struct net_device *dev)
-{
-	isdn_net_local *lp = dev->priv;
-	if (!skb)
-		return;
-	if (lp->p_encap == ISDN_NET_ENCAP_ETHER) {
-		int pullsize = (ulong)skb->nh.raw - (ulong)skb->data - ETH_HLEN;
-		if (pullsize > 0) {
-			printk(KERN_DEBUG "isdn_net: Pull junk %d\n", pullsize);
-			skb_pull(skb, pullsize);
-		}
+		list_del(&mlp->online);
+		list_add_tail(&mlp->online, &idev->online);
 	}
-}
-
-
-void isdn_net_tx_timeout(struct net_device * ndev)
-{
-	isdn_net_local *lp = ndev->priv;
-	isdn_net_dev *idev = lp->netdev;
 
-	printk(KERN_WARNING "isdn_tx_timeout dev %s %d\n",
-	       ndev->name, idev->dialstate);
-
-	netif_wake_queue(ndev);
+	return 0;
 }
 
-static int
+int
 isdn_net_autodial(struct sk_buff *skb, struct net_device *ndev)
 {
-	isdn_net_local *lp = ndev->priv;
-	isdn_net_dev *idev = lp->netdev;
+	isdn_net_local *mlp = ndev->priv;
+	isdn_net_dev *idev = list_entry(mlp->slaves.next, isdn_net_dev, slaves);
 
-	if (!(ISDN_NET_DIALMODE(*lp) == ISDN_NET_DM_AUTO))
-		goto discard;
-
-	if (idev->dialwait_timer <= 0)
-		if (idev->dialstarted > 0 && lp->dialtimeout > 0 && time_before(jiffies, idev->dialstarted + lp->dialtimeout + lp->dialwait))
-			idev->dialwait_timer = idev->dialstarted + lp->dialtimeout + lp->dialwait;
-		
-	if (idev->dialwait_timer > 0) {
-		if(time_before(jiffies, idev->dialwait_timer))
-			goto discard;
+	/* are we dialing already? */
+	if (isdn_net_bound(idev))
+		goto stop_queue;
 
-		idev->dialwait_timer = 0;
-	}
+	if (ISDN_NET_DIALMODE(*mlp) != ISDN_NET_DM_AUTO)
+		goto discard;
 
-	if (isdn_net_force_dial_lp(lp) < 0)
+	if (isdn_net_dial(idev) < 0)
 		goto discard;
 
 	/* Log packet, which triggered dialing */
 	if (dev->net_verbose)
-		isdn_net_log_skb(skb, lp);
+		isdn_net_log_skb(skb, idev);
 
+ stop_queue:
 	netif_stop_queue(ndev);
 	return 1;
 
@@ -1071,103 +383,23 @@
 
 
 /*
- * Try sending a packet.
- * If this interface isn't connected to a ISDN-Channel, find a free channel,
- * and start dialing.
- */
-static int
-isdn_net_start_xmit(struct sk_buff *skb, struct net_device *ndev)
-{
-	isdn_net_local *lp = ndev->priv;
-	isdn_net_dev *idev = lp->netdev;
-	int retval;
-
-	if (lp->p_encap == ISDN_NET_ENCAP_X25IFACE)
-		return isdn_x25_start_xmit(skb, ndev);
-		
-	/* auto-dialing xmit function */
-	isdn_net_adjust_hdr(skb, ndev);
-	isdn_dumppkt("S:", skb->data, skb->len, 40);
-	
-	if (!isdn_net_bound(idev))
-		return isdn_net_autodial(skb, ndev);
-
-	/* Device is bound to an ISDN channel */ 
-	ndev->trans_start = jiffies;
-
-	if (idev->dialstate != ST_ACTIVE) {
-		netif_stop_queue(ndev);
-		return 1;
-	}
-	/* ISDN connection is established, try sending */
-	retval = isdn_net_xmit(ndev, skb);
-	if (retval)
-		netif_stop_queue(ndev);
-
-	return retval;
-}
-
-/*
- * Shutdown a net-interface.
- */
-static int
-isdn_net_close(struct net_device *dev)
-{
-	struct net_device *p;
-	isdn_net_local *lp = dev->priv;
-
-	if (lp->ops->close)
-		lp->ops->close(lp);
-
-	netif_stop_queue(dev);
-	
-	for (p = lp->slave; p; p = ((isdn_net_local *) p->priv)->slave)
-		isdn_net_hangup(p->priv);
-
-	isdn_net_hangup(dev->priv);
-	isdn_MOD_DEC_USE_COUNT();
-	return 0;
-}
-
-/*
- * Get statistics
- */
-static struct net_device_stats *
-isdn_net_get_stats(struct net_device *dev)
-{
-	isdn_net_local *lp = (isdn_net_local *) dev->priv;
-	return &lp->stats;
-}
-
-/*
  * Got a packet from ISDN-Channel.
  */
 static void
-isdn_net_receive(struct net_device *ndev, struct sk_buff *skb)
+isdn_net_receive(isdn_net_dev *idev, struct sk_buff *skb)
 {
-	isdn_net_local *lp = (isdn_net_local *) ndev->priv;
-	isdn_net_dev *idev = lp->netdev;
-	isdn_net_local *olp = lp;	/* original 'lp' */
+	isdn_net_local *mlp = idev->mlp;
 
 	idev->transcount += skb->len;
 
-	lp->stats.rx_packets++;
-	lp->stats.rx_bytes += skb->len;
-	if (lp->master) {
-		/* Bundling: If device is a slave-device, deliver to master, also
-		 * handle master's statistics and hangup-timeout
-		 */
-		ndev = lp->master;
-		lp = (isdn_net_local *) ndev->priv;
-		lp->stats.rx_packets++;
-		lp->stats.rx_bytes += skb->len;
-	}
-	skb->dev = ndev;
+	mlp->stats.rx_packets++;
+	mlp->stats.rx_bytes += skb->len;
+	skb->dev = &mlp->dev;
 	skb->pkt_type = PACKET_HOST;
 	skb->mac.raw = skb->data;
 	isdn_dumppkt("R:", skb->data, skb->len, 40);
 
-	lp->ops->receive(lp->netdev, olp, skb);
+	mlp->ops->receive(mlp, idev, skb);
 }
 
 /*
@@ -1178,73 +410,16 @@
 int
 isdn_net_rcv_skb(int idx, struct sk_buff *skb)
 {
-	isdn_net_dev *idev = isdn_slot_rx_netdev(idx);
+	isdn_net_dev *idev = isdn_slot_idev(idx);
 
-	if (!idev)
+	if (!idev) {
+		HERE;
 		return 0;
-
+	}
 	if (!isdn_net_online(idev))
 		return 0;
 
-	isdn_net_receive(&idev->dev, skb);
-	return 0;
-}
-
-/*
- * Interface-setup. (just after registering a new interface)
- */
-static int
-isdn_net_init(struct net_device *ndev)
-{
-	/* Setup the generic properties */
-
-	ndev->mtu = 1500;
-	ndev->tx_queue_len = 10;
-	ndev->open = &isdn_net_open;
-	ndev->hard_start_xmit = &isdn_net_start_xmit;
-	ndev->hard_header_len = ETH_HLEN + isdn_hard_header_len();
-	ndev->stop = &isdn_net_close;
-	ndev->get_stats = &isdn_net_get_stats;
-
-	return 0;
-}
-
-static int
-isdn_net_do_callback(isdn_net_local *lp)
-{
-	isdn_net_dev *idev = lp->netdev;
-	int slot;
-	/*
-	 * Is the state MANUAL?
-	 * If so, no callback can be made,
-	 * so reject actively.
-	 */
-	if (ISDN_NET_DIALMODE(*lp) == ISDN_NET_DM_OFF) {
-		printk(KERN_INFO "incoming call for callback, interface %s `off' -> rejected\n",
-		       idev->name);
-		return 3;
-	}
-	printk(KERN_DEBUG "%s: start callback\n", idev->name);
-	
-	/* Grab a free ISDN-Channel */
-	slot = isdn_get_free_slot(ISDN_USAGE_NET, lp->l2_proto, lp->l3_proto,
-				  idev->pre_device, idev->pre_channel, lp->msn);
-	if (slot < 0)
-		goto err;
-
-	if (isdn_net_bind_channel(lp, slot) < 0)
-		goto err;
-
-	/* Setup dialstate. */
-	idev->dial_timer.expires = jiffies + lp->cbdelay;
-	idev->dial_event = EV_NET_TIMER_CB;
-	add_timer(&idev->dial_timer);
-	idev->dialstate = ST_WAIT_BEFORE_CB;
-
-	/* Initiate dialing by returning 2 or 4 */
-	return (lp->flags & ISDN_NET_CBHUP) ? 2 : 4;
-
- err:
+	isdn_net_receive(idev, skb);
 	return 0;
 }
 
@@ -1270,13 +445,12 @@
 	char *eaz;
 	unsigned char si1, si2;
 	int match_more = 0;
+	int retval;
 	struct list_head *l;
 	struct isdn_net_phone *n;
 	ulong flags;
 	char nr[32];
 	char *my_eaz;
-	int retval;
-	isdn_ctrl cmd;
 
 	int slot = isdn_dc2minor(di, ch);
 	/* Search name in netdev-chain */
@@ -1314,13 +488,13 @@
 
 	list_for_each(l, &isdn_net_devs) {
 		isdn_net_dev *idev = list_entry(l, isdn_net_dev, global_list);
-		isdn_net_local *lp = &idev->local;
+		isdn_net_local *mlp = idev->mlp;
 
                 /* check acceptable call types for DOV */
-		dbg_net_icall("n_fi: if='%s', l.msn=%s, l.flags=%d, l.dstate=%d\n",
-			      lp->name, lp->msn, lp->flags, lp->dialstate);
+		dbg_net_icall("n_fi: if='%s', l.msn=%s, l.flags=%#x, l.dstate=%d\n",
+			      idev->name, mlp->msn, mlp->flags, idev->fi.state);
 
-                my_eaz = isdn_slot_map_eaz2msn(slot, lp->msn);
+                my_eaz = isdn_slot_map_eaz2msn(slot, mlp->msn);
                 if (si1 == 1) { /* it's a DOV call, check if we allow it */
                         if (*my_eaz == 'v' || *my_eaz == 'V' ||
 			    *my_eaz == 'b' || *my_eaz == 'B')
@@ -1347,7 +521,7 @@
 			continue;
 
 		dbg_net_icall("n_fi: match1, pdev=%d pch=%d\n",
-			      lp->pre_device, lp->pre_channel);
+			      idev->pre_device, idev->pre_channel);
 
 		if (isdn_slot_usage(idx) & ISDN_USAGE_EXCLUSIVE &&
 		    (idev->pre_channel != ch || idev->pre_device != di)) {
@@ -1355,15 +529,12 @@
 			continue;
 		}
 		dbg_net_icall("n_fi: match2\n");
-		if (lp->flags & ISDN_NET_SECURE) {
-			spin_lock_irqsave(&lp->lock, flags);
-			list_for_each_entry(n, &lp->phone[0], list) {
+		if (mlp->flags & ISDN_NET_SECURE) {
+			list_for_each_entry(n, &mlp->phone[0], list) {
 				if (!isdn_msncmp(nr, n->num)) {
-					spin_unlock_irqrestore(&lp->lock, flags);
 					goto found;
 				}
 			}
-			spin_unlock_irqrestore(&lp->lock, flags);
 			continue;
 		}
 	found:
@@ -1375,7 +546,7 @@
 		 * If so, no dialin is allowed,
 		 * so reject actively.
 		 * */
-		if (ISDN_NET_DIALMODE(*lp) == ISDN_NET_DM_OFF) {
+		if (ISDN_NET_DIALMODE(*mlp) == ISDN_NET_DM_OFF) {
 			restore_flags(flags);
 			printk(KERN_INFO "incoming call, interface %s `stopped' -> rejected\n",
 			       idev->name);
@@ -1391,58 +562,15 @@
 			       idev->name);
 			return 3;
 		}
-		/* Interface is up, now see if it's a slave. If so, see if
-		 * it's master and parent slave is online. If not, reject the call.
-		 */
-		if (lp->master) {
-			isdn_net_local *mlp = (isdn_net_local *) lp->master->priv;
-			printk(KERN_DEBUG "ICALLslv: %s\n", idev->name);
-			printk(KERN_DEBUG "master=%s\n", mlp->netdev->name);
-			if (isdn_net_bound(mlp->netdev)) {
-				printk(KERN_DEBUG "master online\n");
-				/* Master is online, find parent-slave (master if first slave) */
-				while (mlp->slave) {
-					if ((isdn_net_local *) mlp->slave->priv == lp)
-						break;
-					mlp = (isdn_net_local *) mlp->slave->priv;
-				}
-			} else
-				printk(KERN_DEBUG "master offline\n");
-			/* Found parent, if it's offline iterate next device */
-			printk(KERN_DEBUG "mlpf: %d\n", isdn_net_bound(mlp->netdev));
-			if (!isdn_net_bound(mlp->netdev)) {
-				continue;
-			}
-		} 
-		if (lp->flags & ISDN_NET_CALLBACK) {
-			retval = isdn_net_do_callback(lp);
-			restore_flags(flags);
-			return retval;
-		}
-		printk(KERN_DEBUG "%s: call from %s -> %s accepted\n", idev->name,
-		       nr, eaz);
-
-		strcpy(isdn_slot_num(idx), nr);
-		isdn_slot_set_usage(idx, (isdn_slot_usage(idx) & ISDN_USAGE_EXCLUSIVE) | ISDN_USAGE_NET);
+                if (mlp->flags & ISDN_NET_CALLBACK) {
+                        retval = isdn_net_do_callback(idev);
+                        restore_flags(flags);
+                        return retval;
+                }
+		printk(KERN_DEBUG "%s: call from %s -> %s accepted\n",
+		       idev->name, nr, eaz);
 
-		isdn_net_bind_channel(lp, idx);
-		
-		idev->outgoing = 0;
-		idev->huptimer = 0;
-		idev->charge_state = ST_CHARGE_NULL;
-		/* Got incoming Call, setup L2 and L3 protocols,
-		 * then wait for D-Channel-connect
-		 */
-		cmd.arg = lp->l2_proto << 8;
-		isdn_slot_command(idev->isdn_slot, ISDN_CMD_SETL2, &cmd);
-		cmd.arg = lp->l3_proto << 8;
-		isdn_slot_command(idev->isdn_slot, ISDN_CMD_SETL3, &cmd);
-		
-		idev->dial_timer.expires = jiffies + 15 * HZ;
-		idev->dial_event = EV_NET_TIMER_IN_DCONN;
-		add_timer(&idev->dial_timer);
-		idev->dialstate = ST_IN_WAIT_DCONN;
-		
+		isdn_net_accept(idev, idx, nr);
 		restore_flags(flags);
 		return 1;
 	}
@@ -1453,753 +581,19 @@
 }
 
 /*
- * Search list of net-interfaces for an interface with given name.
- */
-isdn_net_dev *
-isdn_net_findif(char *name)
-{
-	struct list_head *l;
-
-	list_for_each(l, &isdn_net_devs) {
-		isdn_net_dev *p = list_entry(l, isdn_net_dev, global_list);
-		if (!strcmp(p->name, name))
-			return p;
-	}
-	return NULL;
-}
-
-/*
- * Force a net-interface to dial out.
- * This is called from the userlevel-routine below or
- * from isdn_net_start_xmit().
- */
-static int
-isdn_net_force_dial_lp(isdn_net_local *lp)
-{
-	isdn_net_dev *idev = lp->netdev;
-	int slot;
-	unsigned long flags;
-
-	if (isdn_net_bound(idev))
-		return -EBUSY;
-
-	save_flags(flags);
-	cli();
-
-	if (idev->exclusive >= 0)
-		slot = idev->exclusive;
-	else
-		slot = isdn_get_free_slot(ISDN_USAGE_NET, lp->l2_proto,
-					  lp->l3_proto, idev->pre_device, 
-					  idev->pre_channel, lp->msn);
-	if (slot < 0)
-		goto err;
-
-	if (isdn_net_bind_channel(lp, slot) < 0)
-		goto err;;
-
-	/* Initiate dialing */
-	restore_flags(flags);
-	init_dialout(lp);
-
-	return 0;
-
- err:
-	restore_flags(flags);
-	return -EAGAIN;
-}
-
-/*
  * This is called from certain upper protocol layers (multilink ppp
  * and x25iface encapsulation module) that want to initiate dialing
  * themselves.
  */
 int
-isdn_net_dial_req(isdn_net_local * lp)
+isdn_net_dial_req(isdn_net_dev *idev)
 {
+	isdn_net_local *mlp = idev->mlp;
 	/* is there a better error code? */
-	if (!(ISDN_NET_DIALMODE(*lp) == ISDN_NET_DM_AUTO)) return -EBUSY;
-
-	return isdn_net_force_dial_lp(lp);
-}
-
-/*
- * Force a net-interface to dial out.
- * This is always called from within userspace (ISDN_IOCTL_NET_DIAL).
- */
-int
-isdn_net_force_dial(char *name)
-{
-	isdn_net_dev *p = isdn_net_findif(name);
-
-	if (!p)
-		return -ENODEV;
-	return (isdn_net_force_dial_lp(&p->local));
-}
-
-/*
- * Allocate a new network-interface and initialize its data structures.
- */
-int
-isdn_net_new(char *name, struct net_device *master)
-{
-	int retval;
-	isdn_net_dev *netdev;
-
-	/* Avoid creating an existing interface */
-	if (isdn_net_findif(name)) {
-		printk(KERN_WARNING "isdn_net: interface %s already exists\n", name);
-		return -EEXIST;
-	}
-	if (!(netdev = kmalloc(sizeof(isdn_net_dev), GFP_KERNEL))) {
-		printk(KERN_WARNING "isdn_net: Could not allocate net-device\n");
-		return -ENOMEM;
-	}
-	memset(netdev, 0, sizeof(isdn_net_dev));
-	strcpy(netdev->name, name);
-	strcpy(netdev->dev.name, name);
-	netdev->dev.priv = &netdev->local;
-	netdev->dev.init = isdn_net_init;
-	if (master) {
-		/* Device shall be a slave */
-		struct net_device *p = (((isdn_net_local *) master->priv)->slave);
-		struct net_device *q = master;
-
-		netdev->local.master = master;
-		/* Put device at end of slave-chain */
-		while (p) {
-			q = p;
-			p = (((isdn_net_local *) p->priv)->slave);
-		}
-		((isdn_net_local *) q->priv)->slave = &(netdev->dev);
-	} else {
-		/* Device shall be a master */
-		/*
-		 * Watchdog timer (currently) for master only.
-		 */
-		netdev->dev.tx_timeout = isdn_net_tx_timeout;
-		netdev->dev.watchdog_timeo = ISDN_NET_TX_TIMEOUT;
-		retval = register_netdev(&netdev->dev);
-		if (retval) {
-			printk(KERN_WARNING "isdn_net: Could not register net-device\n");
-			kfree(netdev);
-			return retval;
-		}
-	}
-	netdev->local.magic = ISDN_NET_MAGIC;
-
-	netdev->queue = &netdev->local;
-	spin_lock_init(&netdev->queue_lock);
-
-	netdev->local.last = &netdev->local;
-	netdev->local.netdev = netdev;
-	netdev->local.next = &netdev->local;
-
-	netdev->local.tqueue.sync = 0;
-	netdev->local.tqueue.routine = isdn_net_softint;
-	netdev->local.tqueue.data = &netdev->local;
-	spin_lock_init(&netdev->local.xmit_lock);
-
-	netdev->isdn_slot = -1;
-	netdev->pre_device = -1;
-	netdev->pre_channel = -1;
-	netdev->exclusive = -1;
-
-	netdev->ppp_slot = -1;
-	netdev->pppbind = -1;
-
-	netdev->local.p_encap = -1;
-	skb_queue_head_init(&netdev->local.super_tx_queue);
-	netdev->local.l2_proto = ISDN_PROTO_L2_X75I;
-	netdev->local.l3_proto = ISDN_PROTO_L3_TRANS;
-	netdev->local.triggercps = 6000;
-	netdev->local.slavedelay = 10 * HZ;
-	netdev->local.hupflags = ISDN_INHUP;	/* Do hangup even on incoming calls */
-	netdev->local.onhtime = 10;	/* Default hangup-time for saving costs
-	   of those who forget configuring this */
-	netdev->local.dialmax = 1;
-	netdev->local.flags = ISDN_NET_CBHUP | ISDN_NET_DM_MANUAL;	/* Hangup before Callback, manual dial */
-	netdev->local.cbdelay = 5 * HZ;	/* Wait 5 secs before Callback */
-	netdev->local.dialtimeout = -1;  /* Infinite Dial-Timeout */
-	netdev->local.dialwait = 5 * HZ; /* Wait 5 sec. after failed dial */
-	netdev->dialstarted = 0;   /* Jiffies of last dial-start */
-	netdev->dialwait_timer = 0;  /* Jiffies of earliest next dial-start */
-
-	init_timer(&netdev->dial_timer);
-	netdev->dial_timer.data = (unsigned long) netdev;
-	netdev->dial_timer.function = isdn_net_dial_timer;
-	init_timer(&netdev->hup_timer);
-	netdev->hup_timer.data = (unsigned long) netdev;
-	netdev->hup_timer.function = isdn_net_hup_timer;
-	spin_lock_init(&netdev->local.lock);
-	INIT_LIST_HEAD(&netdev->local.phone[0]);
-	INIT_LIST_HEAD(&netdev->local.phone[1]);
-	isdn_net_set_encap(netdev, ISDN_NET_ENCAP_RAWIP);
-
-	/* Put into to netdev-chain */
-	list_add(&netdev->global_list, &isdn_net_devs);
-	return 0;
-}
-
-int
-isdn_net_newslave(char *parm)
-{
-	char *p = strchr(parm, ',');
-	isdn_net_dev *m;
-
-	/* Slave-Name MUST not be empty */
-	if (!p || !p[1])
-		return -EINVAL;
-
-	*p = 0;
-	/* Master must already exist */
-	if (!(m = isdn_net_findif(parm)))
-		return -ESRCH;
-	/* Master must be a real interface, not a slave */
-	if (m->local.master)
-		return -ENXIO;
-	/* Master must not be started yet */
-	if (isdn_net_device_started(m)) 
-		return -EBUSY;
-
-	return isdn_net_new(p+1, &m->dev);
-}
-
-static int
-isdn_net_set_encap(isdn_net_dev *p, int encap)
-{
-	isdn_net_local *lp = &p->local;
-	int retval = 0;
-
-	if (lp->p_encap == encap){
-		/* nothing to do */
-		retval = 0;
-		goto out;
-	}
-	if (isdn_net_device_started(p)) {
-		retval = -EBUSY;
-		goto out;
-	}
-	if (lp->ops && lp->ops->cleanup)
-		lp->ops->cleanup(lp);
-
-	if (encap < 0 || encap >= ISDN_NET_ENCAP_NR) {
-		lp->p_encap = -1;
-		lp->ops = NULL;
-		retval = -EINVAL;
-		goto out;
-	}
-
-	lp->p_encap = encap;
-	lp->ops = netif_ops[encap];
-
-	p->dev.hard_header         = lp->ops->hard_header;
-	p->dev.do_ioctl            = lp->ops->do_ioctl;
-	p->dev.flags               = lp->ops->flags;
-	p->dev.type                = lp->ops->type;
-	p->dev.addr_len            = lp->ops->addr_len;
-	if (lp->ops->init)
-		retval = lp->ops->init(lp);
-
-	if (retval != 0) {
-		lp->p_encap = -1;
-		lp->ops = NULL;
-	}
- out:
-	return retval;
-}
-
-static int
-isdn_net_bind(isdn_net_dev *idev, isdn_net_ioctl_cfg *cfg)
-{
-	isdn_net_local *lp = &idev->local;
-	int i, retval;
-	int drvidx = -1;
-	int chidx = -1;
-	char drvid[25];
-
-	strncpy(drvid, cfg->drvid, 24);
-	drvid[24] = 0;
-
-	if (cfg->exclusive && !strlen(drvid)) {
-		/* If we want to bind exclusively, need to specify drv/chan */
-		retval = -ENODEV;
-		goto out;
-	}
-	if (strlen(drvid)) {
-		/* A bind has been requested ... */
-		char *c = strchr(drvid, ',');
-		if (!c) {
-			retval = -ENODEV;
-			goto out;
-		}
-		/* The channel-number is appended to the driver-Id with a comma */
-		*c = 0;
-		chidx = simple_strtol(c + 1, NULL, 10);
-
-		for (i = 0; i < ISDN_MAX_DRIVERS; i++) {
-			/* Lookup driver-Id in array */
-			if (!strcmp(dev->drvid[i], drvid)) {
-				drvidx = i;
-				break;
-			}
-		}
-		if (drvidx == -1 || chidx == -1) {
-			/* Either driver-Id or channel-number invalid */
-			retval = -ENODEV;
-			goto out;
-		}
-	}
-	if (cfg->exclusive == (idev->exclusive >= 0) &&
-	    drvidx == idev->pre_device && chidx == idev->pre_channel) {
-		/* no change */
-		retval = 0;
-		goto out;
-	}
-	if (idev->exclusive >= 0) {
-		isdn_unexclusive_channel(idev->pre_device, idev->pre_channel);
-		isdn_free_channel(idev->pre_device, idev->pre_channel, ISDN_USAGE_NET);
-		idev->exclusive = -1;
-	}
-	if (cfg->exclusive) {
-		/* If binding is exclusive, try to grab the channel */
-		idev->exclusive = isdn_get_free_slot(ISDN_USAGE_NET, lp->l2_proto, 
-						     lp->l3_proto, drvidx, chidx, cfg->eaz);
-		if (idev->exclusive < 0) {
-			/* Grab failed, because desired channel is in use */
-			retval = -EBUSY;
-			goto out;
-		}
-		/* All went ok, so update isdninfo */
-		isdn_slot_set_usage(idev->exclusive, ISDN_USAGE_EXCLUSIVE);
-	}
-	idev->pre_device = drvidx;
-	idev->pre_channel = chidx;
-	retval = 0;
- out:
-	return retval;
-}
-
-/*
- * Set interface-parameters.
- * Always set all parameters, so the user-level application is responsible
- * for not overwriting existing setups. It has to get the current
- * setup first, if only selected parameters are to be changed.
- */
-int
-isdn_net_setcfg(isdn_net_ioctl_cfg *cfg)
-{
-	isdn_net_dev *idev = isdn_net_findif(cfg->name);
-	isdn_net_local *lp = &idev->local;
-	ulong features;
-	int i, retval;
-
-	if (!idev) {
-		retval = -ENODEV;
-		goto out;
-	}
-	/* See if any registered driver supports the features we want */
-	features = ((1 << cfg->l2_proto) << ISDN_FEATURE_L2_SHIFT) |
-		   ((1 << cfg->l3_proto) << ISDN_FEATURE_L3_SHIFT);
-	for (i = 0; i < ISDN_MAX_DRIVERS; i++)
-		if (dev->drv[i] &&
-		    (dev->drv[i]->interface->features & features) == features)
-				break;
-
-	if (i == ISDN_MAX_DRIVERS) {
-		printk(KERN_WARNING "isdn_net: No driver with selected features\n");
-		retval = -ENODEV;
-		goto out;
-	}
-
-	retval = isdn_net_set_encap(idev, cfg->p_encap);
-	if (retval)
-		goto out;
-
-	retval = isdn_net_bind(idev, cfg);
-	if (retval)
-		goto out;
-
-	strncpy(lp->msn, cfg->eaz, ISDN_MSNLEN-1);
-	lp->msn[ISDN_MSNLEN-1] = 0;
-	lp->onhtime = cfg->onhtime;
-	idev->charge = cfg->charge;
-	lp->l2_proto = cfg->l2_proto;
-	lp->l3_proto = cfg->l3_proto;
-	lp->cbdelay = cfg->cbdelay * HZ / 5;
-	lp->dialmax = cfg->dialmax;
-	lp->triggercps = cfg->triggercps;
-	lp->slavedelay = cfg->slavedelay * HZ;
-	idev->pppbind = cfg->pppbind;
-	lp->dialtimeout = cfg->dialtimeout >= 0 ? cfg->dialtimeout * HZ : -1;
-	lp->dialwait = cfg->dialwait * HZ;
-	if (cfg->secure)
-		lp->flags |= ISDN_NET_SECURE;
-	else
-		lp->flags &= ~ISDN_NET_SECURE;
-	if (cfg->cbhup)
-		lp->flags |= ISDN_NET_CBHUP;
-	else
-		lp->flags &= ~ISDN_NET_CBHUP;
-	switch (cfg->callback) {
-	case 0:
-		lp->flags &= ~(ISDN_NET_CALLBACK | ISDN_NET_CBOUT);
-		break;
-	case 1:
-		lp->flags |= ISDN_NET_CALLBACK;
-		lp->flags &= ~ISDN_NET_CBOUT;
-		break;
-	case 2:
-		lp->flags |= ISDN_NET_CBOUT;
-		lp->flags &= ~ISDN_NET_CALLBACK;
-		break;
-	}
-	lp->flags &= ~ISDN_NET_DIALMODE_MASK;	/* first all bits off */
-	if (cfg->dialmode && !(cfg->dialmode & ISDN_NET_DIALMODE_MASK)) {
-		retval = -EINVAL;
-		goto out;
-	}
-
-	lp->flags |= cfg->dialmode;  /* turn on selected bits */
-	if (lp->flags & ISDN_NET_DM_OFF)
-		isdn_net_hangup(idev);
-
-	if (cfg->chargehup)
-		lp->hupflags |= ISDN_CHARGEHUP;
-	else
-		lp->hupflags &= ~ISDN_CHARGEHUP;
-
-	if (cfg->ihup)
-		lp->hupflags |= ISDN_INHUP;
-	else
-		lp->hupflags &= ~ISDN_INHUP;
-
-	if (cfg->chargeint > 10) {
-		idev->chargeint = cfg->chargeint * HZ;
-		idev->charge_state = ST_CHARGE_HAVE_CINT;
-		lp->hupflags |= ISDN_MANCHARGE;
-	}
-	retval = 0;
-
- out:
-	return retval;
-}
-
-/*
- * Perform get-interface-parameters.ioctl
- */
-int
-isdn_net_getcfg(isdn_net_ioctl_cfg * cfg)
-{
-	isdn_net_dev *idev = isdn_net_findif(cfg->name);
-	isdn_net_local *lp = &idev->local;
-		
-	if (!idev)
-		return -ENODEV;
-
-	strcpy(cfg->eaz, lp->msn);
-	cfg->exclusive = idev->exclusive >= 0;
-	if (idev->pre_device >= 0) {
-		sprintf(cfg->drvid, "%s,%d", dev->drvid[idev->pre_device],
-			idev->pre_channel);
-	} else
-		cfg->drvid[0] = '\0';
-	cfg->onhtime = lp->onhtime;
-	cfg->charge = idev->charge;
-	cfg->l2_proto = lp->l2_proto;
-	cfg->l3_proto = lp->l3_proto;
-	cfg->p_encap = lp->p_encap;
-	cfg->secure = (lp->flags & ISDN_NET_SECURE) ? 1 : 0;
-	cfg->callback = 0;
-	if (lp->flags & ISDN_NET_CALLBACK)
-		cfg->callback = 1;
-	if (lp->flags & ISDN_NET_CBOUT)
-		cfg->callback = 2;
-	cfg->cbhup = (lp->flags & ISDN_NET_CBHUP) ? 1 : 0;
-	cfg->dialmode = lp->flags & ISDN_NET_DIALMODE_MASK;
-	cfg->chargehup = (lp->hupflags & ISDN_CHARGEHUP) ? 1 : 0;
-	cfg->ihup = (lp->hupflags & ISDN_INHUP) ? 1 : 0;
-	cfg->cbdelay = lp->cbdelay * 5 / HZ;
-	cfg->dialmax = lp->dialmax;
-	cfg->triggercps = lp->triggercps;
-	cfg->slavedelay = lp->slavedelay / HZ;
-	cfg->chargeint = (lp->hupflags & ISDN_CHARGEHUP) ?
-		(idev->chargeint / HZ) : 0;
-	cfg->pppbind = idev->pppbind;
-	cfg->dialtimeout = lp->dialtimeout >= 0 ? lp->dialtimeout / HZ : -1;
-	cfg->dialwait = lp->dialwait / HZ;
-	if (lp->slave)
-		strcpy(cfg->slave, ((isdn_net_local *) lp->slave->priv)->netdev->name);
-	else
-		cfg->slave[0] = '\0';
-	if (lp->master)
-		strcpy(cfg->master, ((isdn_net_local *) lp->master->priv)->netdev->name);
-	else
-		cfg->master[0] = '\0';
-
-	return 0;
-}
-
-/*
- * Add a phone-number to an interface.
- */
-int
-isdn_net_addphone(isdn_net_ioctl_phone * phone)
-{
-	isdn_net_dev *p = isdn_net_findif(phone->name);
-	unsigned long flags;
-	struct isdn_net_phone *n;
-
-	if (!p)
-		return -ENODEV;
-
-	n = kmalloc(sizeof(*n), GFP_KERNEL);
-	if (!n)
-		return -ENOMEM;
-
-	strcpy(n->num, phone->phone);
-	spin_lock_irqsave(&p->local.lock, flags);
-	list_add_tail(&n->list, &p->local.phone[phone->outgoing & 1]);
-	spin_unlock_irqrestore(&p->local.lock, flags);
-	return 0;
-}
-
-/*
- * Copy a string of all phone-numbers of an interface to user space.
- * This might sleep and must be called with the isdn semaphore down.
- */
-int
-isdn_net_getphones(isdn_net_ioctl_phone * phone, char *phones)
-{
-	isdn_net_dev *p = isdn_net_findif(phone->name);
-	unsigned long flags;
-	int inout = phone->outgoing & 1;
-	int count = 0;
-	char *buf = (char *)__get_free_page(GFP_KERNEL);
-	struct isdn_net_phone *n;
-
-	if (!p)
-		return -ENODEV;
-
-	if (!buf)
-		return -ENOMEM;
-
-	inout &= 1;
-	spin_lock_irqsave(&p->local.lock, flags);
-	list_for_each_entry(n, &p->local.phone[inout], list) {
-		strcpy(&buf[count], n->num);
-		count += strlen(n->num);
-		buf[count++] = ' ';
-		if (count > PAGE_SIZE - ISDN_MSNLEN - 1)
-			break;
-	}
-	spin_unlock_irqrestore(&p->local.lock, flags);
-	if (!count)
-		count++;
-
-	buf[count-1] = 0;
-
-	if (copy_to_user(phones, buf, count))
-		count = -EFAULT;
-
-	free_page((unsigned long)buf);
-	return count;
-}
-
-/*
- * Copy a string containing the peer's phone number of a connected interface
- * to user space.
- */
-int
-isdn_net_getpeer(isdn_net_ioctl_phone *phone, isdn_net_ioctl_phone *peer)
-{
-	isdn_net_dev *p = isdn_net_findif(phone->name);
-	int idx;
-
-	if (!p) return -ENODEV;
-	/*
-	 * Theoretical race: while this executes, the remote number might
-	 * become invalid (hang up) or change (new connection), resulting
-         * in (partially) wrong number copied to user. This race
-	 * currently ignored.
-	 */
-	idx = p->isdn_slot;
-	if (idx<0) return -ENOTCONN;
-	/* for pre-bound channels, we need this extra check */
-	if (strncmp(isdn_slot_num(idx),"???",3) == 0 ) return -ENOTCONN;
-	strncpy(phone->phone,isdn_slot_num(idx),ISDN_MSNLEN);
-	phone->outgoing=USG_OUTGOING(isdn_slot_usage(idx));
-	if ( copy_to_user(peer,phone,sizeof(*peer)) ) return -EFAULT;
-	return 0;
-}
-/*
- * Delete a phone-number from an interface.
- */
-int
-isdn_net_delphone(isdn_net_ioctl_phone * phone)
-{
-	isdn_net_dev *p = isdn_net_findif(phone->name);
-	int inout = phone->outgoing & 1;
-	struct isdn_net_phone *n;
-	unsigned long flags;
-	int retval;
-
-	if (!p)
-		return -ENODEV;
-
-	retval = -EINVAL;
-	spin_lock_irqsave(&p->local.lock, flags);
-	list_for_each_entry(n, &p->local.phone[inout], list) {
-		if (!strcmp(n->num, phone->phone)) {
-			list_del(&n->list);
-			kfree(n);
-			retval = 0;
-			break;
-		}
-	}
-	spin_unlock_irqrestore(&p->local.lock, flags);
-	return retval;
-}
-
-/*
- * Delete all phone-numbers of an interface.
- */
-static int
-isdn_net_rmallphone(isdn_net_dev * p)
-{
-	struct isdn_net_phone *n;
-	unsigned long flags;
-	int i;
-
-	spin_lock_irqsave(&p->local.lock, flags);
-	for (i = 0; i < 2; i++) {
-		while (!list_empty(&p->local.phone[i])) {
-			n = list_entry(p->local.phone[i].next, struct isdn_net_phone, list);
-			list_del(&n->list);
-			kfree(n);
-		}
-	}
-	spin_lock_irqsave(&p->local.lock, flags);
-	return 0;
-}
-
-/*
- * Force a hangup of a network-interface.
- */
-int
-isdn_net_force_hangup(char *name)
-{
-	isdn_net_dev *idev = isdn_net_findif(name);
-	struct net_device *q;
-
-	if (!idev)
-		return -ENODEV;
-
-	if (idev->isdn_slot < 0)
-		return -ENOTCONN;
-
-	q = idev->local.slave;
-	/* If this interface has slaves, do a hangup for them also. */
-	while (q) {
-		isdn_net_hangup(((isdn_net_local *) q->priv)->netdev);
-		q = (((isdn_net_local *) q->priv)->slave);
-	}
-	isdn_net_hangup(idev);
-	return 0;
-}
-
-/*
- * Helper-function for isdn_net_rm: Do the real work.
- */
-static int
-isdn_net_realrm(isdn_net_dev *p)
-{
-	unsigned long flags;
-
-	save_flags(flags);
-	cli();
-	if (isdn_net_device_started(p)) {
-		restore_flags(flags);
+	if (ISDN_NET_DIALMODE(*mlp) != ISDN_NET_DM_AUTO)
 		return -EBUSY;
-	}
-	isdn_net_set_encap(p, -1);
-
-	/* Free all phone-entries */
-	isdn_net_rmallphone(p);
-	/* If interface is bound exclusive, free channel-usage */
-	if (p->exclusive >= 0)
-		isdn_unexclusive_channel(p->pre_device, p->pre_channel);
-	if (p->local.master) {
-		/* It's a slave-device, so update master's slave-pointer if necessary */
-		if (((isdn_net_local *) (p->local.master->priv))->slave == &p->dev)
-			((isdn_net_local *) (p->local.master->priv))->slave = p->local.slave;
-	} else {
-		/* Unregister only if it's a master-device */
-		unregister_netdev(&p->dev);
-	}
-	/* Unlink device from chain */
-	list_del(&p->global_list);
-	if (p->local.slave) {
-		/* If this interface has a slave, remove it also */
-		char *slavename = ((isdn_net_local *) (p->local.slave->priv))->netdev->name;
-		struct list_head *l;
-
-		list_for_each(l, &isdn_net_devs) {
-			isdn_net_dev *n = list_entry(l, isdn_net_dev, global_list);
-			if (!strcmp(n->name, slavename)) {
-				isdn_net_realrm(n);
-				break;
-			}
-		}
-	}
-	restore_flags(flags);
-	kfree(p);
-
-	return 0;
-}
-
-/*
- * Remove a single network-interface.
- */
-int
-isdn_net_rm(char *name)
-{
-	struct list_head *l;
-
-	/* Search name in netdev-chain */
-	list_for_each(l, &isdn_net_devs) {
-		isdn_net_dev *p = list_entry(l, isdn_net_dev, global_list);
-		if (!strcmp(p->name, name))
-			return isdn_net_realrm(p);
-	}
-	return -ENODEV;
-}
 
-/*
- * Remove all network-interfaces
- */
-int
-isdn_net_rmall(void)
-{
-	unsigned long flags;
-	int ret;
-
-	/* Walk through netdev-chain */
-	save_flags(flags);
-	cli();
-	while (!list_empty(&isdn_net_devs)) {
-		isdn_net_dev *p = list_entry(isdn_net_devs.next, isdn_net_dev, global_list);
-
-		/* Remove master-devices only, slaves get removed with their master */
-		if (!p->local.master) {
-			if ((ret = isdn_net_realrm(p))) {
-				restore_flags(flags);
-				return ret;
-			}
-		}
-	}
-	restore_flags(flags);
-	return 0;
+	return isdn_net_dial(idev);
 }
 
 // ISDN_NET_ENCAP_IPTYP
@@ -2216,16 +610,17 @@
 }
 
 static void
-isdn_iptyp_receive(isdn_net_dev *p, isdn_net_local *olp, 
+isdn_iptyp_receive(isdn_net_local *lp, isdn_net_dev *idev, 
 		   struct sk_buff *skb)
 {
-	isdn_net_reset_huptimer(p, olp->netdev);
+	idev->huptimer = 0;
 	get_u16(skb->data, &skb->protocol);
 	skb_pull(skb, 2);
 	netif_rx(skb);
 }
 
 static struct isdn_netif_ops iptyp_ops = {
+	.hard_start_xmit     = isdn_net_start_xmit,
 	.hard_header         = isdn_iptyp_header,
 	.flags               = IFF_NOARP | IFF_POINTOPOINT,
 	.type                = ARPHRD_PPP,
@@ -2247,16 +642,17 @@
 }
 
 static void
-isdn_uihdlc_receive(isdn_net_dev *p, isdn_net_local *olp, 
+isdn_uihdlc_receive(isdn_net_local *lp, isdn_net_dev *idev, 
 		    struct sk_buff *skb)
 {
-	isdn_net_reset_huptimer(p, olp->netdev);
+	idev->huptimer = 0;
 	skb_pull(skb, 2);
 	skb->protocol = htons(ETH_P_IP);
 	netif_rx(skb);
 }
 
 static struct isdn_netif_ops uihdlc_ops = {
+	.hard_start_xmit     = isdn_net_start_xmit,
 	.hard_header         = isdn_uihdlc_header,
 	.flags               = IFF_NOARP | IFF_POINTOPOINT,
 	.type                = ARPHRD_HDLC,
@@ -2269,15 +665,16 @@
 // ======================================================================
 
 static void
-isdn_rawip_receive(isdn_net_dev *p, isdn_net_local *olp, 
+isdn_rawip_receive(isdn_net_local *lp, isdn_net_dev *idev, 
 		   struct sk_buff *skb)
 {
-	isdn_net_reset_huptimer(p, olp->netdev);
+	idev->huptimer = 0;
 	skb->protocol = htons(ETH_P_IP);
 	netif_rx(skb);
 }
 
 static struct isdn_netif_ops rawip_ops = {
+	.hard_start_xmit     = isdn_net_start_xmit,
 	.flags               = IFF_NOARP | IFF_POINTOPOINT,
 	.type                = ARPHRD_PPP,
 	.receive             = isdn_rawip_receive,
@@ -2287,73 +684,19 @@
 // Ethernet over ISDN
 // ======================================================================
 
-/*      This is simply a copy from std. eth.c EXCEPT we pull ETH_HLEN
- *      instead of dev->hard_header_len off. This is done because the
- *      lowlevel-driver has already pulled off its stuff when we get
- *      here and this routine only gets called with p_encap == ETHER.
- *      Determine the packet's protocol ID. The rule here is that we
- *      assume 802.3 if the type field is short enough to be a length.
- *      This is normal practice and works for any 'now in use' protocol.
- *      FIXME
- */
-
-static unsigned short
-isdn_eth_type_trans(struct sk_buff *skb, struct net_device *dev)
-{
-	struct ethhdr *eth;
-	unsigned char *rawp;
-
-	skb->mac.raw = skb->data;
-	skb_pull(skb, ETH_HLEN);
-	eth = skb->mac.ethernet;
-
-	if (*eth->h_dest & 1) {
-		if (memcmp(eth->h_dest, dev->broadcast, ETH_ALEN) == 0)
-			skb->pkt_type = PACKET_BROADCAST;
-		else
-			skb->pkt_type = PACKET_MULTICAST;
-	}
-	/*
-	 *      This ALLMULTI check should be redundant by 1.4
-	 *      so don't forget to remove it.
-	 */
-
-	else if (dev->flags & (IFF_PROMISC /*| IFF_ALLMULTI*/)) {
-		if (memcmp(eth->h_dest, dev->dev_addr, ETH_ALEN))
-			skb->pkt_type = PACKET_OTHERHOST;
-	}
-	if (ntohs(eth->h_proto) >= 1536)
-		return eth->h_proto;
-
-	rawp = skb->data;
-
-	/*
-	 *      This is a magic hack to spot IPX packets. Older Novell breaks
-	 *      the protocol design and runs IPX over 802.3 without an 802.2 LLC
-	 *      layer. We look for FFFF which isn't a used 802.2 SSAP/DSAP. This
-	 *      won't work for fault tolerant netware but does for the rest.
-	 */
-	if (*(unsigned short *) rawp == 0xFFFF)
-		return htons(ETH_P_802_3);
-	/*
-	 *      Real 802.2 LLC
-	 */
-	return htons(ETH_P_802_2);
-}
-
 static void
-isdn_ether_receive(isdn_net_dev *p, isdn_net_local *olp, 
+isdn_ether_receive(isdn_net_local *lp, isdn_net_dev *idev, 
 		   struct sk_buff *skb)
 {
-	isdn_net_reset_huptimer(p, olp->netdev);
-	skb->protocol = isdn_eth_type_trans(skb, skb->dev);
+	idev->huptimer = 0;
+	skb->protocol = eth_type_trans(skb, skb->dev);
 	netif_rx(skb);
 }
 
 static int
 isdn_ether_open(isdn_net_local *lp)
 {
-	struct net_device *dev = &lp->netdev->dev;
+	struct net_device *dev = &lp->dev;
 	struct in_device *in_dev;
 	int i;
 
@@ -2373,7 +716,7 @@
 static int
 isdn_ether_init(isdn_net_local *lp)
 {
-	struct net_device *dev = &lp->netdev->dev;
+	struct net_device *dev = &lp->dev;
 
 	ether_setup(dev);
 	dev->tx_queue_len = 10;
@@ -2383,6 +726,7 @@
 }
 
 static struct isdn_netif_ops ether_ops = {
+	.hard_start_xmit     = isdn_net_start_xmit,
 	.hard_header         = eth_header,
 	.receive             = isdn_ether_receive,
 	.init                = isdn_ether_init,
@@ -2392,7 +736,7 @@
 // ======================================================================
 
 void
-isdn_net_init_module(void)
+isdn_net_init(void)
 {
 	register_isdn_netif(ISDN_NET_ENCAP_ETHER,      &ether_ops);
 	register_isdn_netif(ISDN_NET_ENCAP_RAWIP,      &rawip_ops);
@@ -2406,4 +750,7 @@
 #ifdef CONFIG_ISDN_PPP
 	register_isdn_netif(ISDN_NET_ENCAP_SYNCPPP,    &isdn_ppp_ops);
 #endif
+
+	isdn_net_lib_init();
 }
+
diff -Nru a/drivers/isdn/i4l/isdn_net.h b/drivers/isdn/i4l/isdn_net.h
--- a/drivers/isdn/i4l/isdn_net.h	Mon Oct  7 11:25:37 2002
+++ b/drivers/isdn/i4l/isdn_net.h	Mon Oct  7 11:25:37 2002
@@ -32,46 +32,49 @@
 #define CISCO_SLARP_REPLY     1
 #define CISCO_SLARP_KEEPALIVE 2
 
-extern void isdn_net_init_module(void);
+extern void isdn_net_init(void);
+extern void isdn_net_exit(void);
+extern void isdn_net_lib_init(void);
+extern void isdn_net_lib_exit(void);
+extern void isdn_net_hangup_all(void);
+extern int isdn_net_ioctl(struct inode *, struct file *, uint, ulong);
+
+extern int register_isdn_netif(int encap, struct isdn_netif_ops *ops);
+extern int isdn_net_autodial(struct sk_buff *skb, struct net_device *ndev);
+extern int isdn_net_start_xmit(struct sk_buff *skb, struct net_device *ndev);
+
+extern int  isdn_net_bind_channel(isdn_net_dev *idev, int slot);
+extern void isdn_net_unbind_channel(isdn_net_dev *idev);
+extern int  isdn_net_dial(isdn_net_dev *idev);
+extern void isdn_net_accept(isdn_net_dev *idev, int slot, char *nr);
+extern int  isdn_net_do_callback(isdn_net_dev *idev);
+
+extern int  isdn_net_bsent(isdn_net_dev *idev, isdn_ctrl *c);
 
-extern int isdn_net_new(char *, struct net_device *);
-extern int isdn_net_newslave(char *);
-extern int isdn_net_rm(char *);
-extern int isdn_net_rmall(void);
 extern int isdn_net_stat_callback(int, isdn_ctrl *);
-extern int isdn_net_setcfg(isdn_net_ioctl_cfg *);
-extern int isdn_net_getcfg(isdn_net_ioctl_cfg *);
-extern int isdn_net_addphone(isdn_net_ioctl_phone *);
-extern int isdn_net_getphones(isdn_net_ioctl_phone *, char *);
-extern int isdn_net_getpeer(isdn_net_ioctl_phone *, isdn_net_ioctl_phone *);
-extern int isdn_net_delphone(isdn_net_ioctl_phone *);
 extern int isdn_net_find_icall(int, int, int, setup_parm *);
-extern void isdn_net_hangup(isdn_net_dev *);
-extern void isdn_net_hangup_all(void);
-extern int isdn_net_force_hangup(char *);
-extern int isdn_net_force_dial(char *);
-extern isdn_net_dev *isdn_net_findif(char *);
+extern int isdn_net_hangup(isdn_net_dev *);
 extern int isdn_net_rcv_skb(int, struct sk_buff *);
-extern int isdn_net_dial_req(isdn_net_local *);
-extern void isdn_net_writebuf_skb(isdn_net_local *lp, struct sk_buff *skb);
-extern void isdn_net_write_super(isdn_net_local *lp, struct sk_buff *skb);
-extern int isdn_net_online(isdn_net_dev *idev);
-
-static inline void
-isdn_net_reset_huptimer(isdn_net_dev *idev, isdn_net_dev *idev2)
-{
-	idev->huptimer = 0;
-	idev2->huptimer = 0;
-}
+extern int isdn_net_dial_req(isdn_net_dev *);
+extern void isdn_net_writebuf_skb(isdn_net_dev *, struct sk_buff *skb);
+extern void isdn_net_write_super(isdn_net_dev *, struct sk_buff *skb);
+extern int isdn_net_online(isdn_net_dev *);
+
+enum {
+	ST_CHARGE_NULL,
+	ST_CHARGE_GOT_CINF,  /* got a first charge info */
+	ST_CHARGE_HAVE_CINT, /* got a second chare info and thus the timing */
+};
 
 #define ISDN_NET_MAX_QUEUE_LENGTH 2
 
 /*
  * is this particular channel busy?
  */
-static __inline__ int isdn_net_lp_busy(isdn_net_local *lp)
+static inline int
+isdn_net_dev_busy(isdn_net_dev *idev)
 {
-	if (atomic_read(&lp->frame_cnt) < ISDN_NET_MAX_QUEUE_LENGTH)
+	if (atomic_read(&idev->frame_cnt) < ISDN_NET_MAX_QUEUE_LENGTH)
 		return 0;
 	else 
 		return 1;
@@ -81,86 +84,69 @@
  * For the given net device, this will get a non-busy channel out of the
  * corresponding bundle. The returned channel is locked.
  */
-static __inline__ isdn_net_local * isdn_net_get_locked_lp(isdn_net_dev *nd)
+static inline isdn_net_dev *
+isdn_net_get_locked_dev(isdn_net_local *mlp)
 {
 	unsigned long flags;
-	isdn_net_local *lp;
+	isdn_net_dev *idev;
+
+	spin_lock_irqsave(&mlp->online_lock, flags);
 
-	spin_lock_irqsave(&nd->queue_lock, flags);
-	lp = nd->queue;         /* get lp on top of queue */
-	spin_lock_bh(&nd->queue->xmit_lock);
-	while (isdn_net_lp_busy(nd->queue)) {
-		spin_unlock_bh(&nd->queue->xmit_lock);
-		nd->queue = nd->queue->next;
-		if (nd->queue == lp) { /* not found -- should never happen */
-			lp = NULL;
-			goto errout;
+	list_for_each_entry(idev, &mlp->online, online) {
+		spin_lock_bh(&idev->xmit_lock);
+		if (!isdn_net_dev_busy(idev)) {
+			/* point the head to next online channel */
+			list_del(&mlp->online);
+			list_add(&mlp->online, &idev->online);
+			goto found;
 		}
-		spin_lock_bh(&nd->queue->xmit_lock);
+		spin_unlock_bh(&idev->xmit_lock);
 	}
-	lp = nd->queue;
-	nd->queue = nd->queue->next;
-errout:
-	spin_unlock_irqrestore(&nd->queue_lock, flags);
-	return lp;
+	idev = NULL;
+
+ found:
+	spin_unlock_irqrestore(&mlp->online_lock, flags);
+	return idev;
 }
 
 /*
  * add a channel to a bundle
  */
-static __inline__ void isdn_net_add_to_bundle(isdn_net_dev *nd, isdn_net_local *nlp)
+static inline void
+isdn_net_add_to_bundle(isdn_net_local *mlp, isdn_net_dev *idev)
 {
-	isdn_net_local *lp;
 	unsigned long flags;
 
-	spin_lock_irqsave(&nd->queue_lock, flags);
-
-	lp = nd->queue;
-	nlp->last = lp->last;
-	lp->last->next = nlp;
-	lp->last = nlp;
-	nlp->next = lp;
-	nd->queue = nlp;
-
-	spin_unlock_irqrestore(&nd->queue_lock, flags);
+	spin_lock_irqsave(&mlp->online_lock, flags);
+	list_add(&idev->online, &mlp->online);
+	spin_unlock_irqrestore(&mlp->online_lock, flags);
 }
 /*
  * remove a channel from the bundle it belongs to
  */
-static __inline__ void isdn_net_rm_from_bundle(isdn_net_local *lp)
+static inline void
+isdn_net_rm_from_bundle(isdn_net_dev *idev)
 {
-	isdn_net_local *master_lp = lp;
+	isdn_net_local *mlp = idev->mlp;
 	unsigned long flags;
 
-	if (lp->master)
-		master_lp = (isdn_net_local *) lp->master->priv;
-
-	spin_lock_irqsave(&master_lp->netdev->queue_lock, flags);
-	lp->last->next = lp->next;
-	lp->next->last = lp->last;
-	if (master_lp->netdev->queue == lp) {
-		master_lp->netdev->queue = lp->next;
-		if (lp->next == lp) { /* last in queue */
-			master_lp->netdev->queue = &master_lp->netdev->local;
-		}
-	}
-	lp->next = lp->last = lp;	/* (re)set own pointers */
-	spin_unlock_irqrestore(&master_lp->netdev->queue_lock, flags);
+	spin_lock_irqsave(&mlp->online_lock, flags);
+	//	list_del(&idev->online); FIXME
+	spin_unlock_irqrestore(&mlp->online_lock, flags);
 }
 
 /*
  * wake up the network -> net_device queue.
  * For slaves, wake the corresponding master interface.
  */
-static inline void isdn_net_device_wake_queue(isdn_net_local *lp)
+static inline void
+isdn_net_dev_wake_queue(isdn_net_dev *idev)
 {
-	if (lp->master) 
-		netif_wake_queue(lp->master);
-	else
-		netif_wake_queue(&lp->netdev->dev);
+	netif_wake_queue(&idev->mlp->dev);
 }
 
-static inline int isdn_net_bound(isdn_net_dev *idev)
+static inline int
+isdn_net_bound(isdn_net_dev *idev)
 {
 	return idev->isdn_slot >= 0;
 }
diff -Nru a/drivers/isdn/i4l/isdn_net_lib.c b/drivers/isdn/i4l/isdn_net_lib.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/isdn/i4l/isdn_net_lib.c	Mon Oct  7 11:25:40 2002
@@ -0,0 +1,1665 @@
+/*
+ * Linux ISDN subsystem, Network interface configuration
+ *
+ * Copyright 1994-1998  by Fritz Elfert (fritz@isdn4linux.de)
+ *           1995,96    by Thinking Objects Software GmbH Wuerzburg
+ *           1995,96    by Michael Hipp (Michael.Hipp@student.uni-tuebingen.de)
+ *           1999-2002  by Kai Germaschewski <kai@germaschewski.name>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/skbuff.h>
+#include <linux/socket.h>
+#include <linux/capability.h>
+#include <linux/rtnetlink.h>
+#include "isdn_common.h"
+#include "isdn_net.h"
+#include "isdn_ppp.h"
+#include "isdn_fsm.h"
+
+#define ISDN_NET_TX_TIMEOUT (20*HZ) 
+
+/* All of this configuration code is globally serialized */
+
+static DECLARE_MUTEX(sem);
+LIST_HEAD(isdn_net_devs); /* Linked list of isdn_net_dev's */ // FIXME static
+
+int isdn_net_handle_event(isdn_net_dev *idev, int pr, void *arg); /* FIXME */
+
+static void isdn_net_tasklet(unsigned long data);
+static void isdn_net_dial_timer(unsigned long data);
+static int isdn_init_netif(struct net_device *ndev);
+static void isdn_net_dev_debug(struct fsm_inst *fi, char *fmt, ...);
+
+static struct fsm isdn_net_fsm;
+
+enum {
+	ST_NULL,
+	ST_OUT_WAIT_DCONN,
+	ST_OUT_WAIT_BCONN,
+	ST_IN_WAIT_DCONN,
+	ST_IN_WAIT_BCONN,
+	ST_ACTIVE,
+	ST_WAIT_DHUP,
+	ST_WAIT_BEFORE_CB,
+	ST_OUT_DIAL_WAIT,
+};
+
+static char *isdn_net_st_str[] = {
+	"ST_NULL",
+	"ST_OUT_WAIT_DCONN",
+	"ST_OUT_WAIT_BCONN",
+	"ST_IN_WAIT_DCONN",
+	"ST_IN_WAIT_BCONN",
+	"ST_ACTIVE",
+	"ST_WAIT_DHUP",
+	"ST_WAIT_BEFORE_CB",
+	"ST_OUT_DIAL_WAIT",
+};
+
+enum {
+	EV_TIMER_INCOMING,
+	EV_TIMER_DIAL,
+	EV_TIMER_DIAL_WAIT,
+	EV_TIMER_CB_OUT,
+	EV_TIMER_CB_IN,
+	EV_TIMER_HUP,
+	EV_STAT_DCONN,
+	EV_STAT_BCONN,
+	EV_STAT_DHUP,
+	EV_STAT_BHUP,
+	EV_STAT_CINF,
+	EV_STAT_BSENT,
+	EV_CMD_DIAL,
+};
+
+static char *isdn_net_ev_str[] = {
+	"EV_NET_TIMER_INCOMING",
+	"EV_NET_TIMER_DIAL",
+	"EV_NET_TIMER_DIAL_WAIT",
+	"EV_NET_TIMER_CB_OUT",
+	"EV_NET_TIMER_CB_IN",
+	"EV_NET_TIMER_HUP",
+	"EV_STAT_DCONN",
+	"EV_STAT_BCONN",
+	"EV_STAT_DHUP",
+	"EV_STAT_BHUP",
+	"EV_STAT_CINF",
+	"EV_STAT_BSENT",
+	"EV_CMD_DIAL",
+};
+
+/* ====================================================================== */
+/* Registration of ISDN network interface types                           */
+/* ====================================================================== */
+
+static struct isdn_netif_ops *isdn_netif_ops[ISDN_NET_ENCAP_NR];
+
+int
+register_isdn_netif(int encap, struct isdn_netif_ops *ops)
+{
+	if (encap < 0 || encap >= ISDN_NET_ENCAP_NR)
+		return -EINVAL;
+
+	if (isdn_netif_ops[encap])
+		return -EBUSY;
+
+	isdn_netif_ops[encap] = ops;
+
+	return 0;
+}
+
+/* ====================================================================== */
+/* Helpers                                                                */
+/* ====================================================================== */
+
+/* Search list of net-interfaces for an interface with given name. */
+
+static isdn_net_dev *
+isdn_net_findif(char *name)
+{
+	isdn_net_dev *idev;
+
+	list_for_each_entry(idev, &isdn_net_devs, global_list) {
+		if (!strcmp(idev->name, name))
+			return idev;
+	}
+	return NULL;
+}
+
+/* Set up a certain encapsulation */
+
+static int
+isdn_net_set_encap(isdn_net_local *lp, int encap)
+{
+	int retval = 0;
+
+	if (lp->p_encap == encap){
+		/* nothing to do */
+		retval = 0;
+		goto out;
+	}
+	if (netif_running(&lp->dev)) {
+		retval = -EBUSY;
+		goto out;
+	}
+	if (lp->ops && lp->ops->cleanup)
+		lp->ops->cleanup(lp);
+
+	if (encap < 0 || encap >= ISDN_NET_ENCAP_NR) {
+		lp->p_encap = -1;
+		lp->ops = NULL;
+		retval = -EINVAL;
+		goto out;
+	}
+
+	lp->p_encap = encap;
+	lp->ops = isdn_netif_ops[encap];
+
+	lp->dev.hard_start_xmit     = lp->ops->hard_start_xmit;
+	lp->dev.hard_header         = lp->ops->hard_header;
+	lp->dev.do_ioctl            = lp->ops->do_ioctl;
+	lp->dev.flags               = lp->ops->flags;
+	lp->dev.type                = lp->ops->type;
+	lp->dev.addr_len            = lp->ops->addr_len;
+	if (lp->ops->init)
+		retval = lp->ops->init(lp);
+
+	if (retval != 0) {
+		lp->p_encap = -1;
+		lp->ops = NULL;
+	}
+ out:
+	return retval;
+}
+
+static int
+isdn_net_bind(isdn_net_dev *idev, isdn_net_ioctl_cfg *cfg)
+{
+	isdn_net_local *mlp = idev->mlp;
+	int i, retval;
+	int drvidx = -1;
+	int chidx = -1;
+	char drvid[25];
+
+	strncpy(drvid, cfg->drvid, 24);
+	drvid[24] = 0;
+
+	if (cfg->exclusive && !strlen(drvid)) {
+		/* If we want to bind exclusively, need to specify drv/chan */
+		retval = -ENODEV;
+		goto out;
+	}
+	if (strlen(drvid)) {
+		/* A bind has been requested ... */
+		char *c = strchr(drvid, ',');
+		if (!c) {
+			retval = -ENODEV;
+			goto out;
+		}
+		/* The channel-number is appended to the driver-Id with a comma */
+		*c = 0;
+		chidx = simple_strtol(c + 1, NULL, 10);
+
+		for (i = 0; i < ISDN_MAX_DRIVERS; i++) {
+			/* Lookup driver-Id in array */
+			if (!strcmp(dev->drvid[i], drvid)) {
+				drvidx = i;
+				break;
+			}
+		}
+		if (drvidx == -1 || chidx == -1) {
+			/* Either driver-Id or channel-number invalid */
+			retval = -ENODEV;
+			goto out;
+		}
+	}
+	if (cfg->exclusive == (idev->exclusive >= 0) &&
+	    drvidx == idev->pre_device && chidx == idev->pre_channel) {
+		/* no change */
+		retval = 0;
+		goto out;
+	}
+	if (idev->exclusive >= 0) {
+		isdn_unexclusive_channel(idev->pre_device, idev->pre_channel);
+		isdn_free_channel(idev->pre_device, idev->pre_channel, ISDN_USAGE_NET);
+		idev->exclusive = -1;
+	}
+	if (cfg->exclusive) {
+		/* If binding is exclusive, try to grab the channel */
+		idev->exclusive = isdn_get_free_slot(ISDN_USAGE_NET, mlp->l2_proto, 
+						     mlp->l3_proto, drvidx, chidx, cfg->eaz);
+		if (idev->exclusive < 0) {
+			/* Grab failed, because desired channel is in use */
+			retval = -EBUSY;
+			goto out;
+		}
+		/* All went ok, so update isdninfo */
+		isdn_slot_set_usage(idev->exclusive, ISDN_USAGE_EXCLUSIVE);
+	}
+	idev->pre_device = drvidx;
+	idev->pre_channel = chidx;
+	retval = 0;
+ out:
+	return retval;
+}
+
+/*
+ * Delete all phone-numbers of an interface.
+ */
+static void
+isdn_net_rmallphone(isdn_net_dev *idev)
+{
+	isdn_net_local *mlp = idev->mlp;
+	struct isdn_net_phone *n;
+	int i;
+
+	for (i = 0; i < 2; i++) {
+		while (!list_empty(&mlp->phone[i])) {
+			n = list_entry(mlp->phone[i].next, struct isdn_net_phone, list);
+			list_del(&n->list);
+			kfree(n);
+		}
+	}
+}
+
+/* ====================================================================== */
+/* /dev/isdnctrl net ioctl interface                                      */
+/* ====================================================================== */
+
+/*
+ * Allocate a new network-interface and initialize its data structures
+ */
+static int
+isdn_net_addif(char *name, isdn_net_local *mlp)
+{
+	int retval;
+	struct net_device *dev = NULL;
+	isdn_net_dev *idev;
+
+	/* Avoid creating an existing interface */
+	if (isdn_net_findif(name))
+		return -EEXIST;
+
+	idev = kmalloc(sizeof(*idev), GFP_KERNEL);
+	if (!idev)
+		return -ENOMEM;
+
+	memset(idev, 0, sizeof(*idev));
+	strcpy(idev->name, name);
+
+	tasklet_init(&idev->tlet, isdn_net_tasklet, (unsigned long) idev);
+	spin_lock_init(&idev->xmit_lock);
+	skb_queue_head_init(&idev->super_tx_queue);
+
+	idev->isdn_slot = -1;
+	idev->pre_device = -1;
+	idev->pre_channel = -1;
+	idev->exclusive = -1;
+
+	idev->ppp_slot = -1;
+	idev->pppbind = -1;
+
+	init_timer(&idev->dial_timer);
+	idev->dial_timer.data = (unsigned long) idev;
+	idev->dial_timer.function = isdn_net_dial_timer;
+
+	idev->fi.fsm = &isdn_net_fsm;
+	idev->fi.state = ST_NULL;
+	idev->fi.debug = 1;
+	idev->fi.userdata = idev;
+	idev->fi.printdebug = isdn_net_dev_debug;
+
+	if (!mlp) {
+		/* Device shall be a master */
+		mlp = kmalloc(sizeof(*mlp), GFP_KERNEL);
+		if (!mlp)
+			return -ENOMEM;
+		
+		memset(mlp, 0, sizeof(*mlp));
+
+		mlp->magic = ISDN_NET_MAGIC;
+		INIT_LIST_HEAD(&mlp->slaves);
+		INIT_LIST_HEAD(&mlp->online);
+
+		mlp->p_encap = -1;
+		isdn_net_set_encap(mlp, ISDN_NET_ENCAP_RAWIP);
+
+		mlp->l2_proto = ISDN_PROTO_L2_X75I;
+		mlp->l3_proto = ISDN_PROTO_L3_TRANS;
+		mlp->triggercps = 6000;
+		mlp->slavedelay = 10 * HZ;
+		mlp->hupflags = ISDN_INHUP;
+		mlp->onhtime = 10;
+		mlp->dialmax = 1;
+		mlp->flags = ISDN_NET_CBHUP | ISDN_NET_DM_MANUAL;
+		mlp->cbdelay = 5 * HZ;	   /* Wait 5 secs before call-back  */
+		mlp->dialtimeout = 60 * HZ;/* Wait 1 min for connection     */
+		mlp->dialwait = 5 * HZ;    /* Wait 5 sec. after failed dial */
+		INIT_LIST_HEAD(&mlp->phone[0]);
+		INIT_LIST_HEAD(&mlp->phone[1]);
+		dev = &mlp->dev;
+	}
+	idev->mlp = mlp;
+	list_add_tail(&idev->slaves, &mlp->slaves);
+
+	if (dev) {
+		strcpy(dev->name, name);
+		dev->priv = mlp;
+		dev->init = isdn_init_netif;
+		SET_MODULE_OWNER(dev);
+		retval = register_netdev(dev);
+		if (retval) {
+			kfree(mlp);
+			kfree(idev);
+			return retval;
+		}
+	}
+	list_add(&idev->global_list, &isdn_net_devs);
+	return 0;
+}
+
+/*
+ * Add a new slave interface to an existing one
+ */
+static int
+isdn_net_addslave(char *parm)
+{
+	char *p = strchr(parm, ',');
+	isdn_net_dev *idev;
+	isdn_net_local *mlp;
+	int retval;
+
+	/* get slave name */
+	if (!p || !p[1])
+		return -EINVAL;
+
+	*p++ = 0;
+
+	/* find master */
+	idev = isdn_net_findif(parm);
+	if (!idev)
+		return -ESRCH;
+
+	mlp = idev->mlp;
+
+	rtnl_lock();
+
+	if (netif_running(&mlp->dev))
+		return -EBUSY;
+
+	retval = isdn_net_addif(p, mlp);
+	
+	rtnl_unlock();
+	return retval;
+}
+
+/*
+ * Delete a single network-interface
+ */
+static int
+isdn_net_dev_delete(isdn_net_dev *idev)
+{
+	isdn_net_local *mlp = idev->mlp;
+	int retval;
+
+	rtnl_lock();
+	
+	if (netif_running(&mlp->dev)) {
+		retval = -EBUSY;
+		goto unlock;
+	}
+	isdn_net_set_encap(mlp, -1);
+	isdn_net_rmallphone(idev);
+
+	if (idev->exclusive >= 0)
+		isdn_unexclusive_channel(idev->pre_device, idev->pre_channel);
+
+	list_del(&idev->slaves);
+	
+	rtnl_unlock();
+
+	if (list_empty(&mlp->slaves)) {
+		unregister_netdev(&mlp->dev);
+		kfree(mlp);
+	}
+
+	list_del(&idev->global_list);
+	kfree(idev);
+	return 0;
+
+ unlock:
+	rtnl_unlock();
+	return retval;
+}
+
+/*
+ * Delete a single network-interface
+ */
+static int
+isdn_net_delif(char *name)
+{
+	/* FIXME: For compatibility, if a master isdn_net_dev is rm'ed,
+	 * kill all slaves, too */
+
+	isdn_net_dev *idev = isdn_net_findif(name);
+
+	if (!idev)
+		return -ENODEV;
+
+	return isdn_net_dev_delete(idev);
+}
+
+/*
+ * Set interface-parameters.
+ * Always set all parameters, so the user-level application is responsible
+ * for not overwriting existing setups. It has to get the current
+ * setup first, if only selected parameters are to be changed.
+ */
+static int
+isdn_net_setcfg(isdn_net_ioctl_cfg *cfg)
+{
+	isdn_net_dev *idev = isdn_net_findif(cfg->name);
+	isdn_net_local *mlp;
+	ulong features;
+	int i, retval;
+
+	if (!idev)
+		return -ENODEV;
+
+	mlp = idev->mlp;
+
+	rtnl_lock();
+
+	if (netif_running(&mlp->dev)) {
+		retval = -EBUSY;
+		goto out;
+	}
+	/* See if any registered driver supports the features we want */
+	features = ((1 << cfg->l2_proto) << ISDN_FEATURE_L2_SHIFT) |
+		   ((1 << cfg->l3_proto) << ISDN_FEATURE_L3_SHIFT);
+	for (i = 0; i < ISDN_MAX_DRIVERS; i++)
+		if (dev->drv[i] &&
+		    (dev->drv[i]->interface->features & features) == features)
+				break;
+
+	if (i == ISDN_MAX_DRIVERS) {
+		printk(KERN_WARNING "isdn_net: No driver with selected features\n");
+		retval = -ENODEV;
+		goto out;
+	}
+
+	retval = isdn_net_set_encap(mlp, cfg->p_encap);
+	if (retval)
+		goto out;
+
+	retval = isdn_net_bind(idev, cfg);
+	if (retval)
+		goto out;
+
+	strncpy(mlp->msn, cfg->eaz, ISDN_MSNLEN-1);
+	mlp->msn[ISDN_MSNLEN-1] = 0;
+	mlp->onhtime = cfg->onhtime;
+	idev->charge = cfg->charge;
+	mlp->l2_proto = cfg->l2_proto;
+	mlp->l3_proto = cfg->l3_proto;
+	mlp->cbdelay = cfg->cbdelay * HZ / 5;
+	mlp->dialmax = cfg->dialmax;
+	mlp->triggercps = cfg->triggercps;
+	mlp->slavedelay = cfg->slavedelay * HZ;
+	idev->pppbind = cfg->pppbind;
+	mlp->dialtimeout = cfg->dialtimeout >= 0 ? cfg->dialtimeout * HZ : -1;
+	mlp->dialwait = cfg->dialwait * HZ;
+	if (cfg->secure)
+		mlp->flags |= ISDN_NET_SECURE;
+	else
+		mlp->flags &= ~ISDN_NET_SECURE;
+	if (cfg->cbhup)
+		mlp->flags |= ISDN_NET_CBHUP;
+	else
+		mlp->flags &= ~ISDN_NET_CBHUP;
+	switch (cfg->callback) {
+	case 0:
+		mlp->flags &= ~(ISDN_NET_CALLBACK | ISDN_NET_CBOUT);
+		break;
+	case 1:
+		mlp->flags |= ISDN_NET_CALLBACK;
+		mlp->flags &= ~ISDN_NET_CBOUT;
+		break;
+	case 2:
+		mlp->flags |= ISDN_NET_CBOUT;
+		mlp->flags &= ~ISDN_NET_CALLBACK;
+		break;
+	}
+	mlp->flags &= ~ISDN_NET_DIALMODE_MASK;	/* first all bits off */
+	if (cfg->dialmode && !(cfg->dialmode & ISDN_NET_DIALMODE_MASK)) {
+		retval = -EINVAL;
+		goto out;
+	}
+
+	mlp->flags |= cfg->dialmode;  /* turn on selected bits */
+	if (mlp->flags & ISDN_NET_DM_OFF)
+		isdn_net_hangup(idev);
+
+	if (cfg->chargehup)
+		mlp->hupflags |= ISDN_CHARGEHUP;
+	else
+		mlp->hupflags &= ~ISDN_CHARGEHUP;
+
+	if (cfg->ihup)
+		mlp->hupflags |= ISDN_INHUP;
+	else
+		mlp->hupflags &= ~ISDN_INHUP;
+
+	if (cfg->chargeint > 10) {
+		idev->chargeint = cfg->chargeint * HZ;
+		idev->charge_state = ST_CHARGE_HAVE_CINT;
+		mlp->hupflags |= ISDN_MANCHARGE;
+	}
+	retval = 0;
+
+ out:
+	rtnl_unlock();
+	
+	return retval;
+}
+
+/*
+ * Perform get-interface-parameters.ioctl
+ */
+static int
+isdn_net_getcfg(isdn_net_ioctl_cfg *cfg)
+{
+	isdn_net_dev *idev = isdn_net_findif(cfg->name);
+	isdn_net_local *mlp;
+		
+	if (!idev)
+		return -ENODEV;
+
+	mlp = idev->mlp;
+
+	strcpy(cfg->eaz, mlp->msn);
+	cfg->exclusive = idev->exclusive >= 0;
+	if (idev->pre_device >= 0) {
+		sprintf(cfg->drvid, "%s,%d", dev->drvid[idev->pre_device],
+			idev->pre_channel);
+	} else {
+		cfg->drvid[0] = '\0';
+	}
+	cfg->onhtime = mlp->onhtime;
+	cfg->charge = idev->charge;
+	cfg->l2_proto = mlp->l2_proto;
+	cfg->l3_proto = mlp->l3_proto;
+	cfg->p_encap = mlp->p_encap;
+	cfg->secure = (mlp->flags & ISDN_NET_SECURE) ? 1 : 0;
+	cfg->callback = 0;
+	if (mlp->flags & ISDN_NET_CALLBACK)
+		cfg->callback = 1;
+	if (mlp->flags & ISDN_NET_CBOUT)
+		cfg->callback = 2;
+	cfg->cbhup = (mlp->flags & ISDN_NET_CBHUP) ? 1 : 0;
+	cfg->dialmode = mlp->flags & ISDN_NET_DIALMODE_MASK;
+	cfg->chargehup = (mlp->hupflags & ISDN_CHARGEHUP) ? 1 : 0;
+	cfg->ihup = (mlp->hupflags & ISDN_INHUP) ? 1 : 0;
+	cfg->cbdelay = mlp->cbdelay * 5 / HZ;
+	cfg->dialmax = mlp->dialmax;
+	cfg->triggercps = mlp->triggercps;
+	cfg->slavedelay = mlp->slavedelay / HZ;
+	cfg->chargeint = (mlp->hupflags & ISDN_CHARGEHUP) ?
+		(idev->chargeint / HZ) : 0;
+	cfg->pppbind = idev->pppbind;
+	cfg->dialtimeout = mlp->dialtimeout >= 0 ? mlp->dialtimeout / HZ : -1;
+	cfg->dialwait = mlp->dialwait / HZ;
+
+	if (idev->slaves.next != &mlp->slaves)
+		strcpy(cfg->slave, list_entry(idev->slaves.next, isdn_net_dev, slaves)->name);
+	else
+		cfg->slave[0] = '\0';
+	if (strcmp(mlp->dev.name, idev->name))
+		strcpy(cfg->master, mlp->dev.name);
+	else
+		cfg->master[0] = '\0';
+
+	return 0;
+}
+
+/*
+ * Add a phone-number to an interface.
+ */
+static int
+isdn_net_addphone(isdn_net_ioctl_phone *phone)
+{
+	isdn_net_dev *idev = isdn_net_findif(phone->name);
+	struct isdn_net_phone *n;
+	int retval = 0;
+
+	if (!idev)
+		return -ENODEV;
+
+	rtnl_lock();
+
+	if (netif_running(&idev->mlp->dev)) {
+		retval = -EBUSY;
+		goto out;
+	}
+	n = kmalloc(sizeof(*n), GFP_KERNEL);
+	if (!n) {
+		retval = -ENOMEM;
+		goto out;
+	}
+	strcpy(n->num, phone->phone);
+	list_add_tail(&n->list, &idev->mlp->phone[phone->outgoing & 1]);
+
+ out:
+	rtnl_unlock();
+	return retval;
+}
+
+/*
+ * Delete a phone-number from an interface.
+ */
+static int
+isdn_net_delphone(isdn_net_ioctl_phone *phone)
+{
+	isdn_net_dev *idev = isdn_net_findif(phone->name);
+	struct isdn_net_phone *n;
+	int retval;
+
+	if (!idev)
+		return -ENODEV;
+
+	rtnl_lock();
+
+	if (netif_running(&idev->mlp->dev)) {
+		retval = -EBUSY;
+		goto out;
+	}
+	retval = -EINVAL;
+	list_for_each_entry(n, &idev->mlp->phone[phone->outgoing & 1], list) {
+		if (!strcmp(n->num, phone->phone)) {
+			list_del(&n->list);
+			kfree(n);
+			retval = 0;
+			break;
+		}
+	}
+ out:
+	rtnl_unlock();
+	return retval;
+}
+
+/*
+ * Copy a string of all phone-numbers of an interface to user space.
+ */
+static int
+isdn_net_getphone(isdn_net_ioctl_phone * phone, char *phones)
+{
+	isdn_net_dev *idev = isdn_net_findif(phone->name);
+	int count = 0;
+	char *buf = (char *)__get_free_page(GFP_KERNEL);
+	struct isdn_net_phone *n;
+
+	if (!buf)
+		return -ENOMEM;
+
+	if (!idev) {
+		count = -ENODEV;
+		goto free;
+	}
+	list_for_each_entry(n, &idev->mlp->phone[phone->outgoing & 1], list) {
+		strcpy(&buf[count], n->num);
+		count += strlen(n->num);
+		buf[count++] = ' ';
+		if (count > PAGE_SIZE - ISDN_MSNLEN - 1)
+			break;
+	}
+	if (!count) /* list was empty? */
+		count++;
+
+	buf[count-1] = 0;
+
+	if (copy_to_user(phones, buf, count))
+		count = -EFAULT;
+
+ free:
+	free_page((unsigned long)buf);
+	return count;
+}
+
+/*
+ * Force a net-interface to dial out.
+ */
+static int
+isdn_net_dial_out(char *name)
+{
+	isdn_net_dev *idev = isdn_net_findif(name);
+
+	if (!idev)
+		return -ENODEV;
+
+	return isdn_net_dial(idev);
+}
+
+/*
+ * Force a hangup of a network-interface.
+ */
+static int
+isdn_net_force_hangup(char *name) // FIXME rename?
+{
+	isdn_net_dev *idev = isdn_net_findif(name);
+
+	if (!idev)
+		return -ENODEV;
+
+	if (idev->isdn_slot < 0)
+		return -ENOTCONN;
+
+	isdn_net_hangup(idev);
+	return 0;
+}
+
+/*
+ * Copy a string containing the peer's phone number of a connected interface
+ * to user space.
+ */
+static int
+isdn_net_getpeer(isdn_net_ioctl_phone *phone, isdn_net_ioctl_phone *peer)
+{
+	isdn_net_dev *idev = isdn_net_findif(phone->name);
+	int idx;
+
+	if (!idev)
+		return -ENODEV;
+	/* FIXME
+	 * Theoretical race: while this executes, the remote number might
+	 * become invalid (hang up) or change (new connection), resulting
+         * in (partially) wrong number copied to user. This race
+	 * currently ignored.
+	 */
+	idx = idev->isdn_slot;
+	if (idx < 0)
+		return -ENOTCONN;
+	/* for pre-bound channels, we need this extra check */
+	if (strncmp(isdn_slot_num(idx), "???", 3) == 0 )
+		return -ENOTCONN;
+
+	strncpy(phone->phone, isdn_slot_num(idx), ISDN_MSNLEN);
+	phone->outgoing = USG_OUTGOING(isdn_slot_usage(idx));
+
+	if (copy_to_user(peer, phone, sizeof(*peer)))
+		return -EFAULT;
+
+	return 0;
+}
+
+/*
+ * ioctl on /dev/isdnctrl, used to configure ISDN net interfaces 
+ */
+int
+isdn_net_ioctl(struct inode *ino, struct file *file, uint cmd, ulong arg)
+{
+	/* Save stack space */
+	union {
+		char name[10];
+		char bname[20];
+		isdn_net_ioctl_phone phone;
+		isdn_net_ioctl_cfg cfg;
+	} iocpar;
+	int retval;
+
+#define name  iocpar.name
+#define bname iocpar.bname
+#define phone iocpar.phone
+#define cfg   iocpar.cfg
+
+	name[sizeof(name)-1] = 0;
+	bname[sizeof(bname)-1] = 0;
+
+	down(&sem);
+	
+	switch (cmd) {
+	case IIOCNETAIF: /* add an interface */
+		if (copy_from_user(name, (char *) arg, sizeof(name) - 1)) {
+			retval = -EFAULT;
+			break;
+		}
+		retval = isdn_net_addif(name, NULL);
+		break;
+	case IIOCNETASL: /* add slave to an interface */
+		if (copy_from_user(bname, (char *) arg, sizeof(bname) - 1)) {
+			retval = -EFAULT;
+			break;
+		}
+		retval = isdn_net_addslave(bname);
+		break;
+	case IIOCNETDIF: /* delete an interface */
+		if (copy_from_user(name, (char *) arg, sizeof(name) - 1)) {
+			retval = -EFAULT;
+			break;
+		}
+		retval = isdn_net_delif(name);
+		break;
+	case IIOCNETSCF: /* set config */
+		if (copy_from_user((char *) &cfg, (char *) arg, sizeof(cfg))) {
+			retval = -EFAULT;
+			break;
+		}
+		retval = isdn_net_setcfg(&cfg);
+		break;
+	case IIOCNETGCF: /* get config */
+		if (copy_from_user((char *) &cfg, (char *) arg, sizeof(cfg))) {
+			retval = -EFAULT;
+			break;
+		}
+		retval = isdn_net_getcfg(&cfg);
+		if (retval)
+			break;
+		if (copy_to_user((char *) arg, (char *) &cfg, sizeof(cfg)))
+			retval = -EFAULT;
+		break;
+	case IIOCNETANM: /* add a phone number */
+		if (copy_from_user((char *) &phone, (char *) arg, sizeof(phone))) {
+			retval = -EFAULT;
+			break;
+		}
+		retval = isdn_net_addphone(&phone);
+		break;
+	case IIOCNETGNM: /* get list of phone numbers */
+		if (copy_from_user((char *) &phone, (char *) arg, sizeof(phone))) {
+			retval = -EFAULT;
+			break;
+		}
+		retval = isdn_net_getphone(&phone, (char *) arg);
+		break;
+	case IIOCNETDNM: /* delete a phone number */
+		if (copy_from_user((char *) &phone, (char *) arg, sizeof(phone))) {
+			retval = -EFAULT;
+			break;
+		}
+		retval = isdn_net_delphone(&phone);
+		break;
+	case IIOCNETDIL: /* trigger dial-out */
+		if (copy_from_user(name, (char *) arg, sizeof(name))) {
+			retval = -EFAULT;
+			break;
+		}
+		retval = isdn_net_dial_out(name);
+		break;
+	case IIOCNETHUP: /* hangup */
+		if (copy_from_user(name, (char *) arg, sizeof(name))) {
+			retval = -EFAULT;
+			break;
+		}
+		retval = isdn_net_force_hangup(name);
+		break;
+	case IIOCNETGPN: /* Get peer phone number of a connected interface */
+		if (copy_from_user((char *) &phone, (char *) arg, sizeof(phone))) {
+			retval = -EFAULT;
+		}
+		retval = isdn_net_getpeer(&phone, (isdn_net_ioctl_phone *) arg);
+		break;
+#ifdef CONFIG_ISDN_PPP
+	case IIOCNETALN: /* Add link */
+		if (copy_from_user(name, (char *) arg, sizeof(name))) {
+			retval = -EFAULT;
+			break;
+		}
+		retval = isdn_ppp_dial_slave(name);
+		break;
+	case IIOCNETDLN: /* Delete link */
+		if (copy_from_user(name, (char *) arg, sizeof(name))) {
+			retval = -EFAULT;
+			break;
+		}
+		retval = isdn_ppp_hangup_slave(name);
+		break;
+#endif
+	default:
+		retval = -ENOTTY;
+	}
+	up(&sem);
+	return retval;
+
+#undef name
+#undef bname
+#undef iocts
+#undef phone
+#undef cfg
+}
+
+/*
+ * Hang up all network-interfaces
+ */
+void
+isdn_net_hangup_all(void)
+{
+	isdn_net_dev *idev;
+
+	down(&sem);
+
+	list_for_each_entry(idev, &isdn_net_devs, global_list)
+		isdn_net_hangup(idev);
+
+	up(&sem);
+}
+
+/*
+ * Remove all network-interfaces
+ */
+void
+isdn_net_exit(void)
+{
+	isdn_net_dev *idev;
+	int retval;
+
+	down(&sem);
+
+	while (!list_empty(&isdn_net_devs)) {
+		idev = list_entry(isdn_net_devs.next, isdn_net_dev, global_list);
+		retval = isdn_net_dev_delete(idev);
+		/* can only fail if an interface is still running.
+		 * In this case, an elevated module use count should
+		 * have prevented this function from being called in
+		 * the first place */
+		if (retval)
+			isdn_BUG();
+	}
+	up(&sem);
+
+	// FIXME
+	isdn_net_lib_exit();
+}
+
+/* ====================================================================== */
+/* interface to network layer                                             */
+/* ====================================================================== */
+
+/* 
+ * Open/initialize the board.
+ */
+static int
+isdn_net_open(struct net_device *dev)
+{
+	isdn_net_local *lp = dev->priv;
+	int retval = 0;
+
+	if (!lp->ops)
+		return -ENODEV;
+
+	if (lp->ops->open)
+		retval = lp->ops->open(lp);
+
+	if (!retval)
+		return retval;
+	
+	netif_start_queue(dev);
+	return 0;
+}
+
+/*
+ * Shutdown a net-interface.
+ */
+// FIXME share?
+static int
+isdn_net_close(struct net_device *dev)
+{
+	isdn_net_local *lp = dev->priv;
+	struct list_head *l, *n;
+	isdn_net_dev *sdev;
+
+	if (lp->ops->close)
+		lp->ops->close(lp);
+
+	netif_stop_queue(dev);
+
+	list_for_each_safe(l, n, &lp->online) {
+		sdev = list_entry(l, isdn_net_dev, online);
+		isdn_net_hangup(sdev);
+	}
+	return 0;
+}
+
+/*
+ * Get statistics
+ */
+static struct net_device_stats *
+isdn_net_get_stats(struct net_device *dev)
+{
+	isdn_net_local *lp = dev->priv;
+
+	return &lp->stats;
+}
+
+/*
+ * Transmit timeout
+ */
+static void
+isdn_net_tx_timeout(struct net_device *dev)
+{
+	printk(KERN_WARNING "isdn_tx_timeout dev %s\n", dev->name);
+
+	netif_wake_queue(dev);
+}
+
+/*
+ * Interface-setup. (just after registering a new interface)
+ */
+static int
+isdn_init_netif(struct net_device *ndev)
+{
+	/* Setup the generic properties */
+
+	ndev->mtu = 1500;
+	ndev->tx_queue_len = 10;
+	ndev->open = &isdn_net_open;
+	ndev->hard_header_len = ETH_HLEN + isdn_hard_header_len();
+	ndev->stop = &isdn_net_close;
+	ndev->get_stats = &isdn_net_get_stats;
+	ndev->tx_timeout = isdn_net_tx_timeout;
+	ndev->watchdog_timeo = ISDN_NET_TX_TIMEOUT;
+
+	return 0;
+}
+
+/* ====================================================================== */
+
+static void
+isdn_net_tasklet(unsigned long data)
+{
+	isdn_net_dev *idev = (isdn_net_dev *) data;
+	struct sk_buff *skb;
+
+	spin_lock_bh(&idev->xmit_lock);
+	while (!isdn_net_dev_busy(idev)) {
+		skb = skb_dequeue(&idev->super_tx_queue);
+		if (!skb)
+			break;
+		isdn_net_writebuf_skb(idev, skb);
+	}
+	spin_unlock_bh(&idev->xmit_lock);
+}
+
+/* ====================================================================== */
+/* call control state machine                                             */
+/* ====================================================================== */
+
+// FIXME
+int isdn_net_online(isdn_net_dev *idev)
+{
+	return idev->fi.state == ST_ACTIVE;
+}
+
+static void
+isdn_net_dial_timer(unsigned long data)
+{
+	isdn_net_dev *idev = (isdn_net_dev *) data;
+
+	isdn_net_handle_event(idev, idev->dial_event, NULL);
+}
+
+/*
+ * Assign an ISDN-channel to a net-interface
+ */
+int
+isdn_net_bind_channel(isdn_net_dev *idev, int slot)
+{
+	isdn_net_local *mlp = idev->mlp;
+	int retval = 0;
+	unsigned long flags;
+
+	save_flags(flags);
+	cli();
+
+	idev->isdn_slot = slot;
+	isdn_slot_set_idev(idev->isdn_slot, idev);
+
+	if (mlp->ops->bind)
+		retval = mlp->ops->bind(idev);
+
+	if (retval < 0)
+		isdn_net_unbind_channel(idev);
+
+	restore_flags(flags);
+	return retval;
+}
+
+/*
+ * Unbind a net-interface
+ */
+void
+isdn_net_unbind_channel(isdn_net_dev *idev)
+{
+	isdn_net_local *mlp = idev->mlp;
+	ulong flags;
+
+	save_flags(flags);
+	cli();
+
+	if (idev->isdn_slot < 0) {
+		isdn_BUG();
+		return;
+	}
+
+	if (mlp->ops->unbind)
+		mlp->ops->unbind(idev);
+
+	skb_queue_purge(&idev->super_tx_queue);
+
+	fsm_change_state(&idev->fi, ST_NULL);
+
+	isdn_slot_set_idev(idev->isdn_slot, NULL);
+	isdn_slot_free(idev->isdn_slot, ISDN_USAGE_NET);
+
+	idev->isdn_slot = -1;
+
+	restore_flags(flags);
+}
+
+int
+isdn_net_dial(isdn_net_dev *idev)
+{
+	int slot;
+	isdn_net_local *mlp = idev->mlp;
+
+	if (isdn_net_bound(idev))
+		return -EBUSY;
+
+	if (idev->exclusive >= 0)
+		slot = idev->exclusive;
+	else
+		slot = isdn_get_free_slot(ISDN_USAGE_NET, mlp->l2_proto,
+					  mlp->l3_proto, idev->pre_device, 
+					  idev->pre_channel, mlp->msn);
+	if (slot < 0)
+		goto err;
+
+	isdn_slot_set_usage(slot, isdn_slot_usage(slot) | ISDN_USAGE_OUTGOING);
+
+	if (isdn_net_bind_channel(idev, slot) < 0)
+		goto err;
+
+	/* Initiate dialing */
+	fsm_event(&idev->fi, EV_CMD_DIAL, NULL);
+	return 0;
+
+ err:
+	return -EAGAIN;
+}
+
+void
+isdn_net_accept(isdn_net_dev *idev, int slot, char *nr)
+{
+	isdn_net_local *mlp = idev->mlp;
+	isdn_ctrl cmd;
+
+	strcpy(isdn_slot_num(slot), nr);
+	isdn_slot_set_usage(slot, (isdn_slot_usage(slot) & ISDN_USAGE_EXCLUSIVE) | ISDN_USAGE_NET);
+	
+	isdn_net_bind_channel(idev, slot);
+	
+	idev->outgoing = 0;
+	idev->charge_state = ST_CHARGE_NULL;
+	/* Got incoming call, setup L2 and L3 protocols,
+	 * then wait for D-Channel-connect
+	 */
+	cmd.arg = mlp->l2_proto << 8;
+	isdn_slot_command(idev->isdn_slot, ISDN_CMD_SETL2, &cmd);
+	cmd.arg = mlp->l3_proto << 8;
+	isdn_slot_command(idev->isdn_slot, ISDN_CMD_SETL3, &cmd);
+	
+	idev->dial_timer.expires = jiffies + mlp->dialtimeout;
+	idev->dial_event = EV_TIMER_INCOMING;
+	add_timer(&idev->dial_timer);
+	fsm_change_state(&idev->fi, ST_IN_WAIT_DCONN);
+}
+
+int
+isdn_net_do_callback(isdn_net_dev *idev)
+{
+	isdn_net_local *mlp = idev->mlp;
+	int slot;
+	/*
+	 * Is the state MANUAL?
+	 * If so, no callback can be made,
+	 * so reject actively.
+	 */
+	if (ISDN_NET_DIALMODE(*mlp) == ISDN_NET_DM_OFF) {
+		printk(KERN_INFO "incoming call for callback, interface %s `off' -> rejected\n",
+		       idev->name);
+		return 3;
+	}
+	printk(KERN_DEBUG "%s: start callback\n", idev->name);
+	
+	/* Grab a free ISDN-Channel */
+	slot = isdn_get_free_slot(ISDN_USAGE_NET, mlp->l2_proto, mlp->l3_proto,
+				  idev->pre_device, idev->pre_channel, mlp->msn);
+	if (slot < 0)
+		goto err;
+
+	isdn_slot_set_usage(slot, isdn_slot_usage(slot) | ISDN_USAGE_OUTGOING);
+
+	if (isdn_net_bind_channel(idev, slot) < 0)
+		goto err;
+
+	/* Setup dialstate. */
+	idev->dial_timer.expires = jiffies + mlp->cbdelay;
+	idev->dial_event = EV_TIMER_CB_IN;
+	add_timer(&idev->dial_timer);
+	fsm_change_state(&idev->fi, ST_WAIT_BEFORE_CB);
+
+	/* Initiate dialing by returning 2 or 4 */
+	return (mlp->flags & ISDN_NET_CBHUP) ? 2 : 4;
+
+ err:
+	return 0;
+}
+
+/* ---------------------------------------------------------------------- */
+/* callbacks in the state machine                                         */
+/* ---------------------------------------------------------------------- */
+
+/* Find the idev->dial'th outgoing number. */
+
+static struct isdn_net_phone *
+get_outgoing_phone(isdn_net_dev *idev)
+{
+	isdn_net_local *mlp = idev->mlp;
+	struct isdn_net_phone *phone;
+	int i = 0;
+
+	list_for_each_entry(phone, &mlp->phone[1], list) {
+		if (i++ == idev->dial)
+			return phone;
+	}
+	return NULL;
+}
+
+static int dialout_next(struct fsm_inst *fi, int pr, void *arg);
+
+/* Initiate dialout. */
+
+static int
+dialout_first(struct fsm_inst *fi, int pr, void *arg)
+{
+	isdn_net_dev *idev = fi->userdata;
+	isdn_net_local *mlp = idev->mlp;
+
+	if (ISDN_NET_DIALMODE(*mlp) == ISDN_NET_DM_OFF) {
+		isdn_net_unbind_channel(idev);
+		return -EPERM;
+	}
+	if (list_empty(&mlp->phone[1])) {
+		isdn_net_unbind_channel(idev);
+		return -EINVAL;
+	}
+
+	idev->dial = 0;
+	idev->dialretry = 0;
+	return dialout_next(fi, pr, arg);
+}
+
+/* Try dialing the next number. */
+
+static int
+dialout_next(struct fsm_inst *fi, int pr, void *arg)
+{
+	isdn_net_dev *idev = fi->userdata;
+	isdn_net_local *mlp = idev->mlp;
+	struct dial_info dial = {
+		.l2_proto = mlp->l2_proto,
+		.l3_proto = mlp->l3_proto,
+		.si1      = 7,
+		.si2      = 0,
+		.msn      = mlp->msn,
+		.phone    = get_outgoing_phone(idev)->num,
+	};
+
+	/* next time, try next number */
+	idev->dial++;
+
+	idev->outgoing = 1;
+	if (idev->chargeint)
+		idev->charge_state = ST_CHARGE_HAVE_CINT;
+	else
+		idev->charge_state = ST_CHARGE_NULL;
+
+	/* For outgoing callback, use cbdelay instead of dialtimeout */
+	if (mlp->cbdelay && (mlp->flags & ISDN_NET_CBOUT)) {
+		idev->dial_timer.expires = jiffies + mlp->cbdelay;
+		idev->dial_event = EV_TIMER_CB_OUT;
+	} else {
+		idev->dial_timer.expires = jiffies + mlp->dialtimeout;
+		idev->dial_event = EV_TIMER_DIAL;
+	}
+	fsm_change_state(&idev->fi, ST_OUT_WAIT_DCONN);
+	add_timer(&idev->dial_timer);
+
+	/* Dial */
+	isdn_slot_dial(idev->isdn_slot, &dial);
+	return 0;
+}
+
+/* If we didn't connect within dialtimeout, we give up for now
+ * and wait for dialwait jiffies before trying again.
+ */
+static int
+dial_timeout(struct fsm_inst *fi, int pr, void *arg)
+{
+	isdn_net_dev *idev = fi->userdata;
+	isdn_net_local *mlp = idev->mlp;
+	isdn_ctrl cmd;
+
+	fsm_change_state(&idev->fi, ST_OUT_DIAL_WAIT);
+	isdn_slot_command(idev->isdn_slot, ISDN_CMD_HANGUP, &cmd);
+	
+	/* get next phone number */
+	if (!get_outgoing_phone(idev)) {
+		/* otherwise start over at first entry */
+		idev->dial = 0;
+		idev->dialretry++;
+	}
+	if (idev->dialretry >= mlp->dialmax) {
+		isdn_net_hangup(idev);
+		return 0;
+	}
+	idev->dial_event = EV_TIMER_DIAL_WAIT;
+	mod_timer(&idev->dial_timer, jiffies + mlp->dialwait);
+	return 0;
+}
+
+static int
+connect_fail(struct fsm_inst *fi, int pr, void *arg)
+{
+	isdn_net_dev *idev = fi->userdata;
+
+	del_timer(&idev->dial_timer);
+	isdn_slot_all_eaz(idev->isdn_slot);
+	printk(KERN_INFO "%s: connection failed\n", idev->name);
+	isdn_net_unbind_channel(idev);
+	return 0;
+}
+
+static int
+out_dconn(struct fsm_inst *fi, int pr, void *arg)
+{
+	isdn_net_dev *idev = fi->userdata;
+	isdn_ctrl cmd;
+
+	fsm_change_state(&idev->fi, ST_OUT_WAIT_BCONN);
+	isdn_slot_command(idev->isdn_slot, ISDN_CMD_ACCEPTB, &cmd);
+	return 0;
+}
+
+static int
+in_dconn(struct fsm_inst *fi, int pr, void *arg)
+{
+	isdn_net_dev *idev = fi->userdata;
+	isdn_ctrl cmd;
+
+	fsm_change_state(&idev->fi, ST_IN_WAIT_BCONN);
+	isdn_slot_command(idev->isdn_slot, ISDN_CMD_ACCEPTB, &cmd);
+	return 0;
+}
+
+static int
+bconn(struct fsm_inst *fi, int pr, void *arg)
+{
+	isdn_net_dev *idev = fi->userdata;
+	isdn_net_local *mlp = idev->mlp;
+
+	fsm_change_state(&idev->fi, ST_ACTIVE);
+
+	if (mlp->onhtime) {
+		idev->huptimer = 0;
+		idev->dial_event = EV_TIMER_HUP;
+		mod_timer(&idev->dial_timer, jiffies + HZ);
+	} else {
+		del_timer(&idev->dial_timer);
+	}
+
+	isdn_net_add_to_bundle(mlp, idev);
+
+	printk(KERN_INFO "%s connected\n", idev->name);
+	/* If first Chargeinfo comes before B-Channel connect,
+	 * we correct the timestamp here.
+	 */
+	idev->chargetime = jiffies;
+	
+	idev->transcount = 0;
+	idev->cps = 0;
+	idev->last_jiffies = jiffies;
+
+	if (mlp->ops->connected)
+		mlp->ops->connected(idev);
+	else
+		isdn_net_dev_wake_queue(idev);
+
+	return 0;
+}
+
+static int
+bhup(struct fsm_inst *fi, int pr, void *arg)
+{
+	isdn_net_dev *idev = fi->userdata;
+	isdn_net_local *mlp = idev->mlp;
+
+	del_timer(&idev->dial_timer);
+	if (mlp->ops->disconnected)
+		mlp->ops->disconnected(idev);
+
+	printk(KERN_INFO "%s: disconnected\n", idev->name);
+	fsm_change_state(fi, ST_WAIT_DHUP);
+	isdn_net_rm_from_bundle(idev);
+	return 0;
+}
+
+static int
+dhup(struct fsm_inst *fi, int pr, void *arg)
+{
+	isdn_net_dev *idev = fi->userdata;
+
+	printk(KERN_INFO "%s: Chargesum is %d\n", idev->name, idev->charge);
+	isdn_slot_all_eaz(idev->isdn_slot);
+	isdn_net_unbind_channel(idev);
+	return 0;
+}
+
+/* Check if it's time for idle hang-up */
+
+static int
+check_hup(struct fsm_inst *fi, int pr, void *arg)
+{
+	isdn_net_dev *idev = fi->userdata;
+	isdn_net_local *mlp = idev->mlp;
+
+	dbg_net_dial("%s: huptimer %d onhtime %d chargetime %ld chargeint %d\n",
+		     idev->name, idev->huptimer, mlp->onhtime, idev->chargetime, idev->chargeint);
+
+	if (idev->huptimer++ <= mlp->onhtime)
+		goto mod_timer;
+
+	if (mlp->hupflags & ISDN_CHARGEHUP &&
+	    idev->charge_state == ST_CHARGE_HAVE_CINT) {
+		if (!time_after(jiffies, idev->chargetime 
+				+ idev->chargeint - 2 * HZ))
+			goto mod_timer;
+	}
+	if (idev->outgoing || mlp->hupflags & ISDN_INHUP) {
+		isdn_net_hangup(idev);
+		return 0;
+	}
+ mod_timer:
+	mod_timer(&idev->dial_timer, idev->dial_timer.expires + HZ);
+	return 0;
+}
+
+/* Charge-info from TelCo. */
+
+static int
+got_cinf(struct fsm_inst *fi, int pr, void *arg)
+{
+	isdn_net_dev *idev = fi->userdata;
+
+	idev->charge++;
+	switch (idev->charge_state) {
+	case ST_CHARGE_NULL:
+		idev->charge_state = ST_CHARGE_GOT_CINF;
+		break;
+	case ST_CHARGE_GOT_CINF:
+		idev->charge_state = ST_CHARGE_HAVE_CINT;
+		/* fall through */
+	case ST_CHARGE_HAVE_CINT:
+		idev->chargeint = jiffies - idev->chargetime;
+		break;
+	}
+	idev->chargetime = jiffies;
+	dbg_net_dial("%s: got CINF\n", idev->name);
+	return 0;
+}
+
+/* Perform hangup for a net-interface. */
+
+int
+isdn_net_hangup(isdn_net_dev *idev)
+{
+	isdn_ctrl cmd;
+
+	del_timer(&idev->dial_timer);
+	if (!isdn_net_bound(idev)) {
+		isdn_BUG();
+		return 1;
+	}
+	printk(KERN_INFO "%s: local hangup\n", idev->name);
+	isdn_slot_command(idev->isdn_slot, ISDN_CMD_HANGUP, &cmd);
+	return 1;
+}
+
+/*
+ * Handle status-messages from ISDN-interfacecard.
+ * This function is called from within the main-status-dispatcher
+ * isdn_status_callback, which itself is called from the low-level driver.
+ * Return: 1 = event handled, 0 = not handled
+ */
+int
+isdn_net_stat_callback(int idx, isdn_ctrl *c)
+{
+	isdn_net_dev *idev = isdn_slot_idev(idx);
+
+	if (!idev) {
+		HERE;
+		return 0;
+	}
+	switch (c->command) {
+	case ISDN_STAT_DCONN:
+		return fsm_event(&idev->fi, EV_STAT_DCONN, c);
+	case ISDN_STAT_BCONN:
+		return fsm_event(&idev->fi, EV_STAT_BCONN, c);
+	case ISDN_STAT_BHUP:
+		return fsm_event(&idev->fi, EV_STAT_BHUP, c);
+	case ISDN_STAT_DHUP:
+		return fsm_event(&idev->fi, EV_STAT_DHUP, c);
+	case ISDN_STAT_CINF:
+		return fsm_event(&idev->fi, EV_STAT_CINF, c);
+	case ISDN_STAT_BSENT:
+		return fsm_event(&idev->fi, EV_STAT_BSENT, c);
+	default:
+		printk("unknown stat %d\n", c->command);
+		return 0;
+	}
+}
+
+int
+isdn_net_handle_event(isdn_net_dev *idev, int pr, void *arg)
+{
+	fsm_event(&idev->fi, pr, arg);
+}
+
+static int
+hang_up(struct fsm_inst *fi, int pr, void *arg)
+{
+	isdn_net_dev *idev = fi->userdata;
+
+	isdn_net_hangup(idev);
+	return 0;
+}
+
+static int
+got_bsent(struct fsm_inst *fi, int pr, void *arg)
+{
+	isdn_net_dev *idev = fi->userdata;
+	isdn_ctrl *c = arg;
+	
+	isdn_net_bsent(idev, c);
+	return 0;
+}
+
+static struct fsm_node isdn_net_fn_tbl[] = {
+	{ ST_NULL,           EV_CMD_DIAL,        dialout_first },
+
+	{ ST_OUT_WAIT_DCONN, EV_TIMER_DIAL,      dial_timeout  },
+	{ ST_OUT_WAIT_DCONN, EV_STAT_DCONN,      out_dconn     },
+	{ ST_OUT_WAIT_DCONN, EV_STAT_DHUP,       connect_fail  },
+	{ ST_OUT_WAIT_DCONN, EV_TIMER_CB_OUT,    hang_up       },
+
+	{ ST_OUT_WAIT_BCONN, EV_TIMER_DIAL,      dial_timeout  },
+	{ ST_OUT_WAIT_BCONN, EV_STAT_BCONN,      bconn         },
+	{ ST_OUT_WAIT_BCONN, EV_STAT_DHUP,       connect_fail  },
+
+	{ ST_IN_WAIT_DCONN,  EV_TIMER_INCOMING,  hang_up       },
+	{ ST_IN_WAIT_DCONN,  EV_STAT_DCONN,      in_dconn      },
+	{ ST_IN_WAIT_DCONN,  EV_STAT_DHUP,       connect_fail  },
+
+	{ ST_IN_WAIT_BCONN,  EV_TIMER_INCOMING,  hang_up       },
+	{ ST_IN_WAIT_BCONN,  EV_STAT_BCONN,      bconn         },
+	{ ST_IN_WAIT_BCONN,  EV_STAT_DHUP,       connect_fail  },
+
+	{ ST_ACTIVE,         EV_TIMER_HUP,       check_hup     },
+	{ ST_ACTIVE,         EV_STAT_BHUP,       bhup          },
+	{ ST_ACTIVE,         EV_STAT_CINF,       got_cinf      },
+	{ ST_ACTIVE,         EV_STAT_BSENT,      got_bsent     },
+
+	{ ST_WAIT_DHUP,      EV_STAT_DHUP,       dhup          },
+
+	{ ST_WAIT_BEFORE_CB, EV_TIMER_CB_IN,     dialout_first },
+
+	{ ST_OUT_DIAL_WAIT,  EV_TIMER_DIAL_WAIT, dialout_next  },
+};
+
+static struct fsm isdn_net_fsm = {
+	.st_cnt = ARRAY_SIZE(isdn_net_st_str),
+	.st_str = isdn_net_st_str,
+	.ev_cnt = ARRAY_SIZE(isdn_net_ev_str),
+	.ev_str = isdn_net_ev_str,
+	.fn_cnt = ARRAY_SIZE(isdn_net_fn_tbl),
+	.fn_tbl = isdn_net_fn_tbl,
+};
+
+static void isdn_net_dev_debug(struct fsm_inst *fi, char *fmt, ...)
+{
+	va_list args;
+	isdn_net_dev *idev = fi->userdata;
+	char buf[128];
+	char *p = buf;
+
+	va_start(args, fmt);
+	p += sprintf(p, "%s: ", idev->name);
+	p += vsprintf(p, fmt, args);
+	va_end(args);
+	printk(KERN_DEBUG "%s\n", buf);
+}
+
+void
+isdn_net_lib_init(void)
+{
+	fsm_new(&isdn_net_fsm);
+}
+
+void
+isdn_net_lib_exit(void)
+{
+	fsm_free(&isdn_net_fsm);
+}
diff -Nru a/drivers/isdn/i4l/isdn_ppp.c b/drivers/isdn/i4l/isdn_ppp.c
--- a/drivers/isdn/i4l/isdn_ppp.c	Mon Oct  7 11:25:36 2002
+++ b/drivers/isdn/i4l/isdn_ppp.c	Mon Oct  7 11:25:36 2002
@@ -23,7 +23,7 @@
 /* Prototypes */
 static int isdn_ppp_fill_rq(unsigned char *buf, int len, int proto, int slot);
 static int isdn_ppp_closewait(int slot);
-static void isdn_ppp_push_higher(isdn_net_dev * net_dev, isdn_net_local * lp,
+static void isdn_ppp_push_higher(isdn_net_local *lp, isdn_net_dev *idev,
 				 struct sk_buff *skb, int proto);
 static int isdn_ppp_if_get_unit(char *namebuf);
 static int isdn_ppp_set_compressor(struct ippp_struct *is,struct isdn_ppp_comp_data *);
@@ -59,10 +59,10 @@
 static ippp_bundle * isdn_ppp_bundle_arr = NULL;
  
 static int isdn_ppp_mp_bundle_array_init(void);
-static int isdn_ppp_mp_init( isdn_net_local * lp, ippp_bundle * add_to );
-static void isdn_ppp_mp_receive(isdn_net_dev * net_dev, isdn_net_local * lp, 
-							struct sk_buff *skb);
-static void isdn_ppp_mp_cleanup( isdn_net_local * lp );
+static int isdn_ppp_mp_init(isdn_net_local *lp, ippp_bundle *add_to);
+static void isdn_ppp_mp_receive(isdn_net_local *lp, isdn_net_dev *idev, 
+				struct sk_buff *skb);
+static void isdn_ppp_mp_cleanup(isdn_net_local *lp );
 
 static int isdn_ppp_bundle(struct ippp_struct *, int unit);
 #endif	/* CONFIG_ISDN_MPP */
@@ -100,9 +100,8 @@
  *       in this case we bind another lp to the master device
  */
 static void
-isdn_ppp_free(isdn_net_local * lp)
+isdn_ppp_free(isdn_net_dev *idev)
 {
-	isdn_net_dev *idev = lp->netdev;
 	unsigned long flags;
 	struct ippp_struct *is;
 
@@ -116,9 +115,9 @@
 	cli();
 
 #ifdef CONFIG_ISDN_MPP
-	spin_lock(&lp->netdev->pb->lock);
+	spin_lock(&idev->pb->lock);
 #endif
-	isdn_net_rm_from_bundle(lp);
+	isdn_net_rm_from_bundle(idev);
 #ifdef CONFIG_ISDN_MPP
 	if (lp->netdev->pb->ref_ct == 1)	/* last link in queue? */
 		isdn_ppp_mp_cleanup(lp);
@@ -139,7 +138,7 @@
 		is->state = IPPP_OPEN;	/* fallback to 'OPEN but not ASSIGNED' state */
 
 	if (is->debug & 0x1)
-		printk(KERN_DEBUG "isdn_ppp_free %d %p %p\n", idev->ppp_slot, lp, is->idev);
+		printk(KERN_DEBUG "isdn_ppp_free %d %p\n", idev->ppp_slot, is->idev);
 
 	is->idev = NULL;          /* link is down .. set lp to NULL */
 	idev->ppp_slot = -1;      /* is this OK ?? */
@@ -152,9 +151,8 @@
  * bind isdn_net_local <=> ippp-device
  */
 int
-isdn_ppp_bind(isdn_net_local * lp)
+isdn_ppp_bind(isdn_net_dev *idev)
 {
-	isdn_net_dev *idev = lp->netdev;
 	int i;
 	int unit = 0;
 	long flags;
@@ -226,10 +224,8 @@
  */
 
 static void
-isdn_ppp_wakeup_daemon(isdn_net_local *lp)
+isdn_ppp_wakeup_daemon(isdn_net_dev *idev)
 {
-	isdn_net_dev *idev = lp->netdev;
-
 	if (idev->ppp_slot < 0 || idev->ppp_slot >= ISDN_MAX_CHANNELS) {
 		printk(KERN_ERR "%s: ppp_slot(%d) out of range\n",
 		       __FUNCTION__, idev->ppp_slot);
@@ -314,8 +310,6 @@
 	is->maxcid = 16;        /* VJ: maxcid */
 	is->tk = current;
 	init_waitqueue_head(&is->wq);
-	is->first = is->rq + NUM_RCV_BUFFS - 1;	/* receive queue */
-	is->last = is->rq;
 	is->minor = minor;
 #ifdef CONFIG_ISDN_PPP_VJ
 	/*
@@ -337,7 +331,6 @@
 isdn_ppp_release(struct inode *ino, struct file *file)
 {
 	uint minor = minor(ino->i_rdev) - ISDN_MINOR_PPP;
-	int i;
 	struct ippp_struct *is;
 
 	lock_kernel();
@@ -356,14 +349,7 @@
 		is->state &= ~IPPP_CONNECT;
 		isdn_net_hangup(is->idev);
 	}
-	for (i = 0; i < NUM_RCV_BUFFS; i++) {
-		if (is->rq[i].buf) {
-			kfree(is->rq[i].buf);
-			is->rq[i].buf = NULL;
-		}
-	}
-	is->first = is->rq + NUM_RCV_BUFFS - 1;	/* receive queue */
-	is->last = is->rq;
+	skb_queue_purge(&is->rq);
 
 #ifdef CONFIG_ISDN_PPP_VJ
 /* TODO: if this was the previous master: link the slcomp to the new master */
@@ -487,7 +473,7 @@
 			if (val & SC_ENABLE_IP && !(is->pppcfg & SC_ENABLE_IP) && (is->state & IPPP_CONNECT)) {
 				if (idev) {
 					/* OK .. we are ready to send buffers */
-					netif_wake_queue(&idev->dev);
+					isdn_net_dev_wake_queue(idev);
 				}
 			}
 			is->pppcfg = val;
@@ -562,14 +548,16 @@
 			return isdn_ppp_set_compressor(is, &data);
 		case PPPIOCGCALLINFO:
 			{
+				isdn_net_local *mlp;
 				struct isdn_net_phone *phone;
 				struct pppcallinfo pci;
 				int i;
 				memset((char *) &pci,0,sizeof(struct pppcallinfo));
 				if(idev) {
-					strncpy(pci.local_num,idev->local.msn,63);
+					mlp = idev->mlp;
+					strncpy(pci.local_num, mlp->msn, 63);
 					i = 0;
-					list_for_each_entry(phone, &idev->local.phone[1], list) {
+					list_for_each_entry(phone, &mlp->phone[1], list) {
 						if (i++ == idev->dial) {
 							strncpy(pci.remote_num,phone->num,63);
 							break;
@@ -580,7 +568,7 @@
 						pci.calltype = CALLTYPE_OUTGOING;
 					else
 						pci.calltype = CALLTYPE_INCOMING;
-					if(idev->local.flags & ISDN_NET_CALLBACK)
+					if(mlp->flags & ISDN_NET_CALLBACK)
 						pci.calltype |= CALLTYPE_CALLBACK;
 				}
 				return set_arg((void *)arg,&pci,sizeof(struct pppcallinfo));
@@ -595,12 +583,8 @@
 isdn_ppp_poll(struct file *file, poll_table * wait)
 {
 	unsigned int mask;
-	struct ippp_buf_queue *bf;
-	struct ippp_buf_queue *bl;
-	unsigned long flags;
 	struct ippp_struct *is;
 
-	lock_kernel();
 	is = file->private_data;
 
 	if (is->debug & 0x2)
@@ -622,21 +606,15 @@
 	/* we're always ready to send .. */
 	mask = POLLOUT | POLLWRNORM;
 
-	save_flags(flags);
-	cli();
-	bl = is->last;
-	bf = is->first;
 	/*
 	 * if IPPP_NOBLOCK is set we return even if we have nothing to read
 	 */
-	if (bf->next != bl || (is->state & IPPP_NOBLOCK)) {
+	if (!skb_queue_empty(&is->rq) || is->state & IPPP_NOBLOCK) {
 		is->state &= ~IPPP_NOBLOCK;
 		mask |= POLLIN | POLLRDNORM;
 	}
-	restore_flags(flags);
 
  out:
-	unlock_kernel();
 	return mask;
 }
 
@@ -647,10 +625,8 @@
 static int
 isdn_ppp_fill_rq(unsigned char *buf, int len, int proto, int slot)
 {
-	struct ippp_buf_queue *bf,
-	*bl;
-	unsigned long flags;
-	unsigned char *nbuf;
+	struct sk_buff *skb;
+	unsigned char *p;
 	struct ippp_struct *is;
 
 	if (slot < 0 || slot >= ISDN_MAX_CHANNELS) {
@@ -663,36 +639,23 @@
 		printk(KERN_DEBUG "ippp: device not activated.\n");
 		return 0;
 	}
-	nbuf = (unsigned char *) kmalloc(len + 4, GFP_ATOMIC);
-	if (!nbuf) {
-		printk(KERN_WARNING "ippp: Can't alloc buf\n");
+	if (skb_queue_len(&is->rq) > IPPP_MAX_RQ_LEN) {
+		printk(KERN_WARNING "ippp: Queue is full\n");
 		return 0;
 	}
-	nbuf[0] = PPP_ALLSTATIONS;
-	nbuf[1] = PPP_UI;
-	nbuf[2] = proto >> 8;
-	nbuf[3] = proto & 0xff;
-	memcpy(nbuf + 4, buf, len);
-
-	save_flags(flags);
-	cli();
-
-	bf = is->first;
-	bl = is->last;
-
-	if (bf == bl) {
-		printk(KERN_WARNING "ippp: Queue is full; discarding first buffer\n");
-		bf = bf->next;
-		kfree(bf->buf);
-		is->first = bf;
+	skb = dev_alloc_skb(len + 4);
+	if (!skb) {
+		printk(KERN_WARNING "ippp: Can't alloc buf\n");
+		return 0;
 	}
-	bl->buf = (char *) nbuf;
-	bl->len = len + 4;
+	p = skb_put(skb, 4);
+	p += put_u8(p, PPP_ALLSTATIONS);
+	p += put_u8(p, PPP_UI);
+	p += put_u16(p, proto);
+	memcpy(skb_put(skb, len), buf, len);
 
-	is->last = bl->next;
-	restore_flags(flags);
-
-		wake_up_interruptible(&is->wq);
+	skb_queue_tail(&is->rq, skb);
+	wake_up_interruptible(&is->wq);
 
 	return len;
 }
@@ -706,54 +669,36 @@
 isdn_ppp_read(struct file *file, char *buf, size_t count, loff_t *off)
 {
 	struct ippp_struct *is;
-	struct ippp_buf_queue *b;
-	unsigned long flags;
-	unsigned char *save_buf;
+	struct sk_buff *skb;
 	int retval;
 
 	if (off != &file->f_pos)
 		return -ESPIPE;
 	
-	lock_kernel();
-
 	is = file->private_data;
 
 	if (!(is->state & IPPP_OPEN)) {
 		retval = 0;
 		goto out;
 	}
-	retval = verify_area(VERIFY_WRITE, (void *) buf, count);
-	if (retval)
-		goto out;
-
-	save_flags(flags);
-	cli();
-
-	b = is->first->next;
-	save_buf = b->buf;
-	if (!save_buf) {
-		restore_flags(flags);
+	skb = skb_dequeue(&is->rq);
+	if (!skb) {
 		retval = -EAGAIN;
 		goto out;
 	}
-	if (b->len < count)
-		count = b->len;
-	b->buf = NULL;
-	is->first = b;
-
-	restore_flags(flags);
-
-	if (copy_to_user(buf, save_buf, count)) {
-		kfree(save_buf);
+	if (skb->len > count) {
+		retval = -EMSGSIZE;
+		goto out_free;
+	}
+	if (copy_to_user(buf, skb->data, skb->len)) {
 		retval = -EFAULT;
-		goto out;
+		goto out_free;
 	}
-	kfree(save_buf);
-
-	retval = count;
+	retval = skb->len;
 
+ out_free:
+	dev_kfree_skb(skb);
  out:
-	unlock_kernel();
 	return retval;
 }
 
@@ -832,9 +777,9 @@
 				isdn_ppp_frame_log("xmit", skb->data, skb->len, 32,is->unit,idev->ppp_slot);
 			}
 
-			isdn_ppp_send_ccp(idev,&idev->local,skb); /* keeps CCP/compression states in sync */
+			isdn_ppp_send_ccp(idev,idev->mlp,skb); /* keeps CCP/compression states in sync */
 
-			isdn_net_write_super(&idev->local, skb);
+			isdn_net_write_super(idev, skb);
 		}
 	}
 	retval = count;
@@ -881,15 +826,7 @@
 		}
 		memset((char *) ippp_table[i], 0, sizeof(struct ippp_struct));
 		ippp_table[i]->state = 0;
-		ippp_table[i]->first = ippp_table[i]->rq + NUM_RCV_BUFFS - 1;
-		ippp_table[i]->last = ippp_table[i]->rq;
-
-		for (j = 0; j < NUM_RCV_BUFFS; j++) {
-			ippp_table[i]->rq[j].buf = NULL;
-			ippp_table[i]->rq[j].last = ippp_table[i]->rq +
-			    (NUM_RCV_BUFFS + j - 1) % NUM_RCV_BUFFS;
-			ippp_table[i]->rq[j].next = ippp_table[i]->rq + (j + 1) % NUM_RCV_BUFFS;
-		}
+		skb_queue_head_init(&ippp_table[i]->rq);
 	}
 	return 0;
 }
@@ -963,10 +900,9 @@
 /*
  * handler for incoming packets on a syncPPP interface
  */
-static void isdn_ppp_receive(isdn_net_dev *net_dev, isdn_net_local *lp, 
+static void isdn_ppp_receive(isdn_net_local *lp, isdn_net_dev *idev, 
 			     struct sk_buff *skb)
 {
-	isdn_net_dev *idev = lp->netdev;
 	struct ippp_struct *is;
 	int slot;
 	int proto;
@@ -976,7 +912,7 @@
 	 * huptimer on LCP packets.
 	 */
 	if (PPP_PROTOCOL(skb->data) != PPP_LCP)
-		isdn_net_reset_huptimer(net_dev,lp->netdev);
+		idev->huptimer = 0;
 
 	slot = idev->ppp_slot;
 	if (slot < 0 || slot > ISDN_MAX_CHANNELS) {
@@ -1012,12 +948,12 @@
 	
  	if (!(is->mpppcfg & SC_REJ_MP_PROT)) { // we agreed to receive MPPP
   		if (proto == PPP_MP) {
-  			isdn_ppp_mp_receive(net_dev, lp, skb);
+  			isdn_ppp_mp_receive(lp, idev, skb);
  			return;
  		}
  	} 
 #endif
- 	isdn_ppp_push_higher(net_dev, lp, skb, proto);
+ 	isdn_ppp_push_higher(lp, idev, skb, proto);
 }
 
 /*
@@ -1026,10 +962,10 @@
  * note: net_dev has to be master net_dev
  */
 static void
-isdn_ppp_push_higher(isdn_net_dev * net_dev, isdn_net_local * lp, struct sk_buff *skb, int proto)
+isdn_ppp_push_higher(isdn_net_local *lp, isdn_net_dev *idev,
+		     struct sk_buff *skb, int proto)
 {
-	isdn_net_dev *idev = lp->netdev;
-	struct net_device *dev = &net_dev->dev;
+	struct net_device *dev = &lp->dev;
  	struct ippp_struct *is, *mis;
 	int slot;
 
@@ -1041,14 +977,6 @@
 	}
 	is = ippp_table[slot];
  	
- 	if (lp->master) { // FIXME?
- 		slot = ((isdn_net_local *) (lp->master->priv))->netdev->ppp_slot;
- 		if (slot < 0 || slot > ISDN_MAX_CHANNELS) {
- 			printk(KERN_ERR "isdn_ppp_push_higher: master->ppp_slot(%d)\n",
-			       slot);
-			goto drop_packet;
- 		}
- 	}
  	mis = ippp_table[slot];
 
 	if (is->debug & 0x10) {
@@ -1079,12 +1007,12 @@
 		case PPP_VJC_UNCOMP:
 			if (is->debug & 0x20)
 				printk(KERN_DEBUG "isdn_ppp: VJC_UNCOMP\n");
-			if (net_dev->ppp_slot < 0) {
+			if (idev->ppp_slot < 0) {
 				printk(KERN_ERR "%s: net_dev->ppp_slot(%d) out of range\n",
-				       __FUNCTION__ , net_dev->ppp_slot);
+				       __FUNCTION__ , idev->ppp_slot);
 				goto drop_packet;
 			}
-			if (slhc_remember(ippp_table[net_dev->ppp_slot]->slcomp, skb->data, skb->len) <= 0) {
+			if (slhc_remember(ippp_table[idev->ppp_slot]->slcomp, skb->data, skb->len) <= 0) {
 				printk(KERN_WARNING "isdn_ppp: received illegal VJC_UNCOMP frame!\n");
 				goto drop_packet;
 			}
@@ -1105,12 +1033,12 @@
 				}
 				skb_put(skb, skb_old->len + 128);
 				memcpy(skb->data, skb_old->data, skb_old->len);
-				if (net_dev->ppp_slot < 0) {
+				if (idev->ppp_slot < 0) {
 					printk(KERN_ERR "%s: net_dev->ppp_slot(%d) out of range\n",
-					       __FUNCTION__ , net_dev->ppp_slot);
+					       __FUNCTION__ , idev->ppp_slot);
 					goto drop_packet;
 				}
-				pkt_len = slhc_uncompress(ippp_table[net_dev->ppp_slot]->slcomp,
+				pkt_len = slhc_uncompress(ippp_table[idev->ppp_slot]->slcomp,
 						skb->data, skb_old->len);
 				kfree_skb(skb_old);
 				if (pkt_len < 0)
@@ -1123,7 +1051,7 @@
 #endif
 		case PPP_CCP:
 		case PPP_CCPFRAG:
-			isdn_ppp_receive_ccp(net_dev,lp,skb,proto);
+			isdn_ppp_receive_ccp(idev,lp,skb,proto);
 			/* Dont pop up ResetReq/Ack stuff to the daemon any
 			   longer - the job is done already */
 			if(skb->data[0] == CCP_RESETREQ ||
@@ -1146,7 +1074,7 @@
 	return;
 
  drop_packet:
-	net_dev->local.stats.rx_dropped++;
+	lp->stats.rx_dropped++;
 	kfree_skb(skb);
 }
 
@@ -1184,20 +1112,21 @@
  * skb isn't allowed!!
  */
 
-int
-isdn_ppp_xmit(struct sk_buff *skb, struct net_device *netdev)
+static int
+isdn_ppp_start_xmit(struct sk_buff *skb, struct net_device *ndev)
 {
-	isdn_net_local *lp,*mlp;
-	isdn_net_dev *idev;
-	isdn_net_dev *nd;
+	isdn_net_local *mlp = ndev->priv;
+	isdn_net_dev *idev = list_entry(mlp->online.next, isdn_net_dev, online);
 	unsigned int proto = PPP_IP;     /* 0x21 */
 	struct ippp_struct *ipt,*ipts;
 	int slot;
 
-	mlp = (isdn_net_local *) (netdev->priv);
-	nd = mlp->netdev;       /* get master lp */
+	ndev->trans_start = jiffies;
+
+	if (list_empty(&mlp->online))
+		return isdn_net_autodial(skb, ndev);
 
-	slot = nd->ppp_slot;
+	slot = idev->ppp_slot;
 	if (slot < 0 || slot > ISDN_MAX_CHANNELS) {
 		printk(KERN_ERR "isdn_ppp_xmit: lp->ppp_slot(%d)\n",
 		       slot);
@@ -1208,7 +1137,8 @@
 
 	if (!(ipts->pppcfg & SC_ENABLE_IP)) {	/* PPP connected ? */
 		if (ipts->debug & 0x1)
-			printk(KERN_INFO "%s: IP frame delayed.\n", netdev->name);
+			printk(KERN_INFO "%s: IP frame delayed.\n", ndev->name);
+		netif_stop_queue(ndev);
 		return 1;
 	}
 
@@ -1226,13 +1156,13 @@
 			return 0;
 	}
 
-	lp = isdn_net_get_locked_lp(nd);
-	if (!lp) {
-		printk(KERN_WARNING "%s: all channels busy - requeuing!\n", netdev->name);
+	idev = isdn_net_get_locked_dev(mlp);
+	if (!idev) {
+		printk(KERN_WARNING "%s: all channels busy - requeuing!\n", ndev->name);
+		netif_stop_queue(ndev);
 		return 1;
 	}
 	/* we have our lp locked from now on */
-	idev = lp->netdev;
 	slot = idev->ppp_slot;
 	if (slot < 0 || slot > ISDN_MAX_CHANNELS) {
 		printk(KERN_ERR "isdn_ppp_xmit: lp->ppp_slot(%d)\n",
@@ -1385,10 +1315,10 @@
 		isdn_ppp_frame_log("xmit", skb->data, skb->len, 32,ipt->unit,idev->ppp_slot);
 	}
 	
-	isdn_net_writebuf_skb(lp, skb);
+	isdn_net_writebuf_skb(idev, skb);
 
  unlock:
-	spin_unlock_bh(&lp->xmit_lock);
+	spin_unlock_bh(&idev->xmit_lock);
 	return 0;
 }
 
@@ -1459,7 +1389,7 @@
 		is->mp_seqno = 0;
 		if ((lp->netdev->pb = isdn_ppp_mp_bundle_alloc()) == NULL)
 			return -ENOMEM;
-		lp->next = lp->last = lp;	/* nobody else in a queue */
+
 		lp->netdev->pb->frags = NULL;
 		lp->netdev->pb->frames = 0;
 		lp->netdev->pb->seq = LONG_MAX;
@@ -1479,12 +1409,12 @@
 static void isdn_ppp_mp_free_skb( ippp_bundle * mp, struct sk_buff * skb );
 static void isdn_ppp_mp_print_recv_pkt( int slot, struct sk_buff * skb );
 
-static void isdn_ppp_mp_receive(isdn_net_dev * net_dev, isdn_net_local * lp, 
-							struct sk_buff *skb)
+static void isdn_ppp_mp_receive(isdn_net_local *lp, isdn_net_dev *dev, 
+				struct sk_buff *skb)
 {
 	isdn_net_dev *idev = lp->netdev;
 	struct ippp_struct *is;
-	isdn_net_local * lpq;
+	isdn_net_dev *qdev;
 	ippp_bundle * mp;
 	isdn_mppp_stats * stats;
 	struct sk_buff * newfrag, * frag, * start, *nextf;
@@ -1492,8 +1422,8 @@
 	unsigned long flags;
 	int slot;
 
-	spin_lock_irqsave(&net_dev->pb->lock, flags);
-    	mp = net_dev->pb;
+	spin_lock_irqsave(&lp->netdev->pb->lock, flags);
+    	mp = lp->netdev->pb;
         stats = &mp->stats;
 	slot = idev->ppp_slot;
 	if (slot < 0 || slot > ISDN_MAX_CHANNELS) {
@@ -1531,8 +1461,8 @@
 	
 	/* find the minimum received sequence number over all links */
 	is->last_link_seqno = minseq = newseq;
-	for (lpq = net_dev->queue;;) {
-		slot = lpq->netdev->ppp_slot;
+	list_for_each_entry(qdev, &lp->online, online) {
+		slot = qdev->ppp_slot;
 		if (slot < 0 || slot > ISDN_MAX_CHANNELS) {
 			printk(KERN_ERR "%s: lpq->ppp_slot(%d)\n",
 			       __FUNCTION__ ,slot);
@@ -1541,8 +1471,6 @@
 			if (MP_LT(lls, minseq))
 				minseq = lls;
 		}
-		if ((lpq = lpq->next) == net_dev->queue)
-			break;
 	}
 	if (MP_LT(minseq, mp->seq))
 		minseq = mp->seq;	/* can't go beyond already processed
@@ -1631,7 +1559,7 @@
     		if (start != NULL && (MP_FLAGS(frag) & MP_END_FRAG)) {
       			minseq = mp->seq = (thisseq+1) & MP_LONGSEQ_MASK;
       			/* Reassemble the packet then dispatch it */
-			isdn_ppp_mp_reassembly(net_dev, lp, start, nextf);
+			isdn_ppp_mp_reassembly(lp->netdev, lp, start, nextf);
       
       			start = NULL;
       			frag = NULL;
@@ -1808,7 +1736,7 @@
 		}
 	}
    	proto = isdn_ppp_strip_proto(skb);
-	isdn_ppp_push_higher(net_dev, lp, skb, proto);
+	isdn_ppp_push_higher(lp, idev, skb, proto);
 }
 
 static void isdn_ppp_mp_free_skb(ippp_bundle * mp, struct sk_buff * skb)
@@ -1844,7 +1772,7 @@
     	spin_lock_irqsave(&p->pb->lock, flags);
 
 	nidev = is->idev;
-	idev = p->queue->netdev;
+	idev = list_entry(p->mlp->online.next, isdn_net_dev, online);
 	if( nidev->ppp_slot < 0 || nidev->ppp_slot >= ISDN_MAX_CHANNELS ||
 	    idev ->ppp_slot < 0 || idev ->ppp_slot >= ISDN_MAX_CHANNELS ) {
 		printk(KERN_ERR "ippp_bundle: binding to invalid slot %d\n",
@@ -1854,7 +1782,7 @@
 		goto out;
  	}
 
-	isdn_net_add_to_bundle(p, &nidev->local);
+	isdn_net_add_to_bundle(p->mlp, nidev);
 
 	ippp_table[nidev->ppp_slot]->unit = ippp_table[idev->ppp_slot]->unit;
 
@@ -1863,7 +1791,7 @@
 		(SC_ENABLE_IP | SC_NO_TCP_CCID | SC_REJ_COMP_TCP);
 	ippp_table[nidev->ppp_slot]->mpppcfg |= ippp_table[idev->ppp_slot]->mpppcfg &
 		(SC_MP_PROT | SC_REJ_MP_PROT | SC_OUT_SHORT_SEQ | SC_IN_SHORT_SEQ);
-	rc = isdn_ppp_mp_init(&nidev->local, p->pb);
+	rc = isdn_ppp_mp_init(nidev->mlp, p->pb);
 out:
 	spin_unlock_irqrestore(&p->pb->lock, flags);
 	return rc;
@@ -1936,7 +1864,7 @@
 			if(copy_to_user(r, PPP_VERSION, len)) error = -EFAULT;
 			break;
 		case SIOCGPPPSTATS:
-			error = isdn_ppp_dev_ioctl_stats(lp->netdev->ppp_slot, ifr, dev);
+			error = isdn_ppp_dev_ioctl_stats(0, ifr, dev);
 			break;
 		default:
 			error = -EINVAL;
@@ -1977,28 +1905,25 @@
 {
 #ifdef CONFIG_ISDN_MPP
 	isdn_net_dev *idev;
-	isdn_net_local *lp;
-	struct net_device *sdev;
+	isdn_net_dev *sdev;
 
 	idev = isdn_net_findif(name);
 	if (!idev)
 		return 1;
 
-	lp = &idev->local;
 	if (!isdn_net_bound(idev))
 		return 5;
 
-	sdev = lp->slave;
+	sdev = idev->slave;
 	while (sdev) {
-		isdn_net_local *mlp = (isdn_net_local *) sdev->priv;
-		if (!isdn_net_bound(mlp->netdev))
+		if (!isdn_net_bound(sdev))
 			break;
-		sdev = mlp->slave;
+		sdev = sdev->slave;
 	}
 	if (!sdev)
 		return 2;
 
-	isdn_net_dial_req((isdn_net_local *) sdev->priv);
+	isdn_net_dial_req(sdev);
 	return 0;
 #else
 	return -1;
@@ -2009,35 +1934,20 @@
 isdn_ppp_hangup_slave(char *name)
 {
 #ifdef CONFIG_ISDN_MPP
-	isdn_net_dev *idev;
-	isdn_net_local *lp, *mlp = NULL;
-	struct net_device *sdev;
+	isdn_net_dev *idev, *sdev;
 
 	idev = isdn_net_findif(name);
 	if (!idev)
 		return 1;
-	lp = &idev->local;
+
 	if (!isdn_net_bound(idev))
 		return 5;
 
-	sdev = lp->slave;
-	while (sdev) {
-		mlp = (isdn_net_local *) sdev->priv;
-
-		if (mlp->slave) { /* find last connected link in chain */
-			isdn_net_local *nlp = (isdn_net_local *) mlp->slave->priv;
-
-			if (!isdn_net_bound(nlp->netdev))
-				break;
-		} else if (isdn_net_bound(mlp->netdev))
-			break;
-		
-		sdev = mlp->slave;
-	}
-	if (!sdev)
+	sdev = idev->slave;
+	if (!sdev || !isdn_net_bound(sdev))
 		return 2;
 
-	isdn_net_hangup(mlp->netdev);
+	isdn_net_hangup(sdev);
 	return 0;
 #else
 	return -1;
@@ -2139,7 +2049,7 @@
 	printk(KERN_DEBUG "Sending CCP Frame:\n");
 	isdn_ppp_frame_log("ccp-xmit", skb->data, skb->len, 32, is->unit,idev->ppp_slot);
 
-	isdn_net_write_super(&idev->local, skb);
+	isdn_net_write_super(idev, skb);
 }
 
 /* Allocate the reset state vector */
@@ -2537,10 +2447,9 @@
  * we received a CCP frame .. 
  * not a clean solution, but we MUST handle a few cases in the kernel
  */
-static void isdn_ppp_receive_ccp(isdn_net_dev *net_dev, isdn_net_local *lp,
+static void isdn_ppp_receive_ccp(isdn_net_dev *idev, isdn_net_local *lp,
 	 struct sk_buff *skb,int proto)
 {
-	isdn_net_dev *idev = lp->netdev;
 	struct ippp_struct *is;
 	struct ippp_struct *mis;
 	int len;
@@ -2557,16 +2466,7 @@
 	is = ippp_table[idev->ppp_slot];
 	isdn_ppp_frame_log("ccp-rcv", skb->data, skb->len, 32, is->unit,idev->ppp_slot);
 
-	if(lp->master) {
-		int slot = ((isdn_net_local *) (lp->master->priv))->netdev->ppp_slot;
-		if (slot < 0 || slot > ISDN_MAX_CHANNELS) {
-			printk(KERN_ERR "%s: slot(%d) out of range\n",
-			       __FUNCTION__ , slot);
-			return;
-		}	
-		mis = ippp_table[slot];
-	} else
-		mis = is;
+	mis = is;
 
 	switch(skb->data[0]) {
 	case CCP_CONFREQ:
@@ -2715,9 +2615,8 @@
  * and I tried to modify this file according to that. --abp
  */
 
-static void isdn_ppp_send_ccp(isdn_net_dev *net_dev, isdn_net_local *lp, struct sk_buff *skb)
+static void isdn_ppp_send_ccp(isdn_net_dev *idev, isdn_net_local *lp, struct sk_buff *skb)
 {
-	isdn_net_dev *idev = lp->netdev;
 	struct ippp_struct *mis,*is;
 	int proto, slot = idev->ppp_slot;
 	unsigned char *data;
@@ -2745,16 +2644,7 @@
 	printk(KERN_DEBUG "Received CCP frame from daemon:\n");
 	isdn_ppp_frame_log("ccp-xmit", skb->data, skb->len, 32, is->unit,idev->ppp_slot);
 
-	if (lp->master) {
-		slot = ((isdn_net_local *) (lp->master->priv))->netdev->ppp_slot;
-		if (slot < 0 || slot > ISDN_MAX_CHANNELS) {
-			printk(KERN_ERR "%s: slot(%d) out of range\n",
-			       __FUNCTION__ , slot);
-			return;
-		}	
-		mis = ippp_table[slot];
-	} else
-		mis = is;
+	mis = is;
 	if (mis != is)
 		printk(KERN_DEBUG "isdn_ppp: Ouch! Master CCP sends on slave slot!\n");
 	
@@ -2926,6 +2816,7 @@
 }
 
 struct isdn_netif_ops isdn_ppp_ops = {
+	.hard_start_xmit     = isdn_ppp_start_xmit,
 	.hard_header         = isdn_ppp_header,
 	.do_ioctl            = isdn_ppp_dev_ioctl,
 	.flags               = IFF_NOARP | IFF_POINTOPOINT,
diff -Nru a/drivers/isdn/i4l/isdn_ppp.h b/drivers/isdn/i4l/isdn_ppp.h
--- a/drivers/isdn/i4l/isdn_ppp.h	Mon Oct  7 11:25:29 2002
+++ b/drivers/isdn/i4l/isdn_ppp.h	Mon Oct  7 11:25:29 2002
@@ -20,20 +20,6 @@
 extern int isdn_ppp_dial_slave(char *);
 extern int isdn_ppp_hangup_slave(char *);
 
-#ifdef CONFIG_ISDN_PPP
-
-int  isdn_ppp_xmit(struct sk_buff *, struct net_device *);
-
-#else
-
-static inline int
-isdn_ppp_xmit(struct sk_buff *, struct net_device *);
-{
-	return 0;
-}
-
-#endif
-
 #define IPPP_OPEN	0x01
 #define IPPP_CONNECT	0x02
 #define IPPP_CLOSEWAIT	0x04
diff -Nru a/drivers/isdn/i4l/isdn_tty.c b/drivers/isdn/i4l/isdn_tty.c
--- a/drivers/isdn/i4l/isdn_tty.c	Mon Oct  7 11:25:26 2002
+++ b/drivers/isdn/i4l/isdn_tty.c	Mon Oct  7 11:25:26 2002
@@ -101,7 +101,7 @@
 #endif
 					if (info->emu.mdmreg[REG_CPPP] & BIT_CPPP)
 						tty->flip.flag_buf_ptr[len - 1] = 0xff;
-					queue_task(&tty->flip.tqueue, &tq_timer);
+					schedule_delayed_work(&tty->flip.work, 1);
 					kfree_skb(skb);
 					return 1;
 				}
@@ -153,7 +153,7 @@
 							tty->flip.flag_buf_ptr += r;
 							tty->flip.char_buf_ptr += r;
 							if (r)
-								queue_task(&tty->flip.tqueue, &tq_timer);
+								schedule_delayed_work(&tty->flip.work, 1);
 							restore_flags(flags);
 						}
 					} else
@@ -2498,7 +2498,7 @@
 
 	} else {
 		restore_flags(flags);
-		queue_task(&tty->flip.tqueue, &tq_timer);
+		schedule_delayed_work(&tty->flip.work, 1);
 	}
 }
 
diff -Nru a/drivers/isdn/pcbit/drv.c b/drivers/isdn/pcbit/drv.c
--- a/drivers/isdn/pcbit/drv.c	Mon Oct  7 11:25:26 2002
+++ b/drivers/isdn/pcbit/drv.c	Mon Oct  7 11:25:26 2002
@@ -130,9 +130,7 @@
 	memset(dev->b2, 0, sizeof(struct pcbit_chan));
 	dev->b2->id = 1;
 
-	dev->qdelivery.sync = 0;
-	dev->qdelivery.routine = pcbit_deliver;
-	dev->qdelivery.data = dev;
+	INIT_WORK(&dev->qdelivery, pcbit_deliver, dev);
 
 	/*
 	 *  interrupts
diff -Nru a/drivers/isdn/pcbit/layer2.c b/drivers/isdn/pcbit/layer2.c
--- a/drivers/isdn/pcbit/layer2.c	Mon Oct  7 11:25:36 2002
+++ b/drivers/isdn/pcbit/layer2.c	Mon Oct  7 11:25:36 2002
@@ -30,7 +30,7 @@
 #include <linux/types.h>
 #include <linux/slab.h>
 #include <linux/interrupt.h>
-#include <linux/tqueue.h>
+#include <linux/workqueue.h>
 #include <linux/mm.h>
 #include <linux/skbuff.h>
 
@@ -81,8 +81,7 @@
 static __inline__ void
 pcbit_sched_delivery(struct pcbit_dev *dev)
 {
-	queue_task(&dev->qdelivery, &tq_immediate);
-	mark_bh(IMMEDIATE_BH);
+	schedule_work(&dev->qdelivery);
 }
 
 
diff -Nru a/drivers/isdn/pcbit/pcbit.h b/drivers/isdn/pcbit/pcbit.h
--- a/drivers/isdn/pcbit/pcbit.h	Mon Oct  7 11:25:25 2002
+++ b/drivers/isdn/pcbit/pcbit.h	Mon Oct  7 11:25:25 2002
@@ -12,7 +12,7 @@
 #ifndef PCBIT_H
 #define PCBIT_H
 
-#include <linux/tqueue.h>
+#include <linux/workqueue.h>
 
 #define MAX_PCBIT_CARDS 4
 
@@ -74,7 +74,7 @@
 
 	struct timer_list error_recover_timer;
 
-	struct tq_struct qdelivery;
+	struct work_struct qdelivery;
 
 	u_char w_busy;
 	u_char r_busy;
diff -Nru a/drivers/isdn/tpam/tpam.h b/drivers/isdn/tpam/tpam.h
--- a/drivers/isdn/tpam/tpam.h	Mon Oct  7 11:25:26 2002
+++ b/drivers/isdn/tpam/tpam.h	Mon Oct  7 11:25:26 2002
@@ -16,7 +16,7 @@
 
 #include <linux/isdnif.h>
 #include <linux/init.h>
-#include <linux/tqueue.h>
+#include <linux/workqueue.h>
 
 /* Maximum number of channels for this board */
 #define TPAM_NBCHANNEL		30
@@ -86,8 +86,8 @@
 	int roundrobin;			/* round robin between channels */
 	struct sk_buff_head sendq;	/* send queue */
 	struct sk_buff_head recvq;	/* receive queue */
-	struct tq_struct send_tq;	/* send task queue */
-	struct tq_struct recv_tq;	/* receive task queue */
+	struct work_struct send_tq;	/* send task queue */
+	struct work_struct recv_tq;	/* receive task queue */
 	spinlock_t lock;		/* lock for the card */
 } tpam_card;
 
diff -Nru a/drivers/isdn/tpam/tpam_main.c b/drivers/isdn/tpam/tpam_main.c
--- a/drivers/isdn/tpam/tpam_main.c	Mon Oct  7 11:25:26 2002
+++ b/drivers/isdn/tpam/tpam_main.c	Mon Oct  7 11:25:26 2002
@@ -176,10 +176,8 @@
 	card->loopmode = 0;
 	skb_queue_head_init(&card->sendq);
 	skb_queue_head_init(&card->recvq);
-	card->recv_tq.routine = (void *) (void *) tpam_recv_tq;
-	card->recv_tq.data = card;
-	card->send_tq.routine = (void *) (void *) tpam_send_tq;
-	card->send_tq.data = card;
+	INIT_WORK(&card->recv_tq, (void *) (void *) tpam_recv_tq, card);
+	INIT_WORK(&card->send_tq, (void *) (void *) tpam_send_tq, card);
 
 	/* add the board at the end of the list of boards */
 	card->next = NULL;
diff -Nru a/drivers/isdn/tpam/tpam_queues.c b/drivers/isdn/tpam/tpam_queues.c
--- a/drivers/isdn/tpam/tpam_queues.c	Mon Oct  7 11:25:25 2002
+++ b/drivers/isdn/tpam/tpam_queues.c	Mon Oct  7 11:25:25 2002
@@ -13,7 +13,7 @@
 
 #include <linux/pci.h>
 #include <linux/sched.h>
-#include <linux/tqueue.h>
+#include <linux/workqueue.h>
 #include <linux/interrupt.h>
 #include <asm/io.h>
 
@@ -36,8 +36,7 @@
 	skb_queue_tail(&card->sendq, skb);
 
 	/* queue the board's send task struct for immediate treatment */
-	queue_task(&card->send_tq, &tq_immediate);
-	mark_bh(IMMEDIATE_BH);
+	schedule_work(&card->send_tq);
 }
 
 /*
@@ -58,8 +57,7 @@
 		skb_queue_tail(&channel->sendq, skb);
 
 	/* queue the channel's send task struct for immediate treatment */
-	queue_task(&channel->card->send_tq, &tq_immediate);
-	mark_bh(IMMEDIATE_BH);
+	schedule_work(&channel->card->send_tq);
 }
 
 /*
@@ -169,8 +167,7 @@
 		else {
 			/* put the message in the receive queue */
 			skb_queue_tail(&card->recvq, skb);
-			queue_task(&card->recv_tq, &tq_immediate);
-			mark_bh(IMMEDIATE_BH);
+			schedule_work(&card->recv_tq);
 		}
 		return;
 	}
@@ -187,8 +184,7 @@
 		spin_unlock(&card->lock);
 
 		/* schedule the send queue for execution */
-		queue_task(&card->send_tq, &tq_immediate);
-		mark_bh(IMMEDIATE_BH);
+		schedule_work(&card->send_tq);
 		return;
 	}
 
diff -Nru a/drivers/md/md.c b/drivers/md/md.c
--- a/drivers/md/md.c	Mon Oct  7 11:25:29 2002
+++ b/drivers/md/md.c	Mon Oct  7 11:25:29 2002
@@ -1389,10 +1389,9 @@
 #endif
 	}
 
-	disk = kmalloc(sizeof(struct gendisk), GFP_KERNEL);
+	disk = alloc_disk();
 	if (!disk)
 		return -ENOMEM;
-	memset(disk, 0, sizeof(struct gendisk));
 	disk->major = MD_MAJOR;
 	disk->first_minor = mdidx(mddev);
 	disk->minor_shift = 0;
@@ -1408,7 +1407,7 @@
 	if (err) {
 		printk(KERN_ERR "md: pers->run() failed ...\n");
 		mddev->pers = NULL;
-		kfree(disk);
+		put_disk(disk);
 		return -EINVAL;
 	}
 
@@ -1538,7 +1537,7 @@
 
 		if (disk) {
 			del_gendisk(disk);
-			kfree(disk);
+			put_disk(disk);
 		}
 
 	} else
diff -Nru a/drivers/media/radio/radio-sf16fmi.c b/drivers/media/radio/radio-sf16fmi.c
--- a/drivers/media/radio/radio-sf16fmi.c	Mon Oct  7 11:25:38 2002
+++ b/drivers/media/radio/radio-sf16fmi.c	Mon Oct  7 11:25:38 2002
@@ -116,15 +116,8 @@
 	val = dev->curvol ? 0x08 : 0x00;	/* unmute/mute */
 	outb(val, myport);
 	outb(val | 0x10, myport);
-	for(i=0; i< 100; i++)
-	{
-		udelay(1400);
-		cond_resched();
-	}
-/* If this becomes allowed use it ... 	
-	current->state = TASK_UNINTERRUPTIBLE;
+	set_current_state(TASK_UNINTERRUPTIBLE);
 	schedule_timeout(HZ/7);
-*/	
 	res = (int)inb(myport+1);
 	outb(val, myport);
 	
@@ -296,7 +289,7 @@
 	if (io < 0)
 		io = isapnp_fmi_probe();
 	if (io < 0) {
-		printk(KERN_ERR "radio-sf16fmi: No PnP card found.");
+		printk(KERN_ERR "radio-sf16fmi: No PnP card found.\n");
 		return io;
 	}
 	if (!request_region(io, 2, "radio-sf16fmi")) {
diff -Nru a/drivers/media/video/cpia_pp.c b/drivers/media/video/cpia_pp.c
--- a/drivers/media/video/cpia_pp.c	Mon Oct  7 11:25:25 2002
+++ b/drivers/media/video/cpia_pp.c	Mon Oct  7 11:25:25 2002
@@ -32,7 +32,7 @@
 #include <linux/parport.h>
 #include <linux/interrupt.h>
 #include <linux/delay.h>
-#include <linux/tqueue.h>
+#include <linux/workqueue.h>
 #include <linux/smp_lock.h>
 
 #include <linux/kmod.h>
@@ -137,7 +137,7 @@
 struct pp_cam_entry {
 	struct pardevice *pdev;
 	struct parport *port;
-	struct tq_struct cb_task;
+	struct work_struct cb_task;
 	int open_count;
 	wait_queue_head_t wq_stream;
 	/* image state flags */
@@ -518,8 +518,7 @@
 	int retval = 0;
 	
 	if(cam->port->irq != PARPORT_IRQ_NONE) {
-		cam->cb_task.routine = cb;
-		cam->cb_task.data = cbdata;
+		INIT_WORK(&cam->cb_task, cb, cbdata);
 	} else {
 		retval = -1;
 	}
diff -Nru a/drivers/message/fusion/Makefile b/drivers/message/fusion/Makefile
--- a/drivers/message/fusion/Makefile	Mon Oct  7 11:25:36 2002
+++ b/drivers/message/fusion/Makefile	Mon Oct  7 11:25:36 2002
@@ -13,7 +13,7 @@
 #			# sparc64
 #EXTRA_CFLAGS += -gstabs+
 
-EXTRA_CFLAGS += -I. ${MPT_CFLAGS}
+EXTRA_CFLAGS += ${MPT_CFLAGS}
 
 # Fusion MPT drivers; recognized debug defines...
 #  MPT general:
diff -Nru a/drivers/message/fusion/mptlan.c b/drivers/message/fusion/mptlan.c
--- a/drivers/message/fusion/mptlan.c	Mon Oct  7 11:25:35 2002
+++ b/drivers/message/fusion/mptlan.c	Mon Oct  7 11:25:35 2002
@@ -132,7 +132,7 @@
 	u32 total_received;
 	struct net_device_stats stats;	/* Per device statistics */
 
-	struct tq_struct post_buckets_task;
+	struct work_struct post_buckets_task;
 	unsigned long post_buckets_active;
 };
 
@@ -876,10 +876,9 @@
 	
 	if (test_and_set_bit(0, &priv->post_buckets_active) == 0) {
 		if (priority) {
-			queue_task(&priv->post_buckets_task, &tq_immediate);
-			mark_bh(IMMEDIATE_BH);
+			schedule_work(&priv->post_buckets_task);
 		} else {
-			queue_task(&priv->post_buckets_task, &tq_timer);
+			schedule_delayed_work(&priv->post_buckets_task, 1);
 			dioprintk((KERN_INFO MYNAM ": post_buckets queued on "
 				   "timer.\n"));
 		}
@@ -1365,9 +1364,8 @@
 	priv->mpt_dev = mpt_dev;
 	priv->pnum = pnum;
 
-	memset(&priv->post_buckets_task, 0, sizeof(struct tq_struct));
-	priv->post_buckets_task.routine = mpt_lan_post_receive_buckets;
-	priv->post_buckets_task.data = dev;
+	memset(&priv->post_buckets_task, 0, sizeof(struct work_struct));
+	INIT_WORK(&priv->post_buckets_task, mpt_lan_post_receive_buckets, dev);
 	priv->post_buckets_active = 0;
 
 	dlprintk((KERN_INFO MYNAM "@%d: bucketlen = %d\n",
diff -Nru a/drivers/message/fusion/mptlan.h b/drivers/message/fusion/mptlan.h
--- a/drivers/message/fusion/mptlan.h	Mon Oct  7 11:25:29 2002
+++ b/drivers/message/fusion/mptlan.h	Mon Oct  7 11:25:29 2002
@@ -20,7 +20,7 @@
 #include <linux/slab.h>
 #include <linux/miscdevice.h>
 #include <linux/spinlock.h>
-#include <linux/tqueue.h>
+#include <linux/workqueue.h>
 #include <linux/delay.h>
 // #include <linux/trdevice.h>
 
diff -Nru a/drivers/message/fusion/mptscsih.c b/drivers/message/fusion/mptscsih.c
--- a/drivers/message/fusion/mptscsih.c	Mon Oct  7 11:25:32 2002
+++ b/drivers/message/fusion/mptscsih.c	Mon Oct  7 11:25:32 2002
@@ -76,6 +76,7 @@
 #include <linux/delay.h>	/* for mdelay */
 #include <linux/interrupt.h>	/* needed for in_interrupt() proto */
 #include <linux/reboot.h>	/* notifier code */
+#include <linux/workqueue.h>
 #include "../../scsi/scsi.h"
 #include "../../scsi/hosts.h"
 #include "../../scsi/sd.h"
@@ -244,7 +245,7 @@
  */
 static spinlock_t mytaskQ_lock = SPIN_LOCK_UNLOCKED;
 static int mytaskQ_bh_active = 0;
-static struct tq_struct	mptscsih_ptaskfoo;
+static struct work_struct	mptscsih_ptaskfoo;
 static atomic_t	mpt_taskQdepth;
 #endif
 
@@ -255,7 +256,7 @@
 static spinlock_t dvtaskQ_lock = SPIN_LOCK_UNLOCKED;
 static int dvtaskQ_active = 0;
 static int dvtaskQ_release = 0;
-static struct tq_struct	mptscsih_dvTask;
+static struct work_struct	mptscsih_dvTask;
 #endif
 
 /*
@@ -2019,10 +2020,7 @@
 					if (!dvtaskQ_active) {
 						dvtaskQ_active = 1;
 						spin_unlock_irqrestore(&dvtaskQ_lock, lflags);
-						mptscsih_dvTask.sync = 0;
-						mptscsih_dvTask.routine = mptscsih_domainValidation;
-						mptscsih_dvTask.data = (void *) hd;
-
+						INIT_WORK(&mptscsih_dvTask, mptscsih_domainValidation, (void *) hd);
 						SCHEDULE_TASK(&mptscsih_dvTask);
 					} else {
 						spin_unlock_irqrestore(&dvtaskQ_lock, lflags);
@@ -3048,7 +3046,7 @@
 {
 	MPT_SCSI_HOST		*hd;
 	MPT_FRAME_HDR		*mf;
-	struct tq_struct	*ptaskfoo;
+	struct work_struct	*ptaskfoo;
 	unsigned long		 flags;
 	int			 scpnt_idx;
 
@@ -3156,7 +3154,7 @@
 		 *  Oh how cute, no alloc/free/mgmt needed if we use
 		 *  (bottom/unused portion of) MPT request frame.
 		 */
-		ptaskfoo = (struct tq_struct *) &mptscsih_ptaskfoo;
+		ptaskfoo = (struct work_struct *) &mptscsih_ptaskfoo;
 		ptaskfoo->sync = 0;
 		ptaskfoo->routine = mptscsih_taskmgmt_bh;
 		ptaskfoo->data = SCpnt;
@@ -3184,7 +3182,7 @@
 {
 	MPT_SCSI_HOST		*hd;
 	MPT_FRAME_HDR		*mf;
-	struct tq_struct	*ptaskfoo;
+	struct work_struct	*ptaskfoo;
 	unsigned long		 flags;
 	int			 scpnt_idx;
 
@@ -3286,7 +3284,7 @@
 		 *  Oh how cute, no alloc/free/mgmt needed if we use
 		 *  (bottom/unused portion of) MPT request frame.
 		 */
-		ptaskfoo = (struct tq_struct *) &mptscsih_ptaskfoo;
+		ptaskfoo = (struct work_struct *) &mptscsih_ptaskfoo;
 		ptaskfoo->sync = 0;
 		ptaskfoo->routine = mptscsih_taskmgmt_bh;
 		ptaskfoo->data = SCpnt;
diff -Nru a/drivers/message/fusion/mptscsih.h b/drivers/message/fusion/mptscsih.h
--- a/drivers/message/fusion/mptscsih.h	Mon Oct  7 11:25:40 2002
+++ b/drivers/message/fusion/mptscsih.h	Mon Oct  7 11:25:40 2002
@@ -157,12 +157,11 @@
 #ifdef HAVE_TQ_SCHED
 #define SCHEDULE_TASK(x)		\
 	/*MOD_INC_USE_COUNT*/;		\
-	(x)->next = NULL;		\
-	queue_task(x, &tq_scheduler)
+	schedule_work(x)
 #else
 #define SCHEDULE_TASK(x)		\
 	/*MOD_INC_USE_COUNT*/;		\
-	if (schedule_task(x) == 0) {	\
+	if (schedule_work(x) == 0) {	\
 		/*MOD_DEC_USE_COUNT*/;	\
 	}
 #endif
diff -Nru a/drivers/message/i2o/i2o_block.c b/drivers/message/i2o/i2o_block.c
--- a/drivers/message/i2o/i2o_block.c	Mon Oct  7 11:25:36 2002
+++ b/drivers/message/i2o/i2o_block.c	Mon Oct  7 11:25:36 2002
@@ -186,7 +186,7 @@
 
 static struct i2ob_device i2ob_dev[MAX_I2OB<<4];
 static int i2ob_dev_count = 0;
-static struct gendisk i2o_disk[MAX_I2OB];
+static struct gendisk *i2o_disk[MAX_I2OB];
 
 /*
  * Mutex and spin lock for event handling synchronization
@@ -715,7 +715,7 @@
 			 */
 			case I2O_EVT_IND_BSA_VOLUME_LOAD:
 			{
-				struct gendisk *p = &i2o_disk[unit>>4];
+				struct gendisk *p = i2o_disk[unit>>4];
 				i2ob_install_device(i2ob_dev[unit].i2odev->controller, 
 					i2ob_dev[unit].i2odev, unit);
 				add_disk(p);
@@ -730,7 +730,7 @@
 			 */
 			case I2O_EVT_IND_BSA_VOLUME_UNLOAD:
 			{
-				struct gendisk *p = &i2o_disk[unit>>4];
+				struct gendisk *p = i2o_disk[unit>>4];
 				del_gendisk(p);
 				for(i = unit; i <= unit+15; i++)
 					blk_queue_max_sectors(i2ob_dev[i].req_queue, 0);
@@ -762,7 +762,7 @@
 					i2ob_query_device(&i2ob_dev[unit], 0x0000, 4, &size, 8);
 
 				spin_lock_irqsave(I2O_LOCK(unit), flags);	
-				set_capacity(&i2o_disk[unit>>4], size>>9);
+				set_capacity(i2o_disk[unit>>4], size>>9);
 				spin_unlock_irqrestore(I2O_LOCK(unit), flags);	
 				break;
 			}
@@ -1040,7 +1040,7 @@
 
 	if (cmd != HDIO_GETGEO)
 		return -EINVAL;
-	i2o_block_biosparam(get_capacity(&i2o_disk[u]),
+	i2o_block_biosparam(get_capacity(i2o_disk[u]),
 				&g.cylinders, &g.heads, &g.sectors);
 	g.start = get_start_sect(inode->i_bdev);
 	return copy_to_user((void *)arg, &g, sizeof(g)) ? -EFAULT : 0;
@@ -1216,7 +1216,7 @@
 	
 	i2ob_query_device(dev, 0x0000, 5, &flags, 4);
 	i2ob_query_device(dev, 0x0000, 6, &status, 4);
-	set_capacity(&i2o_disk[unit>>4], size>>9);
+	set_capacity(i2o_disk[unit>>4], size>>9);
 
 	/* Set limit based on inbound frame size */
 	limit = (d->controller->status_block->inbound_frame_size - 8)/2;
@@ -1253,7 +1253,7 @@
 	}
 
 
-	strcpy(d->dev_name, i2o_disk[unit>>4].disk_name);
+	strcpy(d->dev_name, i2o_disk[unit>>4]->disk_name);
 
 	printk(KERN_INFO "%s: Max segments %d, queue depth %d, byte limit %d.\n",
 		 d->dev_name, i2ob_dev[unit].max_segments, i2ob_dev[unit].depth, limit);
@@ -1460,7 +1460,7 @@
 					printk(KERN_WARNING "Could not install I2O block device\n");
 				else
 				{
-					add_disk(&i2o_disk[scan_unit>>4]);
+					add_disk(i2o_disk[scan_unit>>4]);
 					scan_unit+=16;
 					i2ob_dev_count++;
 
@@ -1548,7 +1548,7 @@
 		printk(KERN_ERR "i2o_block: Could not install new device\n");
 	else	
 	{
-		add_disk(&i2o_disk[unit>>4]);
+		add_disk(i2o_disk[unit>>4]);
 		i2ob_dev_count++;
 		i2o_device_notify_on(d, &i2o_block_handler);
 	}
@@ -1599,7 +1599,7 @@
 	 * This will force errors when i2ob_get_queue() is called
 	 * by the kenrel.
 	 */
-	del_gendisk(&i2o_disk[unit>>4]);
+	del_gendisk(i2o_disk[unit>>4]);
 	i2ob_dev[unit].req_queue = NULL;
 	for(i = unit; i <= unit+15; i++)
 	{
@@ -1709,11 +1709,7 @@
  *  (Just smiley confuses emacs :-)
  */
 
-#ifdef MODULE
-#define i2o_block_init init_module
-#endif
-
-int i2o_block_init(void)
+static int __init i2o_block_init(void)
 {
 	int i;
 
@@ -1729,6 +1725,12 @@
 		       MAJOR_NR);
 		return -EIO;
 	}
+	for (i = 0; i < MAX_I2OB; i++) {
+		struct gendisk *disk = alloc_disk();
+		if (!disk)
+			goto Enomem;
+		i2o_disk[i] = disk;
+	}
 #ifdef MODULE
 	printk(KERN_INFO "i2o_block: registered device at major %d\n", MAJOR_NR);
 #endif
@@ -1751,7 +1753,7 @@
 	}
 
 	for (i = 0; i < MAX_I2OB; i++) {
-		struct gendisk *disk = i2o_disk + i;
+		struct gendisk *disk = i2o_disk[i];
 		disk->major = MAJOR_NR;
 		disk->first_minor = i<<4;
 		disk->minor_shift = 4;
@@ -1808,15 +1810,14 @@
 	i2ob_probe();
 
 	return 0;
+Enomem:
+	while (i--)
+		put_disk(i2o_disk[i]);
+	unregister_blkdev(MAJOR_NR, "i2o_block");
+	return -ENOMEM;
 }
 
-#ifdef MODULE
-
-MODULE_AUTHOR("Red Hat Software");
-MODULE_DESCRIPTION("I2O Block Device OSM");
-MODULE_LICENSE("GPL");
-
-void cleanup_module(void)
+static void __exit i2o_block_exit(void)
 {
 	int i;
 	
@@ -1862,11 +1863,20 @@
 	 */
 
 	i2o_remove_handler(&i2o_block_handler);
-		 
+
+	for (i = 0; i < MAX_I2OB; i++)
+		put_disk(i2o_disk[i]);
+ 
 	/*
 	 *	Return the block device
 	 */
 	if (unregister_blkdev(MAJOR_NR, "i2o_block") != 0)
 		printk("i2o_block: cleanup_module failed\n");
 }
-#endif
+
+MODULE_AUTHOR("Red Hat Software");
+MODULE_DESCRIPTION("I2O Block Device OSM");
+MODULE_LICENSE("GPL");
+
+module_init(i2o_block_init)
+module_exit(i2o_block_exit)
diff -Nru a/drivers/message/i2o/i2o_lan.c b/drivers/message/i2o/i2o_lan.c
--- a/drivers/message/i2o/i2o_lan.c	Mon Oct  7 11:25:26 2002
+++ b/drivers/message/i2o/i2o_lan.c	Mon Oct  7 11:25:26 2002
@@ -43,7 +43,7 @@
 #include <linux/slab.h>
 #include <linux/init.h>
 #include <linux/spinlock.h>
-#include <linux/tqueue.h>
+#include <linux/workqueue.h>
 #include <asm/io.h>
 
 #include <linux/errno.h>
@@ -116,11 +116,8 @@
 };
 static int lan_context;
 
-DECLARE_TASK_QUEUE(i2o_post_buckets_task);
-struct tq_struct run_i2o_post_buckets_task = {
-	routine: (void (*)(void *)) run_task_queue,
-	data: (void *) 0
-};
+struct DECLARE_WORK(run_i2o_post_buckets_task,
+			(void (*)(void *)) run_task_queue, NULL);
 
 /* Functions to handle message failures and transaction errors:
 ==============================================================*/
@@ -386,8 +383,7 @@
 
 	if (atomic_read(&priv->buckets_out) <= priv->max_buckets_out - priv->bucket_thresh) {
 		run_i2o_post_buckets_task.data = (void *)dev;
-		queue_task(&run_i2o_post_buckets_task, &tq_immediate);
-		mark_bh(IMMEDIATE_BH);
+		schedule_work(&run_i2o_post_buckets_task);
 	}
 
 	return;
@@ -908,7 +904,7 @@
 		if ((priv->tx_batch_mode & 0x01) && !priv->send_active) {
 			priv->send_active = 1;
 			MOD_INC_USE_COUNT;
-			if (schedule_task(&priv->i2o_batch_send_task) == 0)
+			if (schedule_work(&priv->i2o_batch_send_task) == 0)
 				MOD_DEC_USE_COUNT;
 		}
 	} else {  /* Add new SGL element to the previous message frame */
@@ -996,7 +992,7 @@
 		if ((priv->tx_batch_mode & 0x01) && !priv->send_active) {
 			priv->send_active = 1;
 			MOD_INC_USE_COUNT;
-			if (schedule_task(&priv->i2o_batch_send_task) == 0)
+			if (schedule_work(&priv->i2o_batch_send_task) == 0)
 				MOD_DEC_USE_COUNT;
 		}
 	} else {  /* Add new SGL element to the previous message frame */
@@ -1409,8 +1405,8 @@
 
 	INIT_LIST_HEAD(&priv->i2o_batch_send_task.list);
 	priv->i2o_batch_send_task.sync    = 0;
-	priv->i2o_batch_send_task.routine = (void *)i2o_lan_batch_send;
-	priv->i2o_batch_send_task.data    = (void *)dev;
+	INIT_WORK(&priv->i2o_batch_send_task, (void *)i2o_lan_batch_send,
+			(void *)dev);
 
 	dev->open		= i2o_lan_open;
 	dev->stop		= i2o_lan_close;
diff -Nru a/drivers/message/i2o/i2o_lan.h b/drivers/message/i2o/i2o_lan.h
--- a/drivers/message/i2o/i2o_lan.h	Mon Oct  7 11:25:39 2002
+++ b/drivers/message/i2o/i2o_lan.h	Mon Oct  7 11:25:39 2002
@@ -136,7 +136,7 @@
 	u8 sgl_max;			/* max SGLs in one message frame */
 	u32 m;				/* IOP address of the batch msg frame */
 
-	struct tq_struct i2o_batch_send_task;
+	struct work_struct i2o_batch_send_task;
 	int send_active;
 	struct sk_buff **i2o_fbl;	/* Free bucket list (to reuse skbs) */
 	int i2o_fbl_tail;
diff -Nru a/drivers/mtd/ftl.c b/drivers/mtd/ftl.c
--- a/drivers/mtd/ftl.c	Mon Oct  7 11:25:28 2002
+++ b/drivers/mtd/ftl.c	Mon Oct  7 11:25:28 2002
@@ -1223,18 +1223,17 @@
 	}
 
 	partition = kmalloc(sizeof(partition_t), GFP_KERNEL);
-	disk = kmalloc(sizeof(struct gendisk), GFP_KERNEL);
+	disk = alloc_disk();
 		
 	if (!partition||!disk) {
 		printk(KERN_WARNING "No memory to scan for FTL on %s\n",
 			mtd->name);
 		kfree(partition);
-		kfree(disk);
+		put_disk(disk);
 		return;
 	}    
 
 	memset(partition, 0, sizeof(partition_t));
-	memset(disk, 0, sizeof(struct gendisk));
 	sprintf(disk->disk_name, "ftl%c", 'a' + device);
 	disk->major = FTL_MAJOR;
 	disk->first_minor = device << 4;
@@ -1255,7 +1254,7 @@
 #endif
 	} else {
 		kfree(partition);
-		kfree(disk);
+		put_disk(disk);
 	}
 }
 
@@ -1281,7 +1280,7 @@
 			
 			myparts[i]->state = 0;
 			del_gendisk(myparts[i]->disk);
-			kfree(myparts[i]->disk);
+			put_disk(myparts[i]->disk);
 			kfree(myparts[i]);
 			myparts[i] = NULL;
 		}
diff -Nru a/drivers/mtd/maps/Config.help b/drivers/mtd/maps/Config.help
--- a/drivers/mtd/maps/Config.help	Mon Oct  7 11:25:29 2002
+++ b/drivers/mtd/maps/Config.help	Mon Oct  7 11:25:29 2002
@@ -127,6 +127,14 @@
   you probably want to enable this mapping driver. More info is at
   <http://www.itc.hu/>.
 
+CONFIG_MTD_SCx200_DOCFLASH
+  Enable support for a flash chip mapped using the DOCCS signal on a
+  National Semiconductor SCx200 processor.
+
+  If you don't know what to do here, say N.
+
+  If compiled as a module, it will be called scx200_docflash.o.
+
 CONFIG_MTD_OCTAGON
   This provides a 'mapping' driver which supports the way in which
   the flash chips are connected in the Octagon-5066 Single Board
diff -Nru a/drivers/mtd/maps/Config.in b/drivers/mtd/maps/Config.in
--- a/drivers/mtd/maps/Config.in	Mon Oct  7 11:25:35 2002
+++ b/drivers/mtd/maps/Config.in	Mon Oct  7 11:25:35 2002
@@ -26,6 +26,7 @@
    dep_tristate '  JEDEC Flash device mapped on Mixcom piggyback card' CONFIG_MTD_MIXMEM $CONFIG_MTD_JEDEC
    dep_tristate '  JEDEC Flash device mapped on Octagon 5066 SBC' CONFIG_MTD_OCTAGON $CONFIG_MTD_JEDEC
    dep_tristate '  JEDEC Flash device mapped on Tempustech VMAX SBC301' CONFIG_MTD_VMAX $CONFIG_MTD_JEDEC
+   dep_tristate '  Flash device mapped with DOCCS on NatSemi SCx200' CONFIG_MTD_SCx200_DOCFLASH $CONFIG_MTD_CFI
    dep_tristate '  BIOS flash chip on Intel L440GX boards' CONFIG_MTD_L440GX $CONFIG_MTD_JEDEC
 fi
 
diff -Nru a/drivers/mtd/maps/Makefile b/drivers/mtd/maps/Makefile
--- a/drivers/mtd/maps/Makefile	Mon Oct  7 11:25:25 2002
+++ b/drivers/mtd/maps/Makefile	Mon Oct  7 11:25:25 2002
@@ -24,6 +24,7 @@
 obj-$(CONFIG_MTD_NETSC520)	+= netsc520.o
 obj-$(CONFIG_MTD_SUN_UFLASH)    += sun_uflash.o
 obj-$(CONFIG_MTD_VMAX)		+= vmax301.o
+obj-$(CONFIG_MTD_SCx200_DOCFLASH)+= scx200_docflash.o
 obj-$(CONFIG_MTD_DBOX2)		+= dbox2-flash.o
 obj-$(CONFIG_MTD_OCELOT)	+= ocelot.o
 obj-$(CONFIG_MTD_SOLUTIONENGINE)+= solutionengine.o
diff -Nru a/drivers/mtd/maps/scx200_docflash.c b/drivers/mtd/maps/scx200_docflash.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/mtd/maps/scx200_docflash.c	Mon Oct  7 11:25:40 2002
@@ -0,0 +1,268 @@
+/* linux/drivers/mtd/maps/scx200_docflash.c 
+
+   Copyright (c) 2001,2002 Christer Weinigel <wingel@nano-system.com>
+
+   National Semiconductor SCx200 flash mapped with DOCCS
+*/
+
+#include <linux/module.h>
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <asm/io.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/map.h>
+#include <linux/mtd/partitions.h>
+
+#include <linux/pci.h>
+#include <linux/scx200.h>
+
+#define NAME "scx200_docflash"
+
+MODULE_AUTHOR("Christer Weinigel <wingel@hack.org>");
+MODULE_DESCRIPTION("NatSemi SCx200 DOCCS Flash Driver");
+MODULE_LICENSE("GPL");
+
+/* Set this to one if you want to partition the flash */
+#define PARTITION 1
+
+MODULE_PARM(probe, "i");
+MODULE_PARM_DESC(probe, "Probe for a BIOS mapping");
+MODULE_PARM(size, "i");
+MODULE_PARM_DESC(size, "Size of the flash mapping");
+MODULE_PARM(width, "i");
+MODULE_PARM_DESC(width, "Data width of the flash mapping (8/16)");
+MODULE_PARM(flashtype, "s");
+MODULE_PARM_DESC(flashtype, "Type of MTD probe to do");
+
+static int probe = 0;		/* Don't autoprobe */
+static unsigned size = 0x1000000; /* 16 MB the whole ISA address space */
+static unsigned width = 8;	/* Default to 8 bits wide */
+static char *flashtype = "cfi_probe";
+
+static struct resource docmem = {
+	.flags = IORESOURCE_MEM,
+	.name  = "NatSemi SCx200 DOCCS Flash",
+};
+
+static struct mtd_info *mymtd;
+
+#if PARTITION
+static struct mtd_partition partition_info[] = {
+	{ 
+		.name   = "DOCCS Boot kernel", 
+		.offset = 0, 
+		.size   = 0xc0000
+	},
+	{ 
+		.name   = "DOCCS Low BIOS", 
+		.offset = 0xc0000, 
+		.size   = 0x40000
+	},
+	{ 
+		.name   = "DOCCS File system", 
+		.offset = 0x100000, 
+		.size   = ~0	/* calculate from flash size */
+	},
+	{ 
+		.name   = "DOCCS High BIOS", 
+		.offset = ~0, 	/* calculate from flash size */
+		.size   = 0x80000
+	},
+};
+#define NUM_PARTITIONS (sizeof(partition_info)/sizeof(partition_info[0]))
+#endif
+
+static __u8 scx200_docflash_read8(struct map_info *map, unsigned long ofs)
+{
+	return __raw_readb(map->map_priv_1 + ofs);
+}
+
+static __u16 scx200_docflash_read16(struct map_info *map, unsigned long ofs)
+{
+	return __raw_readw(map->map_priv_1 + ofs);
+}
+
+static void scx200_docflash_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
+{
+	memcpy_fromio(to, map->map_priv_1 + from, len);
+}
+
+static void scx200_docflash_write8(struct map_info *map, __u8 d, unsigned long adr)
+{
+	__raw_writeb(d, map->map_priv_1 + adr);
+	mb();
+}
+
+static void scx200_docflash_write16(struct map_info *map, __u16 d, unsigned long adr)
+{
+	__raw_writew(d, map->map_priv_1 + adr);
+	mb();
+}
+
+static void scx200_docflash_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
+{
+	memcpy_toio(map->map_priv_1 + to, from, len);
+}
+
+static struct map_info scx200_docflash_map = {
+	.name      = "NatSemi SCx200 DOCCS Flash",
+	.read8     = scx200_docflash_read8,
+	.read16    = scx200_docflash_read16,
+	.copy_from = scx200_docflash_copy_from,
+	.write8    = scx200_docflash_write8,
+	.write16   = scx200_docflash_write16,
+	.copy_to   = scx200_docflash_copy_to
+};
+
+int __init init_scx200_docflash(void)
+{
+	unsigned u;
+	unsigned base;
+	unsigned ctrl;
+	unsigned pmr;
+	struct pci_dev *bridge;
+
+	printk(KERN_DEBUG NAME ": NatSemi SCx200 DOCCS Flash Driver\n");
+
+	if ((bridge = pci_find_device(PCI_VENDOR_ID_NS, 
+				      PCI_DEVICE_ID_NS_SCx200_BRIDGE,
+				      NULL)) == NULL)
+		return -ENODEV;
+	
+	if (!scx200_cb_probe(SCx200_CB_BASE)) {
+		printk(KERN_WARNING NAME ": no configuration block found\n");
+		return -ENODEV;
+	}
+
+	if (probe) {
+		/* Try to use the present flash mapping if any */
+		pci_read_config_dword(bridge, SCx200_DOCCS_BASE, &base);
+		pci_read_config_dword(bridge, SCx200_DOCCS_CTRL, &ctrl);
+		pmr = inl(SCx200_CB_BASE + SCx200_PMR);
+
+		if (base == 0
+		    || (ctrl & 0x07000000) != 0x07000000
+		    || (ctrl & 0x0007ffff) == 0)
+			return -ENODEV;
+
+		size = ((ctrl&0x1fff)<<13) + (1<<13);
+
+		for (u = size; u > 1; u >>= 1)
+			;
+		if (u != 1)
+			return -ENODEV;
+
+		if (pmr & (1<<6))
+			width = 16;
+		else
+			width = 8;
+
+		docmem.start = base;
+		docmem.end = base + size;
+
+		if (request_resource(&iomem_resource, &docmem)) {
+			printk(KERN_ERR NAME ": unable to allocate memory for flash mapping\n");
+			return -ENOMEM;
+		}
+	} else {
+		for (u = size; u > 1; u >>= 1)
+			;
+		if (u != 1) {
+			printk(KERN_ERR NAME ": invalid size for flash mapping\n");
+			return -EINVAL;
+		}
+		
+		if (width != 8 && width != 16) {
+			printk(KERN_ERR NAME ": invalid bus width for flash mapping\n");
+			return -EINVAL;
+		}
+		
+		if (allocate_resource(&iomem_resource, &docmem, 
+				      size,
+				      0xc0000000, 0xffffffff, 
+				      size, NULL, NULL)) {
+			printk(KERN_ERR NAME ": unable to allocate memory for flash mapping\n");
+			return -ENOMEM;
+		}
+		
+		ctrl = 0x07000000 | ((size-1) >> 13);
+
+		printk(KERN_INFO "DOCCS BASE=0x%08lx, CTRL=0x%08lx\n", (long)docmem.start, (long)ctrl);
+		
+		pci_write_config_dword(bridge, SCx200_DOCCS_BASE, docmem.start);
+		pci_write_config_dword(bridge, SCx200_DOCCS_CTRL, ctrl);
+		pmr = inl(SCx200_CB_BASE + SCx200_PMR);
+		
+		if (width == 8) {
+			pmr &= ~(1<<6);
+		} else {
+			pmr |= (1<<6);
+		}
+		outl(pmr, SCx200_CB_BASE + SCx200_PMR);
+	}
+	
+       	printk(KERN_INFO NAME ": DOCCS mapped at 0x%lx-0x%lx, width %d\n", 
+	       docmem.start, docmem.end, width);
+
+	scx200_docflash_map.size = size;
+	if (width == 8)
+		scx200_docflash_map.buswidth = 1;
+	else
+		scx200_docflash_map.buswidth = 2;
+
+	scx200_docflash_map.map_priv_1 = (unsigned long)ioremap(docmem.start, scx200_docflash_map.size);
+	if (!scx200_docflash_map.map_priv_1) {
+		printk(KERN_ERR NAME ": failed to ioremap the flash\n");
+		release_resource(&docmem);
+		return -EIO;
+	}
+
+	mymtd = do_map_probe(flashtype, &scx200_docflash_map);
+	if (!mymtd) {
+		printk(KERN_ERR NAME ": unable to detect flash\n");
+		iounmap((void *)scx200_docflash_map.map_priv_1);
+		release_resource(&docmem);
+		return -ENXIO;
+	}
+
+	if (size < mymtd->size)
+		printk(KERN_WARNING NAME ": warning, flash mapping is smaller than flash size\n");
+
+	mymtd->module = THIS_MODULE;
+
+#if PARTITION
+	partition_info[3].offset = mymtd->size-partition_info[3].size;
+	partition_info[2].size = partition_info[3].offset-partition_info[2].offset;
+	add_mtd_partitions(mymtd, partition_info, NUM_PARTITIONS);
+#else
+	add_mtd_device(mymtd);
+#endif
+	return 0;
+}
+
+static void __exit cleanup_scx200_docflash(void)
+{
+	if (mymtd) {
+#if PARTITION
+		del_mtd_partitions(mymtd);
+#else
+		del_mtd_device(mymtd);
+#endif
+		map_destroy(mymtd);
+	}
+	if (scx200_docflash_map.map_priv_1) {
+		iounmap((void *)scx200_docflash_map.map_priv_1);
+		release_resource(&docmem);
+	}
+}
+
+module_init(init_scx200_docflash);
+module_exit(cleanup_scx200_docflash);
+
+/*
+    Local variables:
+        compile-command: "make -k -C ../../.. SUBDIRS=drivers/mtd/maps modules"
+        c-basic-offset: 8
+    End:
+*/
diff -Nru a/drivers/mtd/mtdblock.c b/drivers/mtd/mtdblock.c
--- a/drivers/mtd/mtdblock.c	Mon Oct  7 11:25:32 2002
+++ b/drivers/mtd/mtdblock.c	Mon Oct  7 11:25:32 2002
@@ -42,7 +42,7 @@
 	unsigned long cache_offset;
 	unsigned int cache_size;
 	enum { STATE_EMPTY, STATE_CLEAN, STATE_DIRTY } cache_state;
-	struct gendisk disk;
+	struct gendisk *disk;
 } *mtdblks[MAX_MTD_DEVICES];
 
 static spinlock_t mtdblks_lock;
@@ -263,6 +263,7 @@
 	struct mtdblk_dev *mtdblk;
 	struct mtd_info *mtd;
 	int dev = minor(inode->i_rdev);
+	struct gendisk *disk;
 
 	DEBUG(MTD_DEBUG_LEVEL1,"mtdblock_open\n");
 
@@ -294,10 +295,9 @@
 	spin_unlock(&mtdblks_lock);
 
 	mtdblk = kmalloc(sizeof(struct mtdblk_dev), GFP_KERNEL);
-	if (!mtdblk) {
-		put_mtd_device(mtd);
-		return -ENOMEM;
-	}
+	disk = alloc_disk();
+	if (!mtdblk || !disk)
+		goto Enomem;
 	memset(mtdblk, 0, sizeof(*mtdblk));
 	mtdblk->count = 1;
 	mtdblk->mtd = mtd;
@@ -308,17 +308,15 @@
 	    mtdblk->mtd->erasesize) {
 		mtdblk->cache_size = mtdblk->mtd->erasesize;
 		mtdblk->cache_data = vmalloc(mtdblk->mtd->erasesize);
-		if (!mtdblk->cache_data) {
-			put_mtd_device(mtdblk->mtd);
-			kfree(mtdblk);
-			return -ENOMEM;
-		}
+		if (!mtdblk->cache_data)
+			goto Enomem;
 	}
-	mtdblk->disk.major = MAJOR_NR;
-	mtdblk->disk.first_minor = dev;
-	mtdblk->disk.minor_shift = 0;
-	mtdblk->disk.fops = &mtd_fops;
-	sprintf(mtdblk->disk.disk_name, "mtd%d", dev);
+	disk->major = MAJOR_NR;
+	disk->first_minor = dev;
+	disk->minor_shift = 0;
+	disk->fops = &mtd_fops;
+	sprintf(disk->disk_name, "mtd%d", dev);
+	mtdblk->disk = disk;
 
 	/* OK, we've created a new one. Add it to the list. */
 
@@ -331,19 +329,25 @@
 		put_mtd_device(mtdblk->mtd);
 		vfree(mtdblk->cache_data);
 		kfree(mtdblk);
+		put_disk(disk);
 		return 0;
 	}
 
 	mtdblks[dev] = mtdblk;
-	set_capacity(&mtdblk->disk, mtdblk->mtd->size/512);
-	add_disk(&mtdblk->disk);
+	set_capacity(disk, mtdblk->mtd->size/512);
+	add_disk(disk);
 	set_device_ro (inode->i_rdev, !(mtdblk->mtd->flags & MTD_WRITEABLE));
-	
+
 	spin_unlock(&mtdblks_lock);
-	
+
 	DEBUG(MTD_DEBUG_LEVEL1, "ok\n");
 
 	return 0;
+Enomem:
+	put_mtd_device(mtd);
+	put_disk(disk);
+	kfree(mtdblk);
+	return -ENOMEM;
 }
 
 static release_t mtdblock_release(struct inode *inode, struct file *file)
@@ -367,7 +371,8 @@
 		/* It was the last usage. Free the device */
 		mtdblks[dev] = NULL;
 		spin_unlock(&mtdblks_lock);
-		del_gendisk(&mtdblk->disk);
+		del_gendisk(mtdblk->disk);
+		put_disk(mtdblk->disk);
 		if (mtdblk->mtd->sync)
 			mtdblk->mtd->sync(mtdblk->mtd);
 		put_mtd_device(mtdblk->mtd);
diff -Nru a/drivers/mtd/mtdblock_ro.c b/drivers/mtd/mtdblock_ro.c
--- a/drivers/mtd/mtdblock_ro.c	Mon Oct  7 11:25:35 2002
+++ b/drivers/mtd/mtdblock_ro.c	Mon Oct  7 11:25:35 2002
@@ -29,13 +29,13 @@
 MODULE_PARM(debug, "i");
 #endif
 
-static struct gendisk mtd_disks[MAX_MTD_DEVICES];
+static struct gendisk *mtd_disks[MAX_MTD_DEVICES];
 
 static int mtdblock_open(struct inode *inode, struct file *file)
 {
 	struct mtd_info *mtd = NULL;
 	int dev = minor(inode->i_rdev);
-	struct gendisk *disk = mtd_disks + dev;
+	struct gendisk *disk = mtd_disks[dev];
 
 	DEBUG(1,"mtdblock_open\n");
 
@@ -73,7 +73,7 @@
 		release_return(-ENODEV);
 	}
 
-	del_gendisk(mtd_disks + dev);
+	del_gendisk(mtd_disks[dev]);
 	
 	if (mtd->sync)
 		mtd->sync(mtd);
@@ -218,30 +218,42 @@
 
 int __init init_mtdblock(void)
 {
+	int err = -ENOMEM;
 	int i;
 
-	if (register_blkdev(MAJOR_NR,DEVICE_NAME,&mtd_fops)) {
-		printk(KERN_NOTICE "Can't allocate major number %d for Memory Technology Devices.\n",
-		       MTD_BLOCK_MAJOR);
-		return -EAGAIN;
-	}
-
-	blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), &mtdblock_request);
-
 	for (i = 0; i < MAX_MTD_DEVICES; i++) {
-		struct gendisk *disk = mtd_disks + i;
+		struct gendisk *disk = alloc_disk();
+		if (!disk)
+			goto out;
 		disk->major = MAJOR_NR;
 		disk->first_minor = i;
 		sprintf(disk->disk_name, "mtdblock%d", i);
 		disk->fops = &mtd_fops;
+		mtd_disks[i] = disk;
 	}
+
+	if (register_blkdev(MAJOR_NR,DEVICE_NAME,&mtd_fops)) {
+		printk(KERN_NOTICE "Can't allocate major number %d for Memory Technology Devices.\n",
+		       MTD_BLOCK_MAJOR);
+		err = -EAGAIN;
+		goto out;
+	}
+
+	blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), &mtdblock_request);
 	return 0;
+out:
+	while (i--)
+		put_disk(mtd_disks[i]);
+	return err;
 }
 
 static void __exit cleanup_mtdblock(void)
 {
+	int i;
 	unregister_blkdev(MAJOR_NR,DEVICE_NAME);
 	blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR));
+	for (i = 0; i < MAX_MTD_DEVICES; i++)
+		put_disk(mtd_disks[i]);
 }
 
 module_init(init_mtdblock);
diff -Nru a/drivers/mtd/nftlcore.c b/drivers/mtd/nftlcore.c
--- a/drivers/mtd/nftlcore.c	Mon Oct  7 11:25:25 2002
+++ b/drivers/mtd/nftlcore.c	Mon Oct  7 11:25:25 2002
@@ -74,10 +74,10 @@
         }
 
 	nftl = kmalloc(sizeof(struct NFTLrecord), GFP_KERNEL);
-	gd = kmalloc(sizeof(struct gendisk), GFP_KERNEL);
+	gd = alloc_disk();
 	if (!nftl || !gd) {
 		kfree(nftl);
-		kfree(gd);
+		put_disk(gd);
 		printk(KERN_WARNING "Out of memory for NFTL data structures\n");
 		return;
 	}
@@ -92,7 +92,7 @@
         if (NFTL_mount(nftl) < 0) {
 		printk(KERN_WARNING "Could not mount NFTL device\n");
 		kfree(nftl);
-		kfree(gd);
+		put_disk(gd);
 		return;
         }
 
@@ -129,7 +129,6 @@
 		/* Oh no we don't have nftl->nr_sects = nftl->heads * nftl->cylinders * nftl->sectors; */
 	}
 	NFTLs[firstfree] = nftl;
-	memset(gd, 0, sizeof(struct gendisk));
 	sprintf(gd->disk_name, "nftl%c", 'a' + firstfree);
 	gd->major = MAJOR_NR;
 	gd->first_minor = firstfree << NFTL_PARTN_BITS;
@@ -152,7 +151,7 @@
 	if (nftl->EUNtable)
 		kfree(nftl->EUNtable);
 	del_gendisk(nftl->disk);
-	kfree(nftl->disk);
+	put_disk(nftl->disk);
 	kfree(nftl);
 }
 
diff -Nru a/drivers/net/Config.in b/drivers/net/Config.in
--- a/drivers/net/Config.in	Mon Oct  7 11:25:25 2002
+++ b/drivers/net/Config.in	Mon Oct  7 11:25:25 2002
@@ -108,6 +108,9 @@
       dep_tristate '    NI5210 support' CONFIG_NI52 $CONFIG_ISA
       dep_tristate '    NI6510 support' CONFIG_NI65 $CONFIG_ISA
    fi
+   if [ "$CONFIG_PCI" = "y" -o "$CONFIG_EISA" = "y" -o "$CONFIG_CARDBUS" != "n" ]; then
+      source drivers/net/tulip/Config.in
+   fi
    if [ "$CONFIG_ISA" = "y" -o "$CONFIG_MCA" = "y" ]; then
          dep_tristate '  AT1700/1720 support (EXPERIMENTAL)' CONFIG_AT1700 $CONFIG_EXPERIMENTAL
    fi
@@ -320,9 +323,6 @@
 
 source drivers/net/wan/Config.in
 
-if [ "$CONFIG_PCI" = "y" -o "$CONFIG_EISA" = "y" -o "$CONFIG_CARDBUS" != "n" ]; then
-   source drivers/net/tulip/Config.in
-fi
 if [ "$CONFIG_HOTPLUG" = "y" -a "$CONFIG_PCMCIA" != "n" ]; then
    source drivers/net/pcmcia/Config.in
 fi
diff -Nru a/drivers/net/aironet4500.h b/drivers/net/aironet4500.h
--- a/drivers/net/aironet4500.h	Mon Oct  7 11:25:25 2002
+++ b/drivers/net/aironet4500.h	Mon Oct  7 11:25:25 2002
@@ -1478,8 +1478,8 @@
 	u8			link_status_changed;
 	
 	volatile int		ejected;
-	volatile int		bh_running;
-	volatile int		bh_active;
+	volatile int		work_running;
+	volatile int		work_active;
 	volatile long		tx_chain_active;
 	volatile u16		enabled_interrupts;
 	volatile u16		waiting_interrupts;
@@ -1499,7 +1499,7 @@
         struct awc_command	cmd;
         long long		async_command_start;
         volatile int		command_semaphore_on;
-        struct tq_struct 	immediate_bh;
+        struct work_struct			work;
 	volatile int		process_tx_results;
 
 	u8			p2p[6];
@@ -1559,7 +1559,7 @@
 extern int	awc_transmit_packet(struct net_device * dev, struct awc_fid * tx_buff) ;
 extern int	awc_tx_complete_check(struct net_device * dev);
 extern int	awc_interrupt_process(struct net_device * dev);
-extern void 	awc_bh(struct net_device *dev);
+extern void 	awc_work(struct net_device *dev);
 extern int 	awc_802_11_find_copy_path(struct net_device * dev, struct awc_fid * rx_buff);
 extern void 	awc_802_11_router_rx(struct net_device * dev,struct awc_fid * rx_buff);
 extern int 	awc_802_11_tx_find_path_and_post(struct net_device * dev, struct sk_buff * skb);
diff -Nru a/drivers/net/aironet4500_card.c b/drivers/net/aironet4500_card.c
--- a/drivers/net/aironet4500_card.c	Mon Oct  7 11:25:36 2002
+++ b/drivers/net/aironet4500_card.c	Mon Oct  7 11:25:36 2002
@@ -70,9 +70,6 @@
 MODULE_LICENSE("GPL");
 
 
-static int reverse_probe;
-
-
 static int awc_pci_init(struct net_device * dev, struct pci_dev *pdev,
  			int ioaddr, int cis_addr, int mem_addr,u8 pci_irq_line) ;
 
@@ -80,38 +77,29 @@
 int awc4500_pci_probe(struct net_device *dev)
 {
 	int cards_found = 0;
-	static int pci_index;	/* Static, for multiple probe calls. */
 	u8 pci_irq_line = 0;
 //	int p;
-
-	unsigned char awc_pci_dev, awc_pci_bus;
-
+	struct pci_dev *pdev = NULL;
+		
 	if (!pci_present()) 
 		return -1;
 
-	for (;pci_index < 0xff; pci_index++) {
-		u16 vendor, device, pci_command, new_command;
+	while ((pdev = pci_find_class (PCI_CLASS_NETWORK_OTHER << 8, pdev))) {
+		u16 pci_command, new_command;
 		u32 pci_memaddr;
 		u32 pci_ioaddr;
 		u32 pci_cisaddr;
-		struct pci_dev *pdev;
 
-		if (pcibios_find_class	(PCI_CLASS_NETWORK_OTHER << 8,
-			 reverse_probe ? 0xfe - pci_index : pci_index,
-				 &awc_pci_bus, &awc_pci_dev) != PCIBIOS_SUCCESSFUL){
-				if (reverse_probe){
-					continue;
-				} else {
-					break;
-				}
-		}
-		pdev = pci_find_slot(awc_pci_bus, awc_pci_dev);
-		if (!pdev)
+		if (pdev->vendor != PCI_VENDOR_ID_AIRONET)
+			continue;
+		if ((pdev->device != PCI_DEVICE_AIRONET_4800_1) &&
+		    (pdev->device != PCI_DEVICE_AIRONET_4800) &&
+		    (pdev->device != PCI_DEVICE_AIRONET_4500))
 			continue;
+
 		if (pci_enable_device(pdev))
 			continue;
-		vendor = pdev->vendor;
-		device = pdev->device;
+
 	        pci_irq_line = pdev->irq;
 		pci_memaddr = pci_resource_start (pdev, 0);
                 pci_cisaddr = pci_resource_start (pdev, 1);
@@ -120,13 +108,6 @@
 //		printk("\n pci capabilities %x and ptr %x \n",pci_caps,pci_caps_ptr);
 		/* Remove I/O space marker in bit 0. */
 
-		if (vendor != PCI_VENDOR_ID_AIRONET)
-			continue;
-		if (device != PCI_DEVICE_AIRONET_4800_1 && 
-				device != PCI_DEVICE_AIRONET_4800 &&
-				device != PCI_DEVICE_AIRONET_4500 )
-                        continue;
-
 //		if (check_region(pci_ioaddr, AIRONET4X00_IO_SIZE) ||
 //			check_region(pci_cisaddr, AIRONET4X00_CIS_SIZE) ||
 //			check_region(pci_memaddr, AIRONET4X00_MEM_SIZE)) {
@@ -151,7 +132,7 @@
 
 		udelay(1000);
 */
-		if (device == PCI_DEVICE_AIRONET_4800)
+		if (pdev->device == PCI_DEVICE_AIRONET_4800)
 			pci_write_config_dword(pdev, 0x40, 0x40000000);
 
 		if (awc_pci_init(dev, pdev, pci_ioaddr,pci_cisaddr,pci_memaddr,pci_irq_line)){
diff -Nru a/drivers/net/aironet4500_core.c b/drivers/net/aironet4500_core.c
--- a/drivers/net/aironet4500_core.c	Mon Oct  7 11:25:25 2002
+++ b/drivers/net/aironet4500_core.c	Mon Oct  7 11:25:25 2002
@@ -22,7 +22,7 @@
 #include <linux/init.h>
 #include <linux/config.h>
 #include <linux/kernel.h>
-#include <linux/tqueue.h>
+#include <linux/workqueue.h>
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
 #include <linux/skbuff.h>
@@ -2202,10 +2202,9 @@
 
 
 #define AWC_QUEUE_BH {\
-	if (!priv->bh_active && !priv->bh_running){\
-		priv->bh_active = 1;\
-		queue_task(&priv->immediate_bh, &tq_immediate);\
-		mark_bh(IMMEDIATE_BH);\
+	if (!priv->work_active && !priv->work_running){\
+		priv->work_active = 1;\
+		schedule_work(&priv->work); \
 	}\
 	}
 
@@ -2223,7 +2222,7 @@
 
 	DEBUG(8, "awc_bh awoken on jiffie %ld \n",jiffies);
 
-	priv->bh_running = 1;
+	priv->work_running = 1;
 	
 	active_interrupts = awc_event_status(dev->base_addr);
 	
@@ -2233,7 +2232,7 @@
 
         if (test_and_set_bit( 0, (void *) &priv->tx_chain_active) ) {
 //		printk(KERN_ERR "tx chain active in bh \n");
-//		queue_task(&priv->immediate_bh, &tq_immediate);
+//		schedule_work(&priv->work);
 		goto bad_end;
 	}
 start:
@@ -2281,8 +2280,8 @@
 				goto start;
 			}
 		};
-		priv->bh_active  = 0;
-		priv->bh_running = 0;
+		priv->work_active  = 0;
+		priv->work_running = 0;
 
         priv->tx_chain_active = 0;
 
@@ -2292,8 +2291,8 @@
 //	if (!priv->tx_chain_active) 
 //		wake_up(&priv->tx_chain_wait_queue);
   
-  	priv->bh_running = 0;
-	priv->bh_active = 0;
+  	priv->work_running = 0;
+	priv->work_active = 0;
 	return ;
 };
 
@@ -2366,7 +2365,7 @@
 		//priv->
 		netif_device_detach (dev);
 		priv->ejected = 1;
-		if (priv->bh_active || priv->bh_running){
+		if (priv->work_active || priv->work_running){
 			priv->interrupt_count--;
 			goto bad_end;
 		} else if (priv->command_semaphore_on){
@@ -2498,8 +2497,8 @@
 	active_interrupts = awc_event_status(dev->base_addr);
 
 	if ((active_interrupts & 0x7) && 
-	     !priv->bh_active && 
-	     !priv->bh_running ){
+	     !priv->work_active && 
+	     !priv->work_running ){
 		if (multi_ints++ < 5)
 			goto start;
         }
@@ -2874,12 +2873,9 @@
 	
 	priv->command_semaphore_on = 0;
 	priv->unlock_command_postponed = 0;
-	INIT_LIST_HEAD(&priv->immediate_bh.list);
-	priv->immediate_bh.sync 	= 0;
-	priv->immediate_bh.routine 	= (void *)(void *)awc_bh;
-	priv->immediate_bh.data 	= dev;
-	priv->bh_running	= 0;
-	priv->bh_active		= 0;
+	INIT_WORK(&priv->work, (void *)(void *)awc_work, dev);
+	priv->work_running	= 0;
+	priv->work_active	= 0;
 	priv->tx_chain_active	= 0;
 	priv->enabled_interrupts= 0x00;
 	priv->waiting_interrupts= 0x00;
diff -Nru a/drivers/net/atp.c b/drivers/net/atp.c
--- a/drivers/net/atp.c	Mon Oct  7 11:25:35 2002
+++ b/drivers/net/atp.c	Mon Oct  7 11:25:35 2002
@@ -27,11 +27,12 @@
 
 
 	Modular support/softnet added by Alan Cox.
+	_bit abuse fixed up by Alan Cox
 
 */
 
 static const char versionA[] =
-"atp.c:v1.09 8/9/2000 Donald Becker <becker@scyld.com>\n";
+"atp.c:v1.09=ac 2002/10/01 Donald Becker <becker@scyld.com>\n";
 static const char versionB[] =
 "  http://www.scyld.com/network/atp.html\n";
 
@@ -164,8 +165,6 @@
 MODULE_PARM_DESC(irq, "ATP IRQ number(s)");
 MODULE_PARM_DESC(xcvr, "ATP tranceiver(s) (0=internal, 1=external)");
 
-#define RUN_AT(x) (jiffies + (x))
-
 /* The number of low I/O ports used by the ethercard. */
 #define ETHERCARD_TOTAL_SIZE	3
 
@@ -223,6 +222,8 @@
    If dev->base_addr == 1, always return failure.
    If dev->base_addr == 2, allocate space for the device and return success
    (detachable devices only).
+   
+   FIXME: we should use the parport layer for this
    */
 static int __init atp_init(struct net_device *dev)
 {
@@ -402,13 +403,13 @@
  * DO :	 _________X_______X
  */
 
-static unsigned short __init eeprom_op(long ioaddr, unsigned int cmd)
+static unsigned short __init eeprom_op(long ioaddr, u32 cmd)
 {
 	unsigned eedata_out = 0;
 	int num_bits = EE_CMD_SIZE;
 
 	while (--num_bits >= 0) {
-		char outval = test_bit(num_bits, &cmd) ? EE_DATA_WRITE : 0;
+		char outval = (cmd & (1<<num_bits)) ? EE_DATA_WRITE : 0;
 		write_reg_high(ioaddr, PROM_CMD, outval | EE_CLK_LOW);
 		write_reg_high(ioaddr, PROM_CMD, outval | EE_CLK_HIGH);
 		eedata_out <<= 1;
@@ -445,7 +446,7 @@
 	hardware_init(dev);
 
 	init_timer(&lp->timer);
-	lp->timer.expires = RUN_AT(TIMED_CHECKER);
+	lp->timer.expires = jiffies + TIMED_CHECKER;
 	lp->timer.data = (unsigned long)dev;
 	lp->timer.function = &atp_timed_checker;    /* timer handler */
 	add_timer(&lp->timer);
@@ -687,7 +688,7 @@
 		for (i = 0; i < 6; i++)
 			write_reg_byte(ioaddr, PAR0 + i, dev->dev_addr[i]);
 #if 0 && defined(TIMED_CHECKER)
-		mod_timer(&lp->timer, RUN_AT(TIMED_CHECKER));
+		mod_timer(&lp->timer, jiffies + TIMED_CHECKER);
 #endif
 	}
 
@@ -740,7 +741,7 @@
 #endif
 	}
 	spin_unlock(&lp->lock);
-	lp->timer.expires = RUN_AT(TIMED_CHECKER);
+	lp->timer.expires = jiffies + TIMED_CHECKER;
 	add_timer(&lp->timer);
 }
 #endif
@@ -893,8 +894,10 @@
 		memset(mc_filter, 0, sizeof(mc_filter));
 		for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count;
 			 i++, mclist = mclist->next)
-			set_bit(ether_crc_le(ETH_ALEN, mclist->dmi_addr) & 0x3f,
-					mc_filter);
+		{
+			int filterbit = ether_crc_le(ETH_ALEN, mclist->dmi_addr) & 0x3f;
+			mc_filter[filterbit >> 5] |= cpu_to_le32(1 << (filterbit & 31));
+		}
 		new_mode = CMR2h_Normal;
 	}
 	lp->addr_mode = new_mode;
diff -Nru a/drivers/net/cs89x0.c b/drivers/net/cs89x0.c
--- a/drivers/net/cs89x0.c	Mon Oct  7 11:25:37 2002
+++ b/drivers/net/cs89x0.c	Mon Oct  7 11:25:37 2002
@@ -1649,11 +1649,7 @@
 
 #ifdef MODULE
 
-static struct net_device dev_cs89x0 = {
-        "",
-        0, 0, 0, 0,
-        0, 0,
-        0, 0, 0, NULL, NULL };
+static struct net_device dev_cs89x0;
 
 /*
  * Support the 'debug' module parm even if we're compiled for non-debug to 
diff -Nru a/drivers/net/e1000/e1000.h b/drivers/net/e1000/e1000.h
--- a/drivers/net/e1000/e1000.h	Mon Oct  7 11:25:25 2002
+++ b/drivers/net/e1000/e1000.h	Mon Oct  7 11:25:25 2002
@@ -64,7 +64,7 @@
 #include <linux/list.h>
 #include <linux/reboot.h>
 #include <net/checksum.h>
-#include <linux/tqueue.h>
+#include <linux/workqueue.h>
 #include <linux/ethtool.h>
 #include <linux/if_vlan.h>
 
@@ -160,7 +160,7 @@
 	uint16_t link_duplex;
 	spinlock_t stats_lock;
 	atomic_t irq_sem;
-	struct tq_struct tx_timeout_task;
+	struct work_struct tx_timeout_task;
 
 	struct timer_list blink_timer;
 	unsigned long led_status;
diff -Nru a/drivers/net/e1000/e1000_main.c b/drivers/net/e1000/e1000_main.c
--- a/drivers/net/e1000/e1000_main.c	Mon Oct  7 11:25:32 2002
+++ b/drivers/net/e1000/e1000_main.c	Mon Oct  7 11:25:32 2002
@@ -470,7 +470,7 @@
 	adapter->phy_info_timer.function = &e1000_update_phy_info;
 	adapter->phy_info_timer.data = (unsigned long) adapter;
 
-	INIT_TQUEUE(&adapter->tx_timeout_task, 
+	INIT_WORK(&adapter->tx_timeout_task, 
 		(void (*)(void *))e1000_tx_timeout_task, netdev);
 
 	register_netdev(netdev);
@@ -1542,7 +1542,7 @@
 	struct e1000_adapter *adapter = netdev->priv;
 
 	/* Do the reset outside of interrupt context */
-	schedule_task(&adapter->tx_timeout_task);
+	schedule_work(&adapter->tx_timeout_task);
 }
 
 static void
diff -Nru a/drivers/net/eql.c b/drivers/net/eql.c
--- a/drivers/net/eql.c	Mon Oct  7 11:25:29 2002
+++ b/drivers/net/eql.c	Mon Oct  7 11:25:29 2002
@@ -4,6 +4,7 @@
  * (c) Copyright 1995 Simon "Guru Aleph-Null" Janes
  * NCM: Network and Communications Management, Inc.
  *
+ * (c) Copyright 2002 David S. Miller (davem@redhat.com)
  *
  *	This software may be used and distributed according to the terms
  *	of the GNU General Public License, incorporated herein by reference.
@@ -107,6 +108,7 @@
  * 	the locking mechanism and timer stuff must be written however,
  * 	this version will not work otherwise
  *
+ * Sorry, I had to rewrite most of this for 2.5.x -DaveM
  */
 
 #include <linux/module.h>
@@ -121,112 +123,69 @@
 
 #include <asm/uaccess.h>
 
-static char version[] __initdata = 
-	"Equalizer1996: $Revision: 1.2.1 $ $Date: 1996/09/22 13:52:00 $ Simon Janes (simon@ncm.com)\n";
-
-#ifndef EQL_DEBUG
-/* #undef EQL_DEBUG      -* print nothing at all, not even a boot-banner */
-/* #define EQL_DEBUG 1   -* print only the boot-banner */
-/* #define EQL_DEBUG 5   -* print major function entries */
-/* #define EQL_DEBUG 20  -* print subfunction entries */
-/* #define EQL_DEBUG 50  -* print utility entries */
-/* #define EQL_DEBUG 100 -* print voluminous function entries */
-#define EQL_DEBUG 1
-#endif
-static unsigned int eql_debug = EQL_DEBUG;
-
-static int eql_init(struct net_device *dev); /*  */
-static int eql_open(struct net_device *dev); /*  */
-static int eql_close(struct net_device *dev); /*  */
-static int eql_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd); /*  */
-static int eql_slave_xmit(struct sk_buff *skb, struct net_device *dev); /*  */
-
-static struct net_device_stats *eql_get_stats(struct net_device *dev); /*  */
-
-/* ioctl() handlers
-   ---------------- */
-static int eql_enslave(struct net_device *dev,  slaving_request_t *srq); /*  */
-static int eql_emancipate(struct net_device *dev, slaving_request_t *srq); /*  */
-
-static int eql_g_slave_cfg(struct net_device *dev, slave_config_t *sc); /*  */
-static int eql_s_slave_cfg(struct net_device *dev, slave_config_t *sc); /*  */
-
-static int eql_g_master_cfg(struct net_device *dev, master_config_t *mc); /*  */
-static int eql_s_master_cfg(struct net_device *dev, master_config_t *mc); /*  */
-
-static inline int eql_is_slave(struct net_device *dev); /*  */
-static inline int eql_is_master(struct net_device *dev); /*  */
-
-static slave_t *eql_new_slave(void); /*  */
-static void eql_delete_slave(slave_t *slave); /*  */
+static int eql_open(struct net_device *dev);
+static int eql_close(struct net_device *dev);
+static int eql_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd);
+static int eql_slave_xmit(struct sk_buff *skb, struct net_device *dev);
+static struct net_device_stats *eql_get_stats(struct net_device *dev);
 
-/* static long eql_slave_priority(slave_t *slave); -*  */
-static inline int eql_number_slaves(slave_queue_t *queue); /*  */
+#define eql_is_slave(dev)	((dev->flags & IFF_SLAVE) == IFF_SLAVE)
+#define eql_is_master(dev)	((dev->flags & IFF_MASTER) == IFF_MASTER)
 
-static inline int eql_is_empty(slave_queue_t *queue); /*  */
-static inline int eql_is_full(slave_queue_t *queue); /*  */
+static void eql_kill_one_slave(slave_t *slave);
 
-static slave_queue_t *eql_new_slave_queue(struct net_device *dev); /*  */
-static void eql_delete_slave_queue(slave_queue_t *queue); /*  */
-
-static int eql_insert_slave(slave_queue_t *queue, slave_t *slave); /*  */
-static slave_t *eql_remove_slave(slave_queue_t *queue, slave_t *slave); /*  */
-
-/* static int eql_insert_slave_dev(slave_queue_t *queue, struct net_device *dev); -*  */
-static int eql_remove_slave_dev(slave_queue_t *queue, struct net_device *dev); /*  */
-
-static inline struct net_device *eql_best_slave_dev(slave_queue_t *queue); /*  */
-static inline slave_t *eql_best_slave(slave_queue_t *queue); /*  */
-static inline slave_t *eql_first_slave(slave_queue_t *queue); /*  */
-static inline slave_t *eql_next_slave(slave_queue_t *queue, slave_t *slave); /*  */
-
-static inline void eql_set_best_slave(slave_queue_t *queue, slave_t *slave); /*  */
-static inline void eql_schedule_slaves(slave_queue_t *queue); /*  */
+static void eql_timer(unsigned long param)
+{
+	equalizer_t *eql = (equalizer_t *) param;
+	struct list_head *this, *tmp, *head;
+	
+	spin_lock_bh(&eql->queue.lock);
+	head = &eql->queue.all_slaves;
+	list_for_each_safe(this, tmp, head) {
+		slave_t *slave = list_entry(this, slave_t, list);
+
+		if ((slave->dev->flags & IFF_UP) == IFF_UP) {
+			slave->bytes_queued -= slave->priority_Bps;
+			if (slave->bytes_queued < 0)
+				slave->bytes_queued = 0;
+		} else {
+			eql_kill_one_slave(slave);
+		}
 
-static slave_t *eql_find_slave_dev(slave_queue_t *queue, struct net_device *dev); /*  */
+	}
+	spin_unlock_bh(&eql->queue.lock);
 
-/* static inline eql_lock_slave_queue(slave_queue_t *queue); -*  */
-/* static inline eql_unlock_slave_queue(slave_queue_t *queue); -*  */
+	eql->timer.expires = jiffies + EQL_DEFAULT_RESCHED_IVAL;
+	add_timer(&eql->timer);
+}
 
-static void eql_timer(unsigned long param);	/*  */
-
-/* struct net_device * interface functions 
-   ---------------------------------------------------------
-   */
+static char version[] __initdata = 
+	"Equalizer2002: Simon Janes (simon@ncm.com) and David S. Miller (davem@redhat.com)\n";
 
 static int __init eql_init(struct net_device *dev)
 {
-	static unsigned version_printed;
-	/* static unsigned num_masters     = 0; */
-	equalizer_t *eql = 0;
+	static unsigned int version_printed;
+	equalizer_t *eql;
 
 	SET_MODULE_OWNER(dev);
 
-	if ( version_printed++ == 0 && eql_debug > 0)
+	if (version_printed++ == 0)
 		printk(version);
-	/*
-	 *	Initialize the device structure. 
-	 */
-	dev->priv = kmalloc (sizeof (equalizer_t), GFP_KERNEL);
-	if (dev->priv == NULL)
-		return -ENOMEM;
-	memset (dev->priv, 0, sizeof (equalizer_t));
-	eql = (equalizer_t *) dev->priv;
 
-	eql->stats = kmalloc (sizeof (struct net_device_stats), GFP_KERNEL);
-	if (eql->stats == NULL) 
-	{
-		kfree(dev->priv);
-		dev->priv = NULL;
+	dev->priv = kmalloc(sizeof (equalizer_t), GFP_KERNEL);
+	if (dev->priv == NULL)
 		return -ENOMEM;
-	}
-	memset (eql->stats, 0, sizeof (struct net_device_stats));
+	memset(dev->priv, 0, sizeof (equalizer_t));
+	eql = dev->priv;
 
-	init_timer (&eql->timer);
+	init_timer(&eql->timer);
 	eql->timer.data     	= (unsigned long) dev->priv;
-	eql->timer.expires  	= jiffies+EQL_DEFAULT_RESCHED_IVAL;
-	eql->timer.function 	= &eql_timer;
-	eql->timer_on       	= 0;
+	eql->timer.expires  	= jiffies + EQL_DEFAULT_RESCHED_IVAL;
+	eql->timer.function 	= eql_timer;
+
+	spin_lock_init(&eql->queue.lock);
+	INIT_LIST_HEAD(&eql->queue.all_slaves);
+	eql->queue.master_dev	= dev;
 
 	dev->open		= eql_open;
 	dev->stop		= eql_close;
@@ -234,17 +193,12 @@
 	dev->hard_start_xmit	= eql_slave_xmit;
 	dev->get_stats		= eql_get_stats;
   
-  	/*
-  	 *	Fill in the fields of the device structure with 
-	 *	eql-generic values. 
-	 */
-
 	/*
 	 *	Now we undo some of the things that eth_setup does
 	 * 	that we don't like 
 	 */
 	 
-	dev->mtu        	= EQL_DEFAULT_MTU;	/* set to 576 in eql.h */
+	dev->mtu        	= EQL_DEFAULT_MTU;	/* set to 576 in if_eql.h */
 	dev->flags      	= IFF_MASTER;
 
 	dev->type       	= ARPHRD_SLIP;
@@ -255,257 +209,330 @@
 
 static int eql_open(struct net_device *dev)
 {
-	equalizer_t *eql = (equalizer_t *) dev->priv;
-	slave_queue_t *new_queue;
+	equalizer_t *eql = dev->priv;
 
-#ifdef EQL_DEBUG
-	if (eql_debug >= 5)
-		printk ("%s: open\n", dev->name);
-#endif
-
-	printk ("%s: remember to turn off Van-Jacobson compression on your slave devices.\n", dev->name);
-
-	new_queue = eql_new_slave_queue (dev);
-    
-	if (new_queue != 0)
-	{
-		new_queue->master_dev = dev;
-		eql->queue = new_queue;
-		eql->queue->lock = 0;
-		eql->min_slaves = 1;
-		eql->max_slaves = EQL_DEFAULT_MAX_SLAVES; /* 4 usually... */
-
-		printk ("%s: adding timer\n", dev->name);
-		eql->timer_on = 1;
-		add_timer (&eql->timer);
+	/* XXX We should force this off automatically for the user. */
+	printk(KERN_INFO "%s: remember to turn off Van-Jacobson compression on "
+	       "your slave devices.\n", dev->name);
 
-		return 0;
-	}
-	return -ENOMEM;
+	if (!list_empty(&eql->queue.all_slaves))
+		BUG();
+
+	eql->min_slaves = 1;
+	eql->max_slaves = EQL_DEFAULT_MAX_SLAVES; /* 4 usually... */
+
+	add_timer(&eql->timer);
+
+	return 0;
 }
 
+static void eql_kill_one_slave(slave_t *slave)
+{
+	list_del(&slave->list);
+	slave->dev->flags &= ~IFF_SLAVE;
+	dev_put(slave->dev);
+	kfree(slave);
+}
+
+static void eql_kill_slave_queue(slave_queue_t *queue)
+{ 
+	struct list_head *head, *tmp, *this;
+
+	spin_lock_bh(&queue->lock);
+
+	head = &queue->all_slaves;
+	list_for_each_safe(this, tmp, head) {
+		slave_t *s = list_entry(this, slave_t, list);
+
+		eql_kill_one_slave(s);
+		queue->num_slaves--;
+	}
+
+	spin_unlock_bh(&queue->lock);
+}
 
 static int eql_close(struct net_device *dev)
 {
-	equalizer_t *eql = (equalizer_t *) dev->priv;
+	equalizer_t *eql = dev->priv;
 
-#ifdef EQL_DEBUG
-	if ( eql_debug >= 5)
-		printk ("%s: close\n", dev->name);
-#endif
 	/*
 	 *	The timer has to be stopped first before we start hacking away
 	 *	at the data structure it scans every so often... 
 	 */
 
-#ifdef EQL_DEBUG
-	printk ("%s: stopping timer\n", dev->name);
-#endif	
-	eql->timer_on = 0;
-	del_timer (&eql->timer);
+	del_timer_sync(&eql->timer);
 
-	eql_delete_slave_queue (eql->queue);
+	eql_kill_slave_queue(&eql->queue);
 
 	return 0;
 }
 
+static int eql_enslave(struct net_device *dev,  slaving_request_t *srq);
+static int eql_emancipate(struct net_device *dev, slaving_request_t *srq);
+
+static int eql_g_slave_cfg(struct net_device *dev, slave_config_t *sc);
+static int eql_s_slave_cfg(struct net_device *dev, slave_config_t *sc);
+
+static int eql_g_master_cfg(struct net_device *dev, master_config_t *mc);
+static int eql_s_master_cfg(struct net_device *dev, master_config_t *mc);
 
 static int eql_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
 {  
-	if(cmd!=EQL_GETMASTRCFG && cmd!=EQL_GETSLAVECFG && 
-	   !capable(CAP_NET_ADMIN))
+	if (cmd != EQL_GETMASTRCFG && cmd != EQL_GETSLAVECFG &&
+	    !capable(CAP_NET_ADMIN))
 	  	return -EPERM;
-	switch (cmd)
-	{
+
+	switch (cmd) {
 		case EQL_ENSLAVE:
-			return eql_enslave (dev, (slaving_request_t *) ifr->ifr_data);
+			return eql_enslave(dev,
+					   (slaving_request_t *) ifr->ifr_data);
 		case EQL_EMANCIPATE:
-			return eql_emancipate (dev, (slaving_request_t *) ifr->ifr_data);
+			return eql_emancipate(dev,
+					      (slaving_request_t *) ifr->ifr_data);
 		case EQL_GETSLAVECFG:
-			return eql_g_slave_cfg (dev, (slave_config_t *) ifr->ifr_data);
+			return eql_g_slave_cfg(dev,
+					       (slave_config_t *) ifr->ifr_data);
 		case EQL_SETSLAVECFG:
-			return eql_s_slave_cfg (dev, (slave_config_t *) ifr->ifr_data);
+			return eql_s_slave_cfg(dev,
+					       (slave_config_t *) ifr->ifr_data);
 		case EQL_GETMASTRCFG:
-			return eql_g_master_cfg (dev, (master_config_t *) ifr->ifr_data);
+			return eql_g_master_cfg(dev,
+						(master_config_t *) ifr->ifr_data);
 		case EQL_SETMASTRCFG:
-			return eql_s_master_cfg (dev, (master_config_t *) ifr->ifr_data);
+			return eql_s_master_cfg(dev,
+						(master_config_t *) ifr->ifr_data);
 		default:
 			return -EOPNOTSUPP;
-	}
+	};
 }
 
+/* queue->lock must be held */
+static slave_t *__eql_schedule_slaves(slave_queue_t *queue)
+{
+	unsigned long best_load = ~0UL;
+	struct list_head *this, *tmp, *head;
+	slave_t *best_slave;
+
+	best_slave = NULL;
+
+	/* Make a pass to set the best slave. */
+	head = &queue->all_slaves;
+	list_for_each_safe(this, tmp, head) {
+		slave_t *slave = list_entry(this, slave_t, list);
+		unsigned long slave_load, bytes_queued, priority_Bps; 
+
+		/* Go through the slave list once, updating best_slave
+		 * whenever a new best_load is found.
+		 */
+		bytes_queued = slave->bytes_queued;
+		priority_Bps = slave->priority_Bps;    
+		if ((slave->dev->flags & IFF_UP) == IFF_UP) {
+			slave_load = (~0UL - (~0UL / 2)) - 
+				(priority_Bps) + bytes_queued * 8;
+
+			if (slave_load < best_load) {
+				best_load = slave_load;
+				best_slave = slave;
+			}
+		} else {
+			/* We found a dead slave, kill it. */
+			eql_kill_one_slave(slave);
+		}
+	}
+	return best_slave;
+}
 
 static int eql_slave_xmit(struct sk_buff *skb, struct net_device *dev)
 {
-	equalizer_t *eql = (equalizer_t *) dev->priv;
-	struct net_device *slave_dev = 0;
+	equalizer_t *eql = dev->priv;
 	slave_t *slave;
 
-	if (skb == NULL)
-		return 0;
+	spin_lock(&eql->queue.lock);
 
-	eql_schedule_slaves (eql->queue);
-  
-	slave = eql_best_slave (eql->queue); 
-	slave_dev = slave ? slave->dev : 0;
+	slave = __eql_schedule_slaves(&eql->queue);
+	if (slave) {
+		struct net_device *slave_dev = slave->dev;
 
-	if ( slave_dev != 0 )
-	{
-#ifdef EQL_DEBUG
-		if (eql_debug >= 100)
-			printk ("%s: %d slaves xmitng %d B %s\n", 
-				dev->name, eql_number_slaves (eql->queue), skb->len,
-				slave_dev->name);
-#endif
 		skb->dev = slave_dev;
 		skb->priority = 1;
 		slave->bytes_queued += skb->len; 
-		dev_queue_xmit (skb);
-		eql->stats->tx_packets++;
-	}
-	else
-	{
-		/*
-		 *	The alternative for this is the return 1 and have
-		 *	dev_queue_xmit just queue it up on the eql's queue. 
-		 */
-
-		eql->stats->tx_dropped++;
+		dev_queue_xmit(skb);
+		eql->stats.tx_packets++;
+	} else {
+		eql->stats.tx_dropped++;
 		dev_kfree_skb(skb);
 	}	  
+
+	spin_unlock(&eql->queue.lock);
+
 	return 0;
 }
 
-
 static struct net_device_stats * eql_get_stats(struct net_device *dev)
 {
-	equalizer_t *eql = (equalizer_t *) dev->priv;
-	return eql->stats;
+	equalizer_t *eql = dev->priv;
+	return &eql->stats;
 }
 
 /*
  *	Private ioctl functions
  */
 
-static int eql_enslave(struct net_device *dev, slaving_request_t *srqp)
+/* queue->lock must be held */
+static slave_t *__eql_find_slave_dev(slave_queue_t *queue, struct net_device *dev)
+{
+	struct list_head *this, *head;
+
+	head = &queue->all_slaves;
+	list_for_each(this, head) {
+		slave_t *slave = list_entry(this, slave_t, list);
+
+		if (slave->dev == dev)
+			return slave;
+	}
+
+	return NULL;
+}
+
+static inline int eql_is_full(slave_queue_t *queue)
+{
+	equalizer_t *eql = queue->master_dev->priv;
+
+	if (queue->num_slaves >= eql->max_slaves)
+		return 1;
+	return 0;
+}
+
+/* queue->lock must be held */
+static int __eql_insert_slave(slave_queue_t *queue, slave_t *slave)
+{
+	if (!eql_is_full(queue)) {
+		slave_t *duplicate_slave = 0;
+
+		duplicate_slave = __eql_find_slave_dev(queue, slave->dev);
+		if (duplicate_slave != 0)
+			eql_kill_one_slave(duplicate_slave);
+
+		list_add(&slave->list, &queue->all_slaves);
+		queue->num_slaves++;
+		slave->dev->flags |= IFF_SLAVE;
+
+		return 0;
+	}
+
+	return -ENOSPC;
+}
+
+static int eql_enslave(struct net_device *master_dev, slaving_request_t *srqp)
 {
-	struct net_device *master_dev;
 	struct net_device *slave_dev;
 	slaving_request_t srq;
 
 	if (copy_from_user(&srq, srqp, sizeof (slaving_request_t)))
-	  {
-#ifdef EQL_DEBUG
-	if (eql_debug >= 20)
-		printk ("EQL enslave: error detected by copy_from_user\n");
-#endif  
 		return -EFAULT;
-	  }
 
-#ifdef EQL_DEBUG
-	if (eql_debug >= 20)
-		printk ("%s: enslave '%s' %ld bps\n", dev->name, 
-			srq.slave_name, srq.priority);
-#endif  
-	master_dev = dev;		/* for "clarity" */
-	slave_dev  = __dev_get_by_name (srq.slave_name);
-
-	if (master_dev != 0 && slave_dev != 0)
-	{
-		if ((master_dev->flags & IFF_UP) == IFF_UP)
-                {
-			/*slave is not a master & not already a slave:*/
-			if (! eql_is_master (slave_dev)  &&
-			    ! eql_is_slave (slave_dev) )
-			{
-				slave_t *s = eql_new_slave ();
-				equalizer_t *eql = 
-					(equalizer_t *) master_dev->priv;
-				if (!s)
+	slave_dev  = dev_get_by_name(srq.slave_name);
+	if (slave_dev) {
+		if ((master_dev->flags & IFF_UP) == IFF_UP) {
+			/* slave is not a master & not already a slave: */
+			if (!eql_is_master(slave_dev) &&
+			    !eql_is_slave(slave_dev)) {
+				slave_t *s = kmalloc(sizeof(*s), GFP_KERNEL);
+				equalizer_t *eql = master_dev->priv;
+				int ret;
+
+				if (!s) {
+					dev_put(slave_dev);
 					return -ENOMEM;
+				}
+
+				memset(s, 0, sizeof(*s));
 				s->dev = slave_dev;
 				s->priority = srq.priority;
 				s->priority_bps = srq.priority;
 				s->priority_Bps = srq.priority / 8;
-				slave_dev->flags |= IFF_SLAVE;
-				eql_insert_slave (eql->queue, s);
-				return 0;
+
+				spin_lock_bh(&eql->queue.lock);
+				ret = __eql_insert_slave(&eql->queue, s);
+				if (ret) {
+					dev_put(slave_dev);
+					kfree(s);
+				}
+				spin_unlock_bh(&eql->queue.lock);
+
+				return ret;
 			}
-#ifdef EQL_DEBUG
-			else if (eql_debug >= 20)
-				printk ("EQL enslave: slave is master or slave is already slave\n");
-#endif  
 		}
-#ifdef EQL_DEBUG
-		else if (eql_debug >= 20)
-			printk ("EQL enslave: master device not up!\n");
-#endif  
-	}
-#ifdef EQL_DEBUG
-	else if (eql_debug >= 20)
-		printk ("EQL enslave: master or slave are NULL");
-#endif  
+		dev_put(slave_dev);
+	}
+
 	return -EINVAL;
 }
 
-static int eql_emancipate(struct net_device *dev, slaving_request_t *srqp)
+static int eql_emancipate(struct net_device *master_dev, slaving_request_t *srqp)
 {
-	struct net_device *master_dev;
+	equalizer_t *eql = master_dev->priv;
 	struct net_device *slave_dev;
 	slaving_request_t srq;
+	int ret;
 
 	if (copy_from_user(&srq, srqp, sizeof (slaving_request_t)))
 		return -EFAULT;
 
-#ifdef EQL_DEBUG
-	if (eql_debug >= 20)
-		printk ("%s: emancipate `%s`\n", dev->name, srq.slave_name);
-#endif
-	master_dev = dev;		/* for "clarity" */
-	slave_dev  = __dev_get_by_name (srq.slave_name);
-
-	if ( eql_is_slave (slave_dev) )	/* really is a slave */
-	{
-		equalizer_t *eql = (equalizer_t *) master_dev->priv;
-		slave_dev->flags = slave_dev->flags & ~IFF_SLAVE;
-		eql_remove_slave_dev (eql->queue, slave_dev);
-		return 0;
+	slave_dev = dev_get_by_name(srq.slave_name);
+	ret = -EINVAL;
+	if (slave_dev) {
+		spin_lock_bh(&eql->queue.lock);
+
+		if (eql_is_slave(slave_dev)) {
+			slave_t *slave = __eql_find_slave_dev(&eql->queue,
+							      slave_dev);
+
+			if (slave) {
+				eql_kill_one_slave(slave);
+				ret = 0;
+			}
+		}
+		dev_put(slave_dev);
+
+		spin_unlock_bh(&eql->queue.lock);
 	}
-	return -EINVAL;
-}
 
+	return ret;
+}
 
 static int eql_g_slave_cfg(struct net_device *dev, slave_config_t *scp)
 {
+	equalizer_t *eql = dev->priv;
 	slave_t *slave;
-	equalizer_t *eql;
 	struct net_device *slave_dev;
 	slave_config_t sc;
+	int ret;
 
-	if (copy_from_user (&sc, scp, sizeof (slave_config_t)))
+	if (copy_from_user(&sc, scp, sizeof (slave_config_t)))
 		return -EFAULT;
 
-#ifdef EQL_DEBUG
-	if (eql_debug >= 20)
-		printk ("%s: get config for slave `%s'\n", dev->name, sc.slave_name);
-#endif
-	eql = (equalizer_t *) dev->priv;
-	slave_dev = __dev_get_by_name (sc.slave_name);
-
-	if ( eql_is_slave (slave_dev) )
-	{
-		slave = eql_find_slave_dev (eql->queue,  slave_dev);
-		if (slave != 0)
-		{
+	slave_dev = dev_get_by_name(sc.slave_name);
+
+	ret = -EINVAL;
+
+	spin_lock_bh(&eql->queue.lock);
+	if (eql_is_slave(slave_dev)) {
+		slave = __eql_find_slave_dev(&eql->queue, slave_dev);
+		if (slave) {
 			sc.priority = slave->priority;
-			if (copy_to_user (scp, &sc, sizeof (slave_config_t)))
-				return -EFAULT;
-			return 0;
+			ret = 0;
 		}
 	}
-	return -EINVAL;
-}
+	spin_unlock_bh(&eql->queue.lock);
+
+	dev_put(slave_dev);
 
+	if (!ret && copy_to_user(scp, &sc, sizeof (slave_config_t)))
+		ret = -EFAULT;
+
+	return ret;
+}
 
 static int eql_s_slave_cfg(struct net_device *dev, slave_config_t *scp)
 {
@@ -513,71 +540,57 @@
 	equalizer_t *eql;
 	struct net_device *slave_dev;
 	slave_config_t sc;
+	int ret;
 
-	if (copy_from_user (&sc, scp, sizeof (slave_config_t)))
+	if (copy_from_user(&sc, scp, sizeof (slave_config_t)))
 		return -EFAULT;
 
-#ifdef EQL_DEBUG
-	if (eql_debug >= 20)
-		printk ("%s: set config for slave `%s'\n", dev->name, sc.slave_name);
-#endif
-  
+	eql = dev->priv;
+	slave_dev = dev_get_by_name(sc.slave_name);
 
-	eql = (equalizer_t *) dev->priv;
-	slave_dev = __dev_get_by_name (sc.slave_name);
+	ret = -EINVAL;
 
-	if ( eql_is_slave (slave_dev) )
-	{
-		slave = eql_find_slave_dev (eql->queue, slave_dev);
-		if (slave != 0)
-		{
+	spin_lock_bh(&eql->queue.lock);
+	if (eql_is_slave(slave_dev)) {
+		slave = __eql_find_slave_dev(&eql->queue, slave_dev);
+		if (slave) {
 			slave->priority = sc.priority;
 			slave->priority_bps = sc.priority;
 			slave->priority_Bps = sc.priority / 8;
-			return 0;
+			ret = 0;
 		}
 	}
-	return -EINVAL;
-}
+	spin_unlock_bh(&eql->queue.lock);
 
+	return ret;
+}
 
 static int eql_g_master_cfg(struct net_device *dev, master_config_t *mcp)
 {
 	equalizer_t *eql;
 	master_config_t mc;
 
-#if EQL_DEBUG
-	if (eql_debug >= 20)
-		printk ("%s: get master config\n", dev->name);
-#endif
-
-	if ( eql_is_master (dev) )
-	{
-		eql = (equalizer_t *) dev->priv;
+	if (eql_is_master(dev)) {
+		eql = dev->priv;
 		mc.max_slaves = eql->max_slaves;
 		mc.min_slaves = eql->min_slaves;
-		if (copy_to_user (mcp, &mc, sizeof (master_config_t)))
+		if (copy_to_user(mcp, &mc, sizeof (master_config_t)))
 			return -EFAULT;
 		return 0;
 	}
 	return -EINVAL;
 }
 
-
 static int eql_s_master_cfg(struct net_device *dev, master_config_t *mcp)
 {
 	equalizer_t *eql;
 	master_config_t mc;
 
-	if (copy_from_user (&mc, mcp, sizeof (master_config_t)))
+	if (copy_from_user(&mc, mcp, sizeof (master_config_t)))
 		return -EFAULT;
-#if EQL_DEBUG
-	if (eql_debug >= 20)
-		printk ("%s: set master config\n", dev->name);
-#endif
-	if ( eql_is_master (dev) )
-	{
-		eql = (equalizer_t *) dev->priv;
+
+	if (eql_is_master(dev)) {
+		eql = dev->priv;
 		eql->max_slaves = mc.max_slaves;
 		eql->min_slaves = mc.min_slaves;
 		return 0;
@@ -585,415 +598,6 @@
 	return -EINVAL;
 }
 
-/*
- *	Private device support functions
- */
-
-static inline int eql_is_slave(struct net_device *dev)
-{
-	if (dev)
-	{
-		if ((dev->flags & IFF_SLAVE) == IFF_SLAVE)
-			return 1;
-	}
-	return 0;
-}
-
-
-static inline int eql_is_master(struct net_device *dev)
-{
-	if (dev)
-	{
-		if ((dev->flags & IFF_MASTER) == IFF_MASTER)
-		return 1;
-	}
-	return 0;
-}
-
-
-static slave_t *eql_new_slave(void)
-{
-	slave_t *slave;
-
-	slave = (slave_t *) kmalloc (sizeof (slave_t), GFP_KERNEL);
-	if (slave)
-		memset(slave, 0, sizeof (slave_t));
-	return slave;
-}
-
-
-static void eql_delete_slave(slave_t *slave)
-{
-	kfree (slave);
-}
-
-
-#if 0				/* not currently used, will be used
-				   when we really use a priority queue */
-static long slave_Bps(slave_t *slave)
-{
-	return (slave->priority_Bps);
-}
-
-static long slave_bps(slave_t *slave)
-{
-	return (slave->priority_bps);
-}
-
-#endif
-
-static inline int eql_number_slaves(slave_queue_t *queue)
-{
-	return queue->num_slaves;
-}
-
-static inline int eql_is_empty(slave_queue_t *queue)
-{
-	if (eql_number_slaves (queue) == 0)
-		return 1;
-	return 0;
-}
-
-static inline int eql_is_full(slave_queue_t *queue)
-{
-	equalizer_t *eql = (equalizer_t *) queue->master_dev->priv;
-
-	if (eql_number_slaves (queue) == eql->max_slaves)
-		return 1;
-	return 0;
-}
-
-static slave_queue_t *eql_new_slave_queue(struct net_device *dev)
-{
-	slave_queue_t *queue;
-	slave_t *head_slave;
-	slave_t *tail_slave;
-
-	queue = (slave_queue_t *) kmalloc (sizeof (slave_queue_t), GFP_KERNEL);
-	if (!queue)
-		goto err_out;
-
-	head_slave = eql_new_slave ();
-	if (!head_slave)
-		goto err_out_queue;
-
-	tail_slave = eql_new_slave ();
-	if (!tail_slave)
-		goto err_out_hs;
-
-	memset (queue, 0, sizeof (slave_queue_t));
-
-	head_slave->next = tail_slave;
-	tail_slave->next = 0;
-	queue->head = head_slave;
-	queue->num_slaves = 0;
-	queue->master_dev = dev;
-	return queue;
-
-err_out_hs:
-	kfree (head_slave);
-err_out_queue:
-	kfree (queue);
-err_out:
-	return NULL;
-}
-
-
-static void eql_delete_slave_queue(slave_queue_t *queue)
-{ 
-	slave_t *zapped;
-	/*
-	 *	This should only be called when there isn't a
-	 *	timer running that scans the data periodically.. 
-	 *	dev_close stops the timer... 
-	 */
-
-	while ( ! eql_is_empty (queue) )
-	{
-		zapped = eql_remove_slave (queue, queue->head->next);
-		eql_delete_slave (zapped);
-	}
-	kfree (queue->head->next);
-	kfree (queue->head);
-	kfree (queue);
-}
-
-static int eql_insert_slave(slave_queue_t *queue, slave_t *slave)
-{
-	unsigned long flags;
-
-	save_flags(flags);
-	cli ();
-
-	if ( ! eql_is_full (queue) )
-	{
-		slave_t *duplicate_slave = 0;
-		duplicate_slave = eql_find_slave_dev (queue, slave->dev);
-		if (duplicate_slave != 0)
-		{
-/*			  printk ("%s: found a duplicate, killing it and replacing\n",
-				  queue->master_dev->name); */
-			eql_delete_slave (eql_remove_slave (queue, duplicate_slave));
-		}
-		slave->next = queue->head->next;
-		queue->head->next = slave;
-		queue->num_slaves++;
-		restore_flags(flags);
-		return 0;
-	}
-	restore_flags(flags);
-	return 1;
-}
-
-
-static slave_t *eql_remove_slave(slave_queue_t *queue, slave_t *slave)
-{
-	slave_t *prev;
-	slave_t *curr;
-	unsigned long flags;
-
-	save_flags(flags);
-	cli ();
-
-	prev = queue->head;
-	curr = queue->head->next;
-	while (curr != slave && 
-		curr->dev != 0 )
-	{
-/* 		printk ("%s: remove_slave; searching...\n", queue->master_dev->name); */
-		prev = curr;
-		curr = curr->next;
-	}
-
-	if (curr == slave)
-	{
-		prev->next = curr->next;
-		queue->num_slaves--;
-		curr->dev->flags = curr->dev->flags & ~IFF_SLAVE;
-		restore_flags(flags);
-		return curr;
-	}
-	restore_flags(flags);
-	return 0;			/* not found */
-}
-
-
-static int eql_remove_slave_dev(slave_queue_t *queue, struct net_device *dev)
-{
-	slave_t *prev;
-	slave_t *curr;
-	slave_t *target;
-
-	target = eql_find_slave_dev (queue, dev);
-
-	if (target != 0)
-	{
-		unsigned long flags;
-
-		save_flags(flags);
-		cli ();
-		prev = queue->head;
-		curr = prev->next;
-		while (curr != target)
-		{
-			prev = curr;
-			curr = curr->next;
-		}
-		prev->next = curr->next;
-		queue->num_slaves--;
-		restore_flags(flags);
-		eql_delete_slave (curr);
-		return 0;
-	}
-	return 1;
-}
-
-
-static inline struct net_device *eql_best_slave_dev(slave_queue_t *queue)
-{
-	if (queue->best_slave != 0)
-	{
-		if (queue->best_slave->dev != 0)
-			return queue->best_slave->dev;
-		else
-			return 0;
-	}
-	else
-		return 0;
-}
-
-
-static inline slave_t *eql_best_slave(slave_queue_t *queue)
-{
-	return queue->best_slave;
-}
-
-static inline void eql_schedule_slaves(slave_queue_t *queue)
-{
-	struct net_device *master_dev = queue->master_dev;
-	slave_t *best_slave = 0;
-	slave_t *slave_corpse = 0;
-
-#ifdef EQL_DEBUG
-	if (eql_debug >= 100)
-		printk ("%s: schedule %d slaves\n", 
-			master_dev->name, eql_number_slaves (queue));
-#endif
-	if ( eql_is_empty (queue) )
-	{
-		/*
-		 *	No slaves to play with 
-		 */
-		eql_set_best_slave (queue, (slave_t *) 0);
-		return;
-	}
-	else
-	{		
-		/*
-		 *	Make a pass to set the best slave 
-		 */
-		unsigned long best_load = (unsigned long) ULONG_MAX;
-		slave_t *slave = 0;
-		unsigned long flags;
-		int i;
-
-		save_flags(flags);
-		cli ();
-		for (i = 1, slave = eql_first_slave (queue);
-			i <= eql_number_slaves (queue);
-			i++, slave = eql_next_slave (queue, slave))
-		{
-			/*
-			 *	Go through the slave list once, updating best_slave 
-			 *      whenever a new best_load is found, whenever a dead
-			 *	slave is found, it is marked to be pulled out of the 
-			 *	queue 
-			 */
-		
-			unsigned long slave_load;
-			unsigned long bytes_queued; 
-			unsigned long priority_Bps; 
-	  	
-	  		if (slave != 0)
-			{
-				bytes_queued = slave->bytes_queued;
-				priority_Bps = slave->priority_Bps;    
-				if ( slave->dev != 0)
-				{
-					if ((slave->dev->flags & IFF_UP) == IFF_UP )
-					{
-						slave_load = (ULONG_MAX - (ULONG_MAX / 2)) - 
-							(priority_Bps) + bytes_queued * 8;
-
-		      				if (slave_load < best_load)
-						{
-							best_load = slave_load;
-							best_slave = slave;
-						}
-					}
-					else		/* we found a dead slave */
-					{
-						/* 
-						 *	We only bury one slave at a time, if more than
-						 *	one slave dies, we will bury him on the next 
-						 *	reschedule. slaves don't die all at once that 
-						 *	much anyway 
-						 */
-						slave_corpse = slave;
-					}
-				}
-			}
-		} /* for */
-		restore_flags(flags);
-		eql_set_best_slave (queue, best_slave);
-	} /* else */
-	if (slave_corpse != 0)
-	{
-		printk ("eql: scheduler found dead slave, burying...\n");
-		eql_delete_slave (eql_remove_slave (queue, slave_corpse));
-	}
-	return;
-}
-
-
-static slave_t * eql_find_slave_dev(slave_queue_t *queue, struct net_device *dev)
-{
-	slave_t *slave = 0;
-	slave = eql_first_slave(queue);
-
-	while (slave != 0 && slave->dev != dev && slave != 0)
-	{
-#if 0
-		if (slave->dev != 0)
-			printk ("eql: find_slave_dev; looked at '%s'...\n", slave->dev->name);
-		else
-			printk ("eql: find_slave_dev; looked at nothing...\n");
-#endif
-		slave = slave->next;
-	}
-	return slave;
-}
-
-
-static inline slave_t *eql_first_slave(slave_queue_t *queue)
-{
-	return queue->head->next;
-}
-
-
-static inline slave_t *eql_next_slave(slave_queue_t *queue, slave_t *slave)
-{
-	return slave->next;
-}
-
-static inline void eql_set_best_slave(slave_queue_t *queue, slave_t *slave)
-{
-	queue->best_slave = slave;
-}
-
-static void eql_timer(unsigned long param)
-{
-	equalizer_t *eql = (equalizer_t *) param;
-	slave_t *slave;
-	slave_t *slave_corpse = 0;
-	int i;
-	unsigned long flags;
-	
-	if ( ! eql_is_empty (eql->queue) )
-	{
-		save_flags(flags);
-		cli ();
-		for (i = 1, slave = eql_first_slave (eql->queue);
-			i <= eql_number_slaves (eql->queue);
-			i++, slave = eql_next_slave (eql->queue, slave))
-		{
-			if (slave != 0)
-			{
-				if ((slave->dev->flags & IFF_UP) == IFF_UP )
-				{
-					slave->bytes_queued -= slave->priority_Bps;
-					if (slave->bytes_queued < 0)
-						slave->bytes_queued = 0;
-				}
-				else
-					slave_corpse = slave;
-			}
-		}
-		restore_flags(flags);
-		if (slave_corpse != 0)
-		{
-			printk ("eql: timer found dead slave, burying...\n");
-			eql_delete_slave (eql_remove_slave (eql->queue, slave_corpse));
-		}
-	}
-
-	if (eql->timer_on != 0) 
-	{
-		eql->timer.expires = jiffies+EQL_DEFAULT_RESCHED_IVAL;
-		add_timer (&eql->timer);
-	}
-}
-
 static struct net_device dev_eql;
 
 static int __init eql_init_module(void)
@@ -1009,7 +613,6 @@
 
 static void __exit eql_cleanup_module(void)
 {
-	kfree(((equalizer_t *)dev_eql.priv)->stats );
 	kfree(dev_eql.priv);
 	unregister_netdev(&dev_eql);
 }
@@ -1017,11 +620,3 @@
 module_init(eql_init_module);
 module_exit(eql_cleanup_module);
 MODULE_LICENSE("GPL");
-
-/*
- * Local Variables: 
- * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c eql.c"
- * version-control: t
- * kept-new-versions: 20
- * End:
- */
diff -Nru a/drivers/net/hamachi.c b/drivers/net/hamachi.c
--- a/drivers/net/hamachi.c	Mon Oct  7 11:25:40 2002
+++ b/drivers/net/hamachi.c	Mon Oct  7 11:25:40 2002
@@ -1470,64 +1470,6 @@
 	spin_unlock(&hmp->lock);
 }
 
-#ifdef TX_CHECKSUM
-/*
- * Copied from eth_type_trans(), with reduced header, since we don't
- * get it on RX, only on TX.
- */
-static unsigned short hamachi_eth_type_trans(struct sk_buff *skb,
-  struct net_device *dev)
-{
-	struct ethhdr *eth;
-	unsigned char *rawp;
-	
-	skb->mac.raw=skb->data;
-	skb_pull(skb,dev->hard_header_len-8);  /* artificially enlarged on tx */
-	eth= skb->mac.ethernet;
-	
-	if(*eth->h_dest&1)
-	{
-		if(memcmp(eth->h_dest,dev->broadcast, ETH_ALEN)==0)
-			skb->pkt_type=PACKET_BROADCAST;
-		else
-			skb->pkt_type=PACKET_MULTICAST;
-	}
-	
-	/*
-	 *	This ALLMULTI check should be redundant by 1.4
-	 *	so don't forget to remove it.
-	 *
-	 *	Seems, you forgot to remove it. All silly devices
-	 *	seems to set IFF_PROMISC.
-	 */
-	 
-	else if(dev->flags&(IFF_PROMISC/*|IFF_ALLMULTI*/))
-	{
-		if(memcmp(eth->h_dest,dev->dev_addr, ETH_ALEN))
-			skb->pkt_type=PACKET_OTHERHOST;
-	}
-	
-	if (ntohs(eth->h_proto) >= 1536)
-		return eth->h_proto;
-		
-	rawp = skb->data;
-	
-	/*
-	 *	This is a magic hack to spot IPX packets. Older Novell breaks
-	 *	the protocol design and runs IPX over 802.3 without an 802.2 LLC
-	 *	layer. We look for FFFF which isn't a used 802.2 SSAP/DSAP. This
-	 *	won't work for fault tolerant netware but does for the rest.
-	 */
-	if (*(unsigned short *)rawp == 0xFFFF)
-		return htons(ETH_P_802_3);
-		
-	/*
-	 *	Real 802.2 LLC
-	 */
-	return htons(ETH_P_802_2);
-}
-#endif  /* TX_CHECKSUM */
-
 /* This routine is logically part of the interrupt handler, but seperated
    for clarity and better register allocation. */
 static int hamachi_rx(struct net_device *dev)
@@ -1632,12 +1574,7 @@
 				skb_put(skb = hmp->rx_skbuff[entry], pkt_len);
 				hmp->rx_skbuff[entry] = NULL;
 			}
-#ifdef TX_CHECKSUM
-			/* account for extra TX hard_header bytes */
-			skb->protocol = hamachi_eth_type_trans(skb, dev);
-#else
 			skb->protocol = eth_type_trans(skb, dev);
-#endif
 
 
 #ifdef RX_CHECKSUM
diff -Nru a/drivers/net/hamradio/baycom_epp.c b/drivers/net/hamradio/baycom_epp.c
--- a/drivers/net/hamradio/baycom_epp.c	Mon Oct  7 11:25:39 2002
+++ b/drivers/net/hamradio/baycom_epp.c	Mon Oct  7 11:25:39 2002
@@ -45,7 +45,7 @@
 #include <linux/kernel.h>
 #include <linux/init.h>
 #include <linux/string.h>
-#include <linux/tqueue.h>
+#include <linux/workqueue.h>
 #include <linux/fs.h>
 #include <linux/parport.h>
 #include <linux/smp_lock.h>
@@ -194,8 +194,8 @@
 	int magic;
 
         struct pardevice *pdev;
-	unsigned int bh_running;
-	struct tq_struct run_bh;
+	unsigned int work_running;
+	struct work_struct run_work;
 	unsigned int modem;
 	unsigned int bitrate;
 	unsigned char stat;
@@ -829,7 +829,7 @@
 	
 	baycom_paranoia_check_void(dev, "epp_bh");
 	bc = (struct baycom_state *)dev->priv;
-	if (!bc->bh_running)
+	if (!bc->work_running)
 		return;
 	baycom_int_freq(bc);
 	pp = bc->pdev->port;
@@ -928,7 +928,7 @@
 	bc->debug_vals.mod_cycles = time2 - time1;
 	bc->debug_vals.demod_cycles = time3 - time2;
 #endif /* BAYCOM_DEBUG */
-	queue_task(&bc->run_bh, &tq_timer);
+	schedule_delayed_work(&bc->run_work, 1);
 	if (!bc->skb)
 		netif_wake_queue(dev);
 	return;
@@ -1019,10 +1019,6 @@
 {
 	struct baycom_state *bc;
         struct parport *pp;
-	const struct tq_struct run_bh = {
-		.routine = (void *)(void *)epp_bh,
-		.data = dev
-	};
 	unsigned int i, j;
 	unsigned char tmp[128];
 	unsigned char stat;
@@ -1060,8 +1056,8 @@
                 return -EBUSY;
         }
         dev->irq = /*pp->irq*/ 0;
-	bc->run_bh = run_bh;
-	bc->bh_running = 1;
+	INIT_WORK(&bc->run_work, (void *)(void *)epp_bh, dev);
+	bc->work_running = 1;
 	bc->modem = EPP_CONVENTIONAL;
 	if (eppconfig(bc))
 		printk(KERN_INFO "%s: no FPGA detected, assuming conventional EPP modem\n", bc_drvname);
@@ -1121,7 +1117,7 @@
 	bc->hdlctx.slotcnt = bc->ch_params.slottime;
 	bc->hdlctx.calibrate = 0;
 	/* start the bottom half stuff */
-	queue_task(&bc->run_bh, &tq_timer);
+	schedule_delayed_work(&bc->run_work, 1);
 	netif_start_queue(dev);
 	MOD_INC_USE_COUNT;
 	return 0;
@@ -1145,8 +1141,8 @@
 	baycom_paranoia_check(dev, "epp_close", -EINVAL);
 	bc = (struct baycom_state *)dev->priv;
 	pp = bc->pdev->port;
-	bc->bh_running = 0;
-	run_task_queue(&tq_timer);  /* dequeue bottom half */
+	bc->work_running = 0;
+	flush_scheduled_work();
 	bc->stat = EPP_DCDBIT;
 	tmp[0] = 0;
 	pp->ops->epp_write_addr(pp, tmp, 1, 0);
diff -Nru a/drivers/net/hamradio/dmascc.c b/drivers/net/hamradio/dmascc.c
--- a/drivers/net/hamradio/dmascc.c	Mon Oct  7 11:25:33 2002
+++ b/drivers/net/hamradio/dmascc.c	Mon Oct  7 11:25:33 2002
@@ -36,7 +36,7 @@
 #include <linux/netdevice.h>
 #include <linux/rtnetlink.h>
 #include <linux/sockios.h>
-#include <linux/tqueue.h>
+#include <linux/workqueue.h>
 #include <linux/version.h>
 #include <asm/atomic.h>
 #include <asm/bitops.h>
@@ -225,7 +225,7 @@
   char rx_buf[NUM_RX_BUF][BUF_SIZE];
   int rx_len[NUM_RX_BUF];
   int rx_ptr;
-  struct tq_struct rx_task;
+  struct work_struct rx_work;
   int rx_head, rx_tail, rx_count;
   int rx_over;
   char tx_buf[NUM_TX_BUF][BUF_SIZE];
@@ -569,8 +569,7 @@
     priv->param.clocks = TCTRxCP | RCRTxCP;
     priv->param.persist = 256;
     priv->param.dma = -1;
-    priv->rx_task.routine = rx_bh;
-    priv->rx_task.data = priv;
+    INIT_WORK(&priv->rx_work, rx_bh, priv);
     dev->priv = priv;
 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)
     if (sizeof(dev->name) == sizeof(char *)) dev->name = priv->name;
@@ -1072,9 +1071,7 @@
 	  priv->rx_len[priv->rx_head] = cb;
 	  priv->rx_head = (priv->rx_head + 1) % NUM_RX_BUF;
 	  priv->rx_count++;
-	  /* Mark bottom half handler */
-	  queue_task(&priv->rx_task, &tq_immediate);
-	  mark_bh(IMMEDIATE_BH);
+	  schedule_work(&priv->rx_work);
 	} else {
 	  priv->stats.rx_errors++;
 	  priv->stats.rx_over_errors++;
diff -Nru a/drivers/net/hp100.c b/drivers/net/hp100.c
--- a/drivers/net/hp100.c	Mon Oct  7 11:25:32 2002
+++ b/drivers/net/hp100.c	Mon Oct  7 11:25:32 2002
@@ -412,7 +412,7 @@
 	/* First: scan PCI bus(es) */
 
 #ifdef CONFIG_PCI
-	if (pcibios_present()) {
+	if (pci_present()) {
 		int pci_index;
 		struct pci_dev *pci_dev = NULL;
 		int pci_id_index;
@@ -2960,7 +2960,7 @@
 {
 	int i, cards;
 
-	if (hp100_port == 0 && !EISA_bus && !pcibios_present())
+	if (hp100_port == 0 && !EISA_bus && !pci_present())
 		printk("hp100: You should not use auto-probing with insmod!\n");
 
 	/* Loop on all possible base addresses */
diff -Nru a/drivers/net/natsemi.c b/drivers/net/natsemi.c
--- a/drivers/net/natsemi.c	Mon Oct  7 11:25:25 2002
+++ b/drivers/net/natsemi.c	Mon Oct  7 11:25:25 2002
@@ -1,7 +1,8 @@
 /* natsemi.c: A Linux PCI Ethernet driver for the NatSemi DP8381x series. */
 /*
 	Written/copyright 1999-2001 by Donald Becker.
-	Portions copyright (c) 2001 Sun Microsystems (thockin@sun.com)
+	Portions copyright (c) 2001,2002 Sun Microsystems (thockin@sun.com)
+	Portions copyright 2001,2002 Manfred Spraul (manfred@colorfullife.com)
 
 	This software may be used and distributed according to the terms of
 	the GNU General Public License (GPL), incorporated herein by reference.
@@ -86,7 +87,7 @@
 		* use long for ee_addr (various)
 		* print pointers properly (DaveM)
 		* include asm/irq.h (?)
-	
+
 	version 1.0.11:
 		* check and reset if PHY errors appear (Adrian Sun)
 		* WoL cleanup (Tim Hockin)
@@ -119,10 +120,23 @@
 		  initialized
 		* enable only the WoL and PHY interrupts in wol mode
 
+	version 1.0.17:
+		* only do cable_magic on 83815 and early 83816 (Tim Hockin)
+		* create a function for rx refill (Manfred Spraul)
+		* combine drain_ring and init_ring (Manfred Spraul)
+		* oom handling (Manfred Spraul)
+		* hands_off instead of playing with netif_device_{de,a}ttach
+		  (Manfred Spraul)
+		* be sure to write the MAC back to the chip (Manfred Spraul)
+		* lengthen EEPROM timeout, and always warn about timeouts
+		  (Manfred Spraul)
+		* comments update (Manfred)
+		* do the right thing on a phy-reset (Manfred and Tim)
+
 	TODO:
 	* big endian support with CFG:BEM instead of cpu_to_le32
 	* support for an external PHY
-	* flow control
+	* NAPI
 */
 
 #if !defined(__OPTIMIZE__)
@@ -157,23 +171,11 @@
 #include <asm/uaccess.h>
 
 #define DRV_NAME	"natsemi"
-#define DRV_VERSION	"1.07+LK1.0.16"
-#define DRV_RELDATE	"Aug 28, 2002"
+#define DRV_VERSION	"1.07+LK1.0.17"
+#define DRV_RELDATE	"Sep 27, 2002"
 
 /* Updated to recommendations in pci-skeleton v2.03. */
 
-/* Automatically extracted configuration info:
-probe-func: natsemi_probe
-config-in: tristate 'National Semiconductor DP8381x series PCI Ethernet support' CONFIG_NATSEMI
-
-c-help-name: National Semiconductor DP8381x series PCI Ethernet support
-c-help-symbol: CONFIG_NATSEMI
-c-help: This driver is for the National Semiconductor DP8381x series,
-c-help: including the 8381[56] chips.
-c-help: More specific information and updates are available from 
-c-help: http://www.scyld.com/network/natsemi.html
-*/
-
 /* The user-configurable values.
    These may be modified when a driver module is loaded.*/
 
@@ -235,9 +237,11 @@
 
 /* These identify the driver base version and may not be removed. */
 static char version[] __devinitdata =
-KERN_INFO DRV_NAME ".c:v1.07 1/9/2001  Written by Donald Becker <becker@scyld.com>\n"
-KERN_INFO "  http://www.scyld.com/network/natsemi.html\n"
-KERN_INFO "  (unofficial 2.4.x kernel port, version " DRV_VERSION ", " DRV_RELDATE "  Jeff Garzik, Tjeerd Mulder)\n";
+  KERN_INFO DRV_NAME " dp8381x driver, version "
+      DRV_VERSION ", " DRV_RELDATE "\n"
+  KERN_INFO "  originally by Donald Becker <becker@scyld.com>\n"
+  KERN_INFO "  http://www.scyld.com/network/natsemi.html\n"
+  KERN_INFO "  2.4.x kernel port by Jeff Garzik, Tjeerd Mulder\n";
 
 MODULE_AUTHOR("Donald Becker <becker@scyld.com>");
 MODULE_DESCRIPTION("National Semiconductor DP8381x series PCI Ethernet driver");
@@ -249,11 +253,14 @@
 MODULE_PARM(rx_copybreak, "i");
 MODULE_PARM(options, "1-" __MODULE_STRING(MAX_UNITS) "i");
 MODULE_PARM(full_duplex, "1-" __MODULE_STRING(MAX_UNITS) "i");
-MODULE_PARM_DESC(max_interrupt_work, "DP8381x maximum events handled per interrupt");
+MODULE_PARM_DESC(max_interrupt_work, 
+	"DP8381x maximum events handled per interrupt");
 MODULE_PARM_DESC(mtu, "DP8381x MTU (all boards)");
 MODULE_PARM_DESC(debug, "DP8381x default debug bitmask");
-MODULE_PARM_DESC(rx_copybreak, "DP8381x copy breakpoint for copy-only-tiny-frames");
-MODULE_PARM_DESC(options, "DP8381x: Bits 0-3: media type, bit 17: full duplex");
+MODULE_PARM_DESC(rx_copybreak, 
+	"DP8381x copy breakpoint for copy-only-tiny-frames");
+MODULE_PARM_DESC(options, 
+	"DP8381x: Bits 0-3: media type, bit 17: full duplex");
 MODULE_PARM_DESC(full_duplex, "DP8381x full duplex setting(s) (1)");
 
 /*
@@ -267,7 +274,7 @@
 II. Board-specific settings
 
 This driver requires the PCI interrupt line to be valid.
-It honors the EEPROM-set values. 
+It honors the EEPROM-set values.
 
 III. Driver operation
 
@@ -277,7 +284,7 @@
 formed into rings by a branch from the final descriptor to the beginning of
 the list.  The ring sizes are set at compile time by RX/TX_RING_SIZE.
 The NatSemi design uses a 'next descriptor' pointer that the driver forms
-into a list. 
+into a list.
 
 IIIb/c. Transmit/Receive Structure
 
@@ -306,20 +313,19 @@
 
 IIId. Synchronization
 
-The driver runs as two independent, single-threaded flows of control.  One
-is the send-packet routine, which enforces single-threaded use by the
-dev->tbusy flag.  The other thread is the interrupt handler, which is single
-threaded by the hardware and interrupt handling software.
-
-The send packet thread has partial control over the Tx ring and 'dev->tbusy'
-flag.  It sets the tbusy flag whenever it's queuing a Tx packet. If the next
-queue slot is empty, it clears the tbusy flag when finished otherwise it sets
-the 'lp->tx_full' flag.
-
-The interrupt handler has exclusive control over the Rx ring and records stats
-from the Tx ring.  After reaping the stats, it marks the Tx queue entry as
-empty by incrementing the dirty_tx mark. Iff the 'lp->tx_full' flag is set, it
-clears both the tx_full and tbusy flags.
+Most operations are synchronized on the np->lock irq spinlock, except the
+performance critical codepaths:
+
+The rx process only runs in the interrupt handler. Access from outside
+the interrupt handler is only permitted after disable_irq().
+
+The rx process usually runs under the dev->xmit_lock. If np->intr_tx_reap
+is set, then access is permitted under spin_lock_irq(&np->lock).
+
+Thus configuration functions that want to access everything must call
+	disable_irq(dev->irq);
+	spin_lock_bh(dev->xmit_lock);
+	spin_lock_irq(&np->lock);
 
 IV. Notes
 
@@ -337,7 +343,7 @@
 None characterised.
 */
 
-
+
 
 enum pcistuff {
 	PCI_USES_IO = 0x01,
@@ -378,6 +384,7 @@
 	IntrStatus		= 0x10,
 	IntrMask		= 0x14,
 	IntrEnable		= 0x18,
+	IntrHoldoff		= 0x16, /* DP83816 only */
 	TxRingPtr		= 0x20,
 	TxConfig		= 0x24,
 	RxRingPtr		= 0x30,
@@ -486,11 +493,11 @@
 
 /*
  * Default Interrupts:
- * Rx OK, Rx Packet Error, Rx Overrun, 
- * Tx OK, Tx Packet Error, Tx Underrun, 
+ * Rx OK, Rx Packet Error, Rx Overrun,
+ * Tx OK, Tx Packet Error, Tx Underrun,
  * MIB Service, Phy Interrupt, High Bits,
  * Rx Status FIFO overrun,
- * Received Target Abort, Received Master Abort, 
+ * Received Target Abort, Received Master Abort,
  * Signalled System Error, Received Parity Error
  */
 #define DEFAULT_INTR 0x00f1cd65
@@ -587,8 +594,11 @@
 	PhyAddrMask		= 0xf,
 };
 
-#define SRR_REV_C	0x0302
-#define SRR_REV_D	0x0403
+/* values we might find in the silicon revision register */
+#define SRR_DP83815_C	0x0302
+#define SRR_DP83815_D	0x0403
+#define SRR_DP83816_A4	0x0504
+#define SRR_DP83816_A5	0x0505
 
 /* The Rx and Tx buffer descriptors. */
 /* Note that using only 32 bit fields simplifies conversion to big-endian
@@ -603,14 +613,14 @@
 /* Bits in network_desc.status */
 enum desc_status_bits {
 	DescOwn=0x80000000, DescMore=0x40000000, DescIntr=0x20000000,
-	DescNoCRC=0x10000000, DescPktOK=0x08000000, 
+	DescNoCRC=0x10000000, DescPktOK=0x08000000,
 	DescSizeMask=0xfff,
 
-	DescTxAbort=0x04000000, DescTxFIFO=0x02000000, 
+	DescTxAbort=0x04000000, DescTxFIFO=0x02000000,
 	DescTxCarrier=0x01000000, DescTxDefer=0x00800000,
 	DescTxExcDefer=0x00400000, DescTxOOWCol=0x00200000,
 	DescTxExcColl=0x00100000, DescTxCollCount=0x000f0000,
-	
+
 	DescRxAbort=0x04000000, DescRxOver=0x02000000,
 	DescRxDest=0x01800000, DescRxLong=0x00400000,
 	DescRxRunt=0x00200000, DescRxInvalid=0x00100000,
@@ -619,30 +629,36 @@
 };
 
 struct netdev_private {
-	/* Descriptor rings first for alignment. */
+	/* Descriptor rings first for alignment */
 	dma_addr_t ring_dma;
-	struct netdev_desc* rx_ring;
-	struct netdev_desc* tx_ring;
-	/* The addresses of receive-in-place skbuffs. */
-	struct sk_buff* rx_skbuff[RX_RING_SIZE];
+	struct netdev_desc *rx_ring;
+	struct netdev_desc *tx_ring;
+	/* The addresses of receive-in-place skbuffs */
+	struct sk_buff *rx_skbuff[RX_RING_SIZE];
 	dma_addr_t rx_dma[RX_RING_SIZE];
-	/* The saved address of a sent-in-place packet/buffer, for later free(). */
-	struct sk_buff* tx_skbuff[TX_RING_SIZE];
+	/* address of a sent-in-place packet/buffer, for later free() */
+	struct sk_buff *tx_skbuff[TX_RING_SIZE];
 	dma_addr_t tx_dma[TX_RING_SIZE];
 	struct net_device_stats stats;
-	struct timer_list timer;	/* Media monitoring timer. */
-	/* Frequently used values: keep some adjacent for cache effect. */
+	/* Media monitoring timer */
+	struct timer_list timer;
+	/* Frequently used values: keep some adjacent for cache effect */
 	struct pci_dev *pci_dev;
 	struct netdev_desc *rx_head_desc;
-	unsigned int cur_rx, dirty_rx;		/* Producer/consumer ring indices */
+	/* Producer/consumer ring indices */
+	unsigned int cur_rx, dirty_rx;
 	unsigned int cur_tx, dirty_tx;
-	unsigned int rx_buf_sz;				/* Based on MTU+slack. */
-	/* These values are keep track of the transceiver/media in use. */
+	/* Based on MTU+slack. */
+	unsigned int rx_buf_sz;
+	int oom;
+	/* Do not touch the nic registers */
+	int hands_off;
+	/* These values are keep track of the transceiver/media in use */
 	unsigned int full_duplex;
-	/* Rx filter. */
+	/* Rx filter */
 	u32 cur_rx_mode;
 	u32 rx_filter[16];
-	/* FIFO and PCI burst thresholds. */
+	/* FIFO and PCI burst thresholds */
 	u32 tx_config, rx_config;
 	/* original contents of ClkRun register */
 	u32 SavedClkRun;
@@ -650,8 +666,8 @@
 	u32 srr;
 	/* expected DSPCFG value */
 	u16 dspcfg;
-	/* MII transceiver section. */
-	u16 advertising; /* NWay media advertisement */
+	/* MII transceiver section */
+	u16 advertising;
 	unsigned int iosize;
 	spinlock_t lock;
 	u32 msg_enable;
@@ -668,11 +684,15 @@
 static void undo_cable_magic(struct net_device *dev);
 static void check_link(struct net_device *dev);
 static void netdev_timer(unsigned long data);
+static void dump_ring(struct net_device *dev);
 static void tx_timeout(struct net_device *dev);
 static int alloc_ring(struct net_device *dev);
+static void refill_rx(struct net_device *dev);
 static void init_ring(struct net_device *dev);
+static void drain_tx(struct net_device *dev);
 static void drain_ring(struct net_device *dev);
 static void free_ring(struct net_device *dev);
+static void reinit_ring(struct net_device *dev);
 static void init_registers(struct net_device *dev);
 static int start_tx(struct sk_buff *skb, struct net_device *dev);
 static void intr_handler(int irq, void *dev_instance, struct pt_regs *regs);
@@ -695,9 +715,9 @@
 static int netdev_get_regs(struct net_device *dev, u8 *buf);
 static int netdev_get_eeprom(struct net_device *dev, u8 *buf);
 
-
+
 static int __devinit natsemi_probe1 (struct pci_dev *pdev,
-				     const struct pci_device_id *ent)
+	const struct pci_device_id *ent)
 {
 	struct net_device *dev;
 	struct netdev_private *np;
@@ -777,6 +797,7 @@
 	np->iosize = iosize;
 	spin_lock_init(&np->lock);
 	np->msg_enable = debug;
+	np->hands_off = 0;
 
 	/* Reset the chip to erase previous misconfiguration. */
 	natsemi_reload_eeprom(dev);
@@ -791,7 +812,7 @@
 		if (option & 0x200)
 			np->full_duplex = 1;
 		if (option & 15)
-			printk(KERN_INFO 
+			printk(KERN_INFO
 				"%s: ignoring user supplied media type %d",
 				dev->name, option & 15);
 	}
@@ -823,27 +844,28 @@
 
 	if (netif_msg_drv(np)) {
 		printk(KERN_INFO "%s: %s at %#08lx, ",
-			   dev->name, natsemi_pci_info[chip_idx].name, ioaddr);
+			dev->name, natsemi_pci_info[chip_idx].name, ioaddr);
 		for (i = 0; i < ETH_ALEN-1; i++)
 				printk("%02x:", dev->dev_addr[i]);
 		printk("%02x, IRQ %d.\n", dev->dev_addr[i], irq);
 	}
 
 	np->advertising = mdio_read(dev, 1, MII_ADVERTISE);
-	if ((readl(ioaddr + ChipConfig) & 0xe000) != 0xe000 
+	if ((readl(ioaddr + ChipConfig) & 0xe000) != 0xe000
 	 && netif_msg_probe(np)) {
 		u32 chip_config = readl(ioaddr + ChipConfig);
 		printk(KERN_INFO "%s: Transceiver default autonegotiation %s "
-			   "10%s %s duplex.\n",
-			   dev->name,
-			   chip_config & CfgAnegEnable ? "enabled, advertise" : "disabled, force",
-			   chip_config & CfgAneg100 ? "0" : "",
-			   chip_config & CfgAnegFull ? "full" : "half");
+			"10%s %s duplex.\n",
+			dev->name,
+			chip_config & CfgAnegEnable ?
+			  "enabled, advertise" : "disabled, force",
+			chip_config & CfgAneg100 ? "0" : "",
+			chip_config & CfgAnegFull ? "full" : "half");
 	}
 	if (netif_msg_probe(np))
-		printk(KERN_INFO 
+		printk(KERN_INFO
 			"%s: Transceiver status %#04x advertising %#04x.\n",
-			dev->name, mdio_read(dev, 1, MII_BMSR), 
+			dev->name, mdio_read(dev, 1, MII_BMSR),
 			np->advertising);
 
 	/* save the silicon revision for later querying */
@@ -856,7 +878,7 @@
 	return 0;
 }
 
-
+
 /* Read the EEPROM and MII Management Data I/O (MDIO) interfaces.
    The EEPROM code is for the common 93c06/46 EEPROMs with 6 bit addresses. */
 
@@ -910,9 +932,9 @@
 	return retval;
 }
 
-/*  MII transceiver control section.
-	The 83815 series has an internal transceiver, and we present the
-	management registers as if they were MII connected. */
+/* MII transceiver control section.
+ * The 83815 series has an internal transceiver, and we present the
+ * management registers as if they were MII connected. */
 
 static int mdio_read(struct net_device *dev, int phy_id, int reg)
 {
@@ -950,11 +972,11 @@
 	u16 sopass[3];
 	struct netdev_private *np = dev->priv;
 
-	/* 
+	/*
 	 * Resetting the chip causes some registers to be lost.
 	 * Natsemi suggests NOT reloading the EEPROM while live, so instead
 	 * we save the state that would have been loaded from EEPROM
-	 * on a normal power-up (see the spec EEPROM map).  This assumes 
+	 * on a normal power-up (see the spec EEPROM map).  This assumes
 	 * whoever calls this will follow up with init_registers() eventually.
 	 */
 
@@ -982,12 +1004,12 @@
 			break;
 		udelay(5);
 	}
-	if (i==NATSEMI_HW_TIMEOUT && netif_msg_hw(np)) {
-		printk(KERN_INFO "%s: reset did not complete in %d usec.\n",
-		   dev->name, i*5);
+	if (i==NATSEMI_HW_TIMEOUT) {
+		printk(KERN_WARNING "%s: reset did not complete in %d usec.\n",
+			dev->name, i*5);
 	} else if (netif_msg_hw(np)) {
 		printk(KERN_DEBUG "%s: reset completed in %d usec.\n",
-		   dev->name, i*5);
+			dev->name, i*5);
 	}
 
 	/* restore CFG */
@@ -998,7 +1020,7 @@
 	writel(wcsr, dev->base_addr + WOLCmd);
 	/* read RFCR */
 	rfcr |= readl(dev->base_addr + RxFilterAddr) & ~RFCR_RESET_SAVE;
-	/* restore PMATCH */ 
+	/* restore PMATCH */
 	for (i = 0; i < 3; i++) {
 		writel(i*2, dev->base_addr + RxFilterAddr);
 		writew(pmatch[i], dev->base_addr + RxFilterData);
@@ -1009,7 +1031,6 @@
 	}
 	/* restore RFCR */
 	writel(rfcr, dev->base_addr + RxFilterAddr);
-	
 }
 
 static void natsemi_reload_eeprom(struct net_device *dev)
@@ -1019,16 +1040,16 @@
 
 	writel(EepromReload, dev->base_addr + PCIBusCfg);
 	for (i=0;i<NATSEMI_HW_TIMEOUT;i++) {
+		udelay(50);
 		if (!(readl(dev->base_addr + PCIBusCfg) & EepromReload))
 			break;
-		udelay(5);
 	}
-	if (i==NATSEMI_HW_TIMEOUT && netif_msg_hw(np)) {
-		printk(KERN_INFO "%s: EEPROM did not reload in %d usec.\n",
-		   dev->name, i*5);
+	if (i==NATSEMI_HW_TIMEOUT) {
+		printk(KERN_WARNING "%s: EEPROM did not reload in %d usec.\n",
+			dev->name, i*50);
 	} else if (netif_msg_hw(np)) {
 		printk(KERN_DEBUG "%s: EEPROM reloaded in %d usec.\n",
-		   dev->name, i*5);
+			dev->name, i*50);
 	}
 }
 
@@ -1044,12 +1065,12 @@
 			break;
 		udelay(5);
 	}
-	if (i==NATSEMI_HW_TIMEOUT && netif_msg_hw(np)) {
-		printk(KERN_INFO "%s: Tx/Rx process did not stop in %d usec.\n",
-				dev->name, i*5);
+	if (i==NATSEMI_HW_TIMEOUT) {
+		printk(KERN_WARNING "%s: Tx/Rx process did not stop in %d usec.\n",
+			dev->name, i*5);
 	} else if (netif_msg_hw(np)) {
 		printk(KERN_DEBUG "%s: Tx/Rx process stopped in %d usec.\n",
-				dev->name, i*5);
+			dev->name, i*5);
 	}
 }
 
@@ -1067,7 +1088,7 @@
 
 	if (netif_msg_ifup(np))
 		printk(KERN_DEBUG "%s: netdev_open() irq %d.\n",
-			   dev->name, dev->irq);
+			dev->name, dev->irq);
 	i = alloc_ring(dev);
 	if (i < 0) {
 		free_irq(dev->irq, dev);
@@ -1076,13 +1097,21 @@
 	init_ring(dev);
 	spin_lock_irq(&np->lock);
 	init_registers(dev);
+	/* now set the MAC address according to dev->dev_addr */
+	for (i = 0; i < 3; i++) {
+		u16 mac = (dev->dev_addr[2*i+1]<<8) + dev->dev_addr[2*i];
+
+		writel(i*2, ioaddr + RxFilterAddr);
+		writew(mac, ioaddr + RxFilterData);
+	}
+	writel(np->cur_rx_mode, ioaddr + RxFilterAddr);
 	spin_unlock_irq(&np->lock);
 
 	netif_start_queue(dev);
 
 	if (netif_msg_ifup(np))
 		printk(KERN_DEBUG "%s: Done netdev_open(), status: %#08x.\n",
-			   dev->name, (int)readl(ioaddr + ChipCmd));
+			dev->name, (int)readl(ioaddr + ChipCmd));
 
 	/* Set the timer to check for link beat. */
 	init_timer(&np->timer);
@@ -1096,6 +1125,11 @@
 
 static void do_cable_magic(struct net_device *dev)
 {
+	struct netdev_private *np = dev->priv;
+
+	if (np->srr >= SRR_DP83816_A5)
+		return;
+
 	/*
 	 * 100 MBit links with short cables can trip an issue with the chip.
 	 * The problem manifests as lots of CRC errors and/or flickering
@@ -1134,6 +1168,9 @@
 	u16 data;
 	struct netdev_private *np = dev->priv;
 
+	if (np->srr >= SRR_DP83816_A5)
+		return;
+
 	writew(1, dev->base_addr + PGSEL);
 	/* make sure the lock bit is clear */
 	data = readw(dev->base_addr + DSPCFG);
@@ -1152,7 +1189,7 @@
 	if (!(chipcfg & CfgLink)) {
 		if (netif_carrier_ok(dev)) {
 			if (netif_msg_link(np))
-				printk(KERN_NOTICE "%s: link down.\n", 
+				printk(KERN_NOTICE "%s: link down.\n",
 					dev->name);
 			netif_carrier_off(dev);
 			undo_cable_magic(dev);
@@ -1171,7 +1208,7 @@
 	/* if duplex is set then bit 28 must be set, too */
 	if (duplex ^ !!(np->rx_config & RxAcceptTx)) {
 		if (netif_msg_link(np))
-			printk(KERN_INFO 
+			printk(KERN_INFO
 				"%s: Setting %s-duplex based on negotiated "
 				"link capability.\n", dev->name,
 				duplex ? "full" : "half");
@@ -1199,16 +1236,16 @@
 		udelay(10);
 	}
 	if (i==NATSEMI_HW_TIMEOUT && netif_msg_link(np)) {
-		printk(KERN_INFO 
+		printk(KERN_INFO
 			"%s: autonegotiation did not complete in %d usec.\n",
 			dev->name, i*10);
 	}
 
 	/* On page 78 of the spec, they recommend some settings for "optimum
 	   performance" to be done in sequence.  These settings optimize some
-	   of the 100Mbit autodetection circuitry.  They say we only want to 
-	   do this for rev C of the chip, but engineers at NSC (Bradley 
-	   Kennedy) recommends always setting them.  If you don't, you get 
+	   of the 100Mbit autodetection circuitry.  They say we only want to
+	   do this for rev C of the chip, but engineers at NSC (Bradley
+	   Kennedy) recommends always setting them.  If you don't, you get
 	   errors on some autonegotiations that make the device unusable.
 	*/
 	writew(1, ioaddr + PGSEL);
@@ -1230,14 +1267,14 @@
 	readl(ioaddr + IntrStatus);
 
 	writel(np->ring_dma, ioaddr + RxRingPtr);
-	writel(np->ring_dma + RX_RING_SIZE * sizeof(struct netdev_desc), 
+	writel(np->ring_dma + RX_RING_SIZE * sizeof(struct netdev_desc),
 		ioaddr + TxRingPtr);
 
 	/* Initialize other registers.
 	 * Configure the PCI bus bursts and FIFO thresholds.
 	 * Configure for standard, in-spec Ethernet.
 	 * Start with half-duplex. check_link will update
-	 * to the correct settings. 
+	 * to the correct settings.
 	 */
 
 	/* DRTH: 2: start tx if 64 bytes are in the fifo
@@ -1259,13 +1296,13 @@
 	/* Disable PME:
 	 * The PME bit is initialized from the EEPROM contents.
 	 * PCI cards probably have PME disabled, but motherboard
-	 * implementations may have PME set to enable WakeOnLan. 
+	 * implementations may have PME set to enable WakeOnLan.
 	 * With PME set the chip will scan incoming packets but
 	 * nothing will be written to memory. */
 	np->SavedClkRun = readl(ioaddr + ClkRun);
 	writel(np->SavedClkRun & ~PMEEnable, ioaddr + ClkRun);
 	if (np->SavedClkRun & PMEStatus && netif_msg_wol(np)) {
-		printk(KERN_NOTICE "%s: Wake-up event %#08x\n", 
+		printk(KERN_NOTICE "%s: Wake-up event %#08x\n",
 			dev->name, readl(ioaddr + WOLCmd));
 	}
 
@@ -1281,13 +1318,16 @@
 }
 
 /*
+ * netdev_timer:
  * Purpose:
- * check for sudden death of the NIC:
- *
- * It seems that a reference set for this chip went out with incorrect info,
- * and there exist boards that aren't quite right.  An unexpected voltage drop
- * can cause the PHY to get itself in a weird state (basically reset..).
- * NOTE: this only seems to affect revC chips.
+ * 1) check for link changes. Usually they are handled by the MII interrupt
+ *    but it doesn't hurt to check twice.
+ * 2) check for sudden death of the NIC:
+ *    It seems that a reference set for this chip went out with incorrect info,
+ *    and there exist boards that aren't quite right.  An unexpected voltage
+ *    drop can cause the PHY to get itself in a weird state (basically reset).
+ *    NOTE: this only seems to affect revC chips.
+ * 3) check of death of the RX path due to OOM
  */
 static void netdev_timer(unsigned long data)
 {
@@ -1298,11 +1338,11 @@
 	u16 dspcfg;
 
 	if (netif_msg_timer(np)) {
-		/* DO NOT read the IntrStatus register, 
+		/* DO NOT read the IntrStatus register,
 		 * a read clears any pending interrupts.
 		 */
 		printk(KERN_DEBUG "%s: Media selection timer tick.\n",
-			   dev->name);
+			dev->name);
 	}
 
 	spin_lock_irq(&np->lock);
@@ -1319,6 +1359,9 @@
 					"re-initializing\n", dev->name);
 			disable_irq(dev->irq);
 			spin_lock_irq(&np->lock);
+			natsemi_stop_rxtx(dev);
+			dump_ring(dev);
+			reinit_ring(dev);
 			init_registers(dev);
 			spin_unlock_irq(&np->lock);
 			enable_irq(dev->irq);
@@ -1332,6 +1375,17 @@
 		check_link(dev);
 		spin_unlock_irq(&np->lock);
 	}
+	if (np->oom) {
+		disable_irq(dev->irq);
+		np->oom = 0;
+		refill_rx(dev);
+		enable_irq(dev->irq);
+		if (!np->oom) {
+			writel(RxOn, dev->base_addr + ChipCmd);
+		} else {
+			next_tick = 1;
+		}
+	}
 	mod_timer(&np->timer, jiffies + next_tick);
 }
 
@@ -1344,16 +1398,16 @@
 		printk(KERN_DEBUG "  Tx ring at %p:\n", np->tx_ring);
 		for (i = 0; i < TX_RING_SIZE; i++) {
 			printk(KERN_DEBUG " #%d desc. %#08x %#08x %#08x.\n",
-				   i, np->tx_ring[i].next_desc,
-				   np->tx_ring[i].cmd_status, 
-				   np->tx_ring[i].addr);
+				i, np->tx_ring[i].next_desc,
+				np->tx_ring[i].cmd_status,
+				np->tx_ring[i].addr);
 		}
 		printk(KERN_DEBUG "  Rx ring %p:\n", np->rx_ring);
 		for (i = 0; i < RX_RING_SIZE; i++) {
 			printk(KERN_DEBUG " #%d desc. %#08x %#08x %#08x.\n",
-				   i, np->rx_ring[i].next_desc,
-				   np->rx_ring[i].cmd_status, 
-				   np->rx_ring[i].addr);
+				i, np->rx_ring[i].next_desc,
+				np->rx_ring[i].cmd_status,
+				np->rx_ring[i].addr);
 		}
 	}
 }
@@ -1365,22 +1419,21 @@
 
 	disable_irq(dev->irq);
 	spin_lock_irq(&np->lock);
-	if (netif_device_present(dev)) {
+	if (!np->hands_off) {
 		if (netif_msg_tx_err(np))
-			printk(KERN_WARNING 
+			printk(KERN_WARNING
 				"%s: Transmit timed out, status %#08x,"
-				" resetting...\n", 
+				" resetting...\n",
 				dev->name, readl(ioaddr + IntrStatus));
 		dump_ring(dev);
 
 		natsemi_reset(dev);
-		drain_ring(dev);
-		init_ring(dev);
+		reinit_ring(dev);
 		init_registers(dev);
 	} else {
-		printk(KERN_WARNING 
-			"%s: tx_timeout while in suspended state?\n",
-		   	dev->name);
+		printk(KERN_WARNING
+			"%s: tx_timeout while in hands_off state?\n",
+			dev->name);
 	}
 	spin_unlock_irq(&np->lock);
 	enable_irq(dev->irq);
@@ -1394,24 +1447,62 @@
 {
 	struct netdev_private *np = dev->priv;
 	np->rx_ring = pci_alloc_consistent(np->pci_dev,
-	    sizeof(struct netdev_desc) * (RX_RING_SIZE+TX_RING_SIZE),
-	    &np->ring_dma);
+		sizeof(struct netdev_desc) * (RX_RING_SIZE+TX_RING_SIZE),
+		&np->ring_dma);
 	if (!np->rx_ring)
 		return -ENOMEM;
 	np->tx_ring = &np->rx_ring[RX_RING_SIZE];
 	return 0;
 }
 
+static void refill_rx(struct net_device *dev)
+{
+	struct netdev_private *np = dev->priv;
+
+	/* Refill the Rx ring buffers. */
+	for (; np->cur_rx - np->dirty_rx > 0; np->dirty_rx++) {
+		struct sk_buff *skb;
+		int entry = np->dirty_rx % RX_RING_SIZE;
+		if (np->rx_skbuff[entry] == NULL) {
+			skb = dev_alloc_skb(np->rx_buf_sz);
+			np->rx_skbuff[entry] = skb;
+			if (skb == NULL)
+				break; /* Better luck next round. */
+			skb->dev = dev; /* Mark as being used by this device. */
+			np->rx_dma[entry] = pci_map_single(np->pci_dev,
+				skb->data, skb->len, PCI_DMA_FROMDEVICE);
+			np->rx_ring[entry].addr = cpu_to_le32(np->rx_dma[entry]);
+		}
+		np->rx_ring[entry].cmd_status = cpu_to_le32(np->rx_buf_sz);
+	}
+	if (np->cur_rx - np->dirty_rx == RX_RING_SIZE) {
+		if (netif_msg_rx_err(np))
+			printk(KERN_WARNING "%s: going OOM.\n", dev->name);
+		np->oom = 1;
+	}
+}
+
 /* Initialize the Rx and Tx rings, along with various 'dev' bits. */
 static void init_ring(struct net_device *dev)
 {
 	struct netdev_private *np = dev->priv;
 	int i;
 
-	np->cur_rx = np->cur_tx = 0;
-	np->dirty_rx = np->dirty_tx = 0;
+	/* 1) TX ring */
+	np->dirty_tx = np->cur_tx = 0;
+	for (i = 0; i < TX_RING_SIZE; i++) {
+		np->tx_skbuff[i] = NULL;
+		np->tx_ring[i].next_desc = cpu_to_le32(np->ring_dma
+			+sizeof(struct netdev_desc)
+			*((i+1)%TX_RING_SIZE+RX_RING_SIZE));
+		np->tx_ring[i].cmd_status = 0;
+	}
 
+	/* 2) RX ring */
+	np->dirty_rx = 0;
+	np->cur_rx = RX_RING_SIZE;
 	np->rx_buf_sz = (dev->mtu <= 1500 ? PKT_BUF_SZ : dev->mtu + 32);
+	np->oom = 0;
 	np->rx_head_desc = &np->rx_ring[0];
 
 	/* Please be carefull before changing this loop - at least gcc-2.95.1
@@ -1425,29 +1516,25 @@
 		np->rx_ring[i].cmd_status = cpu_to_le32(DescOwn);
 		np->rx_skbuff[i] = NULL;
 	}
+	refill_rx(dev);
+	dump_ring(dev);
+}
 
-	/* Fill in the Rx buffers.  Handle allocation failure gracefully. */
-	for (i = 0; i < RX_RING_SIZE; i++) {
-		struct sk_buff *skb = dev_alloc_skb(np->rx_buf_sz);
-		np->rx_skbuff[i] = skb;
-		if (skb == NULL)
-			break;
-		skb->dev = dev;			/* Mark as being used by this device. */
-		np->rx_dma[i] = pci_map_single(np->pci_dev,
-						skb->data, skb->len, PCI_DMA_FROMDEVICE);
-		np->rx_ring[i].addr = cpu_to_le32(np->rx_dma[i]);
-		np->rx_ring[i].cmd_status = cpu_to_le32(np->rx_buf_sz);
-	}
-	np->dirty_rx = (unsigned int)(i - RX_RING_SIZE);
+static void drain_tx(struct net_device *dev)
+{
+	struct netdev_private *np = dev->priv;
+	int i;
 
 	for (i = 0; i < TX_RING_SIZE; i++) {
+		if (np->tx_skbuff[i]) {
+			pci_unmap_single(np->pci_dev,
+				np->rx_dma[i], np->rx_skbuff[i]->len,
+				PCI_DMA_TODEVICE);
+			dev_kfree_skb(np->tx_skbuff[i]);
+			np->stats.tx_dropped++;
+		}
 		np->tx_skbuff[i] = NULL;
-		np->tx_ring[i].next_desc = cpu_to_le32(np->ring_dma
-					+sizeof(struct netdev_desc)
-					 *((i+1)%TX_RING_SIZE+RX_RING_SIZE));
-		np->tx_ring[i].cmd_status = 0;
 	}
-	dump_ring(dev);
 }
 
 static void drain_ring(struct net_device *dev)
@@ -1461,31 +1548,43 @@
 		np->rx_ring[i].addr = 0xBADF00D0; /* An invalid address. */
 		if (np->rx_skbuff[i]) {
 			pci_unmap_single(np->pci_dev,
-						np->rx_dma[i],
-						np->rx_skbuff[i]->len,
-						PCI_DMA_FROMDEVICE);
+				np->rx_dma[i], np->rx_skbuff[i]->len,
+				PCI_DMA_FROMDEVICE);
 			dev_kfree_skb(np->rx_skbuff[i]);
 		}
 		np->rx_skbuff[i] = NULL;
 	}
-	for (i = 0; i < TX_RING_SIZE; i++) {
-		if (np->tx_skbuff[i]) {
-			pci_unmap_single(np->pci_dev,
-						np->rx_dma[i],
-						np->rx_skbuff[i]->len,
-						PCI_DMA_TODEVICE);
-			dev_kfree_skb(np->tx_skbuff[i]);
-		}
-		np->tx_skbuff[i] = NULL;
-	}
+	drain_tx(dev);
 }
 
 static void free_ring(struct net_device *dev)
 {
 	struct netdev_private *np = dev->priv;
 	pci_free_consistent(np->pci_dev,
-				sizeof(struct netdev_desc) * (RX_RING_SIZE+TX_RING_SIZE),
-				np->rx_ring, np->ring_dma);
+		sizeof(struct netdev_desc) * (RX_RING_SIZE+TX_RING_SIZE),
+		np->rx_ring, np->ring_dma);
+}
+
+static void reinit_ring(struct net_device *dev)
+{
+	struct netdev_private *np = dev->priv;
+	int i;
+
+	/* drain TX ring */
+	drain_tx(dev);
+	np->dirty_tx = np->cur_tx = 0;
+	for (i=0;i<TX_RING_SIZE;i++)
+		np->tx_ring[i].cmd_status = 0;
+
+	/* RX Ring */
+	np->dirty_rx = 0;
+	np->cur_rx = RX_RING_SIZE;
+	np->rx_head_desc = &np->rx_ring[0];
+	/* Initialize all Rx descriptors. */
+	for (i = 0; i < RX_RING_SIZE; i++)
+		np->rx_ring[i].cmd_status = cpu_to_le32(DescOwn);
+
+	refill_rx(dev);
 }
 
 static int start_tx(struct sk_buff *skb, struct net_device *dev)
@@ -1506,10 +1605,10 @@
 	np->tx_ring[entry].addr = cpu_to_le32(np->tx_dma[entry]);
 
 	spin_lock_irq(&np->lock);
-	
-	if (netif_device_present(dev)) {
+
+	if (!np->hands_off) {
 		np->tx_ring[entry].cmd_status = cpu_to_le32(DescOwn | skb->len);
-		/* StrongARM: Explicitly cache flush np->tx_ring and 
+		/* StrongARM: Explicitly cache flush np->tx_ring and
 		 * skb->data,skb->len. */
 		wmb();
 		np->cur_tx++;
@@ -1530,7 +1629,7 @@
 
 	if (netif_msg_tx_queued(np)) {
 		printk(KERN_DEBUG "%s: Transmit frame #%d queued in slot %d.\n",
-			   dev->name, np->cur_tx, entry);
+			dev->name, np->cur_tx, entry);
 	}
 	return 0;
 }
@@ -1544,7 +1643,7 @@
 		if (np->tx_ring[entry].cmd_status & cpu_to_le32(DescOwn))
 			break;
 		if (netif_msg_tx_done(np))
-			printk(KERN_DEBUG 
+			printk(KERN_DEBUG
 				"%s: tx frame #%d finished, status %#08x.\n",
 					dev->name, np->dirty_tx,
 					le32_to_cpu(np->tx_ring[entry].cmd_status));
@@ -1552,14 +1651,15 @@
 			np->stats.tx_packets++;
 			np->stats.tx_bytes += np->tx_skbuff[entry]->len;
 		} else { /* Various Tx errors */
-			int tx_status = le32_to_cpu(np->tx_ring[entry].cmd_status);
-			if (tx_status & (DescTxAbort|DescTxExcColl)) 
+			int tx_status =
+				le32_to_cpu(np->tx_ring[entry].cmd_status);
+			if (tx_status & (DescTxAbort|DescTxExcColl))
 				np->stats.tx_aborted_errors++;
-			if (tx_status & DescTxFIFO) 
+			if (tx_status & DescTxFIFO)
 				np->stats.tx_fifo_errors++;
-			if (tx_status & DescTxCarrier) 
+			if (tx_status & DescTxCarrier)
 				np->stats.tx_carrier_errors++;
-			if (tx_status & DescTxOOWCol) 
+			if (tx_status & DescTxOOWCol)
 				np->stats.tx_window_errors++;
 			np->stats.tx_errors++;
 		}
@@ -1586,15 +1686,17 @@
 	long ioaddr = dev->base_addr;
 	int boguscnt = max_interrupt_work;
 
-	if (!netif_device_present(dev))
+	if (np->hands_off)
 		return;
 	do {
 		/* Reading automatically acknowledges all int sources. */
 		u32 intr_status = readl(ioaddr + IntrStatus);
 
 		if (netif_msg_intr(np))
-			printk(KERN_DEBUG "%s: Interrupt, status %#08x.\n",
-				   dev->name, intr_status);
+			printk(KERN_DEBUG
+				"%s: Interrupt, status %#08x, mask %#08x.\n",
+				dev->name, intr_status,
+				readl(ioaddr + IntrMask));
 
 		if (intr_status == 0)
 			break;
@@ -1618,9 +1720,10 @@
 
 		if (--boguscnt < 0) {
 			if (netif_msg_intr(np))
-				printk(KERN_WARNING 
-				    "%s: Too much work at interrupt, "
-				    "status=%#08x.\n", dev->name, intr_status);
+				printk(KERN_WARNING
+					"%s: Too much work at interrupt, "
+					"status=%#08x.\n",
+					dev->name, intr_status);
 			break;
 		}
 	} while (1);
@@ -1639,9 +1742,9 @@
 	s32 desc_status = le32_to_cpu(np->rx_head_desc->cmd_status);
 
 	/* If the driver owns the next entry it's a new packet. Send it up. */
-	while (desc_status < 0) {        /* e.g. & DescOwn */
+	while (desc_status < 0) { /* e.g. & DescOwn */
 		if (netif_msg_rx_status(np))
-			printk(KERN_DEBUG 
+			printk(KERN_DEBUG
 				"  netdev_rx() entry %d status was %#08x.\n",
 				entry, desc_status);
 		if (--boguscnt < 0)
@@ -1649,54 +1752,56 @@
 		if ((desc_status&(DescMore|DescPktOK|DescRxLong)) != DescPktOK){
 			if (desc_status & DescMore) {
 				if (netif_msg_rx_err(np))
-					printk(KERN_WARNING 
+					printk(KERN_WARNING
 						"%s: Oversized(?) Ethernet "
 						"frame spanned multiple "
 						"buffers, entry %#08x "
-						"status %#08x.\n", dev->name, 
+						"status %#08x.\n", dev->name,
 						np->cur_rx, desc_status);
 				np->stats.rx_length_errors++;
 			} else {
 				/* There was an error. */
 				np->stats.rx_errors++;
-				if (desc_status & (DescRxAbort|DescRxOver)) 
+				if (desc_status & (DescRxAbort|DescRxOver))
 					np->stats.rx_over_errors++;
-				if (desc_status & (DescRxLong|DescRxRunt)) 
+				if (desc_status & (DescRxLong|DescRxRunt))
 					np->stats.rx_length_errors++;
-				if (desc_status & (DescRxInvalid|DescRxAlign)) 
+				if (desc_status & (DescRxInvalid|DescRxAlign))
 					np->stats.rx_frame_errors++;
-				if (desc_status & DescRxCRC) 
+				if (desc_status & DescRxCRC)
 					np->stats.rx_crc_errors++;
 			}
 		} else {
 			struct sk_buff *skb;
 			/* Omit CRC size. */
 			int pkt_len = (desc_status & DescSizeMask) - 4;
-			/* Check if the packet is long enough to accept 
+			/* Check if the packet is long enough to accept
 			 * without copying to a minimally-sized skbuff. */
 			if (pkt_len < rx_copybreak
-				&& (skb = dev_alloc_skb(pkt_len + 2)) != NULL) {
+			    && (skb = dev_alloc_skb(pkt_len + 2)) != NULL) {
 				skb->dev = dev;
-				skb_reserve(skb, 2);	/* 16 byte align the IP header */
-				pci_dma_sync_single(np->pci_dev, np->rx_dma[entry],
-							np->rx_skbuff[entry]->len,
-							PCI_DMA_FROMDEVICE);
+				/* 16 byte align the IP header */
+				skb_reserve(skb, 2);
+				pci_dma_sync_single(np->pci_dev,
+					np->rx_dma[entry],
+					np->rx_skbuff[entry]->len,
+					PCI_DMA_FROMDEVICE);
 #if HAS_IP_COPYSUM
-				eth_copy_and_sum(skb, np->rx_skbuff[entry]->tail, pkt_len, 0);
+				eth_copy_and_sum(skb,
+					np->rx_skbuff[entry]->tail, pkt_len, 0);
 				skb_put(skb, pkt_len);
 #else
-				memcpy(skb_put(skb, pkt_len), np->rx_skbuff[entry]->tail,
-					   pkt_len);
+				memcpy(skb_put(skb, pkt_len),
+					np->rx_skbuff[entry]->tail, pkt_len);
 #endif
 			} else {
 				pci_unmap_single(np->pci_dev, np->rx_dma[entry],
-							np->rx_skbuff[entry]->len,
-							PCI_DMA_FROMDEVICE);
+					np->rx_skbuff[entry]->len,
+					PCI_DMA_FROMDEVICE);
 				skb_put(skb = np->rx_skbuff[entry], pkt_len);
 				np->rx_skbuff[entry] = NULL;
 			}
 			skb->protocol = eth_type_trans(skb, dev);
-			/* W/ hardware checksum: skb->ip_summed = CHECKSUM_UNNECESSARY; */
 			netif_rx(skb);
 			dev->last_rx = jiffies;
 			np->stats.rx_packets++;
@@ -1706,27 +1811,13 @@
 		np->rx_head_desc = &np->rx_ring[entry];
 		desc_status = le32_to_cpu(np->rx_head_desc->cmd_status);
 	}
-
-	/* Refill the Rx ring buffers. */
-	for (; np->cur_rx - np->dirty_rx > 0; np->dirty_rx++) {
-		struct sk_buff *skb;
-		entry = np->dirty_rx % RX_RING_SIZE;
-		if (np->rx_skbuff[entry] == NULL) {
-			skb = dev_alloc_skb(np->rx_buf_sz);
-			np->rx_skbuff[entry] = skb;
-			if (skb == NULL)
-				break;				/* Better luck next round. */
-			skb->dev = dev;			/* Mark as being used by this device. */
-			np->rx_dma[entry] = pci_map_single(np->pci_dev,
-							skb->data, skb->len, PCI_DMA_FROMDEVICE);
-			np->rx_ring[entry].addr = cpu_to_le32(np->rx_dma[entry]);
-		}
-		np->rx_ring[entry].cmd_status =
-			cpu_to_le32(np->rx_buf_sz);
-	}
+	refill_rx(dev);
 
 	/* Restart Rx engine if stopped. */
-	writel(RxOn, dev->base_addr + ChipCmd);
+	if (np->oom)
+		mod_timer(&np->timer, jiffies + 1);
+	else
+		writel(RxOn, dev->base_addr + ChipCmd);
 }
 
 static void netdev_error(struct net_device *dev, int intr_status)
@@ -1738,9 +1829,9 @@
 	if (intr_status & LinkChange) {
 		u16 adv = mdio_read(dev, 1, MII_ADVERTISE);
 		u16 lpa = mdio_read(dev, 1, MII_LPA);
-		if (mdio_read(dev, 1, MII_BMCR) & BMCR_ANENABLE 
+		if (mdio_read(dev, 1, MII_BMCR) & BMCR_ANENABLE
 		 && netif_msg_link(np)) {
-			printk(KERN_INFO 
+			printk(KERN_INFO
 				"%s: Autonegotiation advertising"
 				" %#04x  partner %#04x.\n", dev->name,
 				adv, lpa);
@@ -1765,7 +1856,7 @@
 	if (intr_status & WOLPkt && netif_msg_wol(np)) {
 		int wol_status = readl(ioaddr + WOLCmd);
 		printk(KERN_NOTICE "%s: Link wake-up event %#08x\n",
-			   dev->name, wol_status);
+			dev->name, wol_status);
 	}
 	if (intr_status & RxStatusFIFOOver) {
 		if (netif_msg_rx_err(np) && netif_msg_intr(np)) {
@@ -1800,8 +1891,8 @@
 
 	/* The chip only need report frame silently dropped. */
 	spin_lock_irq(&np->lock);
- 	if (netif_running(dev) && netif_device_present(dev))
- 		__get_stats(dev);
+	if (netif_running(dev) && !np->hands_off)
+		__get_stats(dev);
 	spin_unlock_irq(&np->lock);
 
 	return &np->stats;
@@ -1817,27 +1908,27 @@
 #define DP_POLYNOMIAL			0x04C11DB7
 static unsigned dp83815_crc(int length, unsigned char *data)
 {
-    u32 crc;
-    u8 cur_byte;
-    u8 msb;
-    u8 byte, bit;
-
-    crc = ~0;
-    for (byte=0; byte<length; byte++) {
-        cur_byte = *data++;
-        for (bit=0; bit<8; bit++) {
-            msb = crc >> 31;
-            crc <<= 1;
-            if (msb ^ (cur_byte & 1)) {
-                crc ^= DP_POLYNOMIAL;
-                crc |= 1;
-            }
-            cur_byte >>= 1;
-        }
-    }
-    crc >>= 23;
+	u32 crc;
+	u8 cur_byte;
+	u8 msb;
+	u8 byte, bit;
+
+	crc = ~0;
+	for (byte=0; byte<length; byte++) {
+		cur_byte = *data++;
+		for (bit=0; bit<8; bit++) {
+			msb = crc >> 31;
+			crc <<= 1;
+			if (msb ^ (cur_byte & 1)) {
+				crc ^= DP_POLYNOMIAL;
+				crc |= 1;
+			}
+			cur_byte >>= 1;
+		}
+	}
+	crc >>= 23;
 
-    return (crc);
+	return (crc);
 }
 
 
@@ -1850,18 +1941,18 @@
 {
 	long ioaddr = dev->base_addr;
 	struct netdev_private *np = dev->priv;
-	u8 mc_filter[64];			/* Multicast hash filter */
+	u8 mc_filter[64]; /* Multicast hash filter */
 	u32 rx_mode;
 
-	if (dev->flags & IFF_PROMISC) {			/* Set promiscuous. */
+	if (dev->flags & IFF_PROMISC) { /* Set promiscuous. */
 		/* Unconditionally log net taps. */
-		printk(KERN_NOTICE "%s: Promiscuous mode enabled.\n", 
+		printk(KERN_NOTICE "%s: Promiscuous mode enabled.\n",
 			dev->name);
-		rx_mode = RxFilterEnable | AcceptBroadcast 
+		rx_mode = RxFilterEnable | AcceptBroadcast
 			| AcceptAllMulticast | AcceptAllPhys | AcceptMyPhys;
 	} else if ((dev->mc_count > multicast_filter_limit)
-			   ||  (dev->flags & IFF_ALLMULTI)) {
-		rx_mode = RxFilterEnable | AcceptBroadcast 
+	  || (dev->flags & IFF_ALLMULTI)) {
+		rx_mode = RxFilterEnable | AcceptBroadcast
 			| AcceptAllMulticast | AcceptMyPhys;
 	} else {
 		struct dev_mc_list *mclist;
@@ -1869,14 +1960,15 @@
 		memset(mc_filter, 0, sizeof(mc_filter));
 		for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count;
 			 i++, mclist = mclist->next) {
-			set_bit_le(dp83815_crc(ETH_ALEN, mclist->dmi_addr) & 0x1ff,
-					mc_filter);
+			set_bit_le(
+				dp83815_crc(ETH_ALEN, mclist->dmi_addr) & 0x1ff,
+				mc_filter);
 		}
-		rx_mode = RxFilterEnable | AcceptBroadcast 
+		rx_mode = RxFilterEnable | AcceptBroadcast
 			| AcceptMulticast | AcceptMyPhys;
 		for (i = 0; i < 64; i += 2) {
 			writew(HASH_TABLE + i, ioaddr + RxFilterAddr);
-			writew((mc_filter[i+1]<<8) + mc_filter[i], 
+			writew((mc_filter[i+1]<<8) + mc_filter[i],
 				ioaddr + RxFilterData);
 		}
 	}
@@ -1888,7 +1980,7 @@
 {
 	struct netdev_private *np = dev->priv;
 	spin_lock_irq(&np->lock);
-	if (netif_device_present(dev))
+	if (!np->hands_off)
 		__set_rx_mode(dev);
 	spin_unlock_irq(&np->lock);
 }
@@ -1897,18 +1989,18 @@
 {
 	struct netdev_private *np = dev->priv;
 	u32 cmd;
-	
+
 	if (get_user(cmd, (u32 *)useraddr))
 		return -EFAULT;
 
-        switch (cmd) {
+	switch (cmd) {
 	/* get driver info */
-        case ETHTOOL_GDRVINFO: {
+	case ETHTOOL_GDRVINFO: {
 		struct ethtool_drvinfo info = {ETHTOOL_GDRVINFO};
 		strncpy(info.driver, DRV_NAME, ETHTOOL_BUSINFO_LEN);
 		strncpy(info.version, DRV_VERSION, ETHTOOL_BUSINFO_LEN);
 		info.fw_version[0] = '\0';
-		strncpy(info.bus_info, np->pci_dev->slot_name, 
+		strncpy(info.bus_info, np->pci_dev->slot_name,
 			ETHTOOL_BUSINFO_LEN);
 		info.eedump_len = NATSEMI_EEPROM_SIZE;
 		info.regdump_len = NATSEMI_REGS_SIZE;
@@ -1968,7 +2060,7 @@
 
 		if (copy_from_user(&regs, useraddr, sizeof(regs)))
 			return -EFAULT;
-		
+
 		if (regs.len > NATSEMI_REGS_SIZE) {
 			regs.len = NATSEMI_REGS_SIZE;
 		}
@@ -2035,7 +2127,7 @@
 
 		if (copy_from_user(&eeprom, useraddr, sizeof(eeprom)))
 			return -EFAULT;
-		
+
 		if (eeprom.offset > eeprom.offset+eeprom.len)
 			return -EINVAL;
 
@@ -2059,8 +2151,8 @@
 		return 0;
 	}
 
-        }
-	
+	}
+
 	return -EOPNOTSUPP;
 }
 
@@ -2082,7 +2174,7 @@
 		data |= WakeArp;
 	if (newval & WAKE_MAGIC)
 		data |= WakeMagic;
-	if (np->srr >= SRR_REV_D) {
+	if (np->srr >= SRR_DP83815_D) {
 		if (newval & WAKE_MAGICSECURE) {
 			data |= WakeMagicSecure;
 		}
@@ -2098,10 +2190,10 @@
 	struct netdev_private *np = dev->priv;
 	u32 regval = readl(dev->base_addr + WOLCmd);
 
-	*supported = (WAKE_PHY | WAKE_UCAST | WAKE_MCAST | WAKE_BCAST 
+	*supported = (WAKE_PHY | WAKE_UCAST | WAKE_MCAST | WAKE_BCAST
 			| WAKE_ARP | WAKE_MAGIC);
-	
-	if (np->srr >= SRR_REV_D) {
+
+	if (np->srr >= SRR_DP83815_D) {
 		/* SOPASS works on revD and higher */
 		*supported |= WAKE_MAGICSECURE;
 	}
@@ -2133,8 +2225,8 @@
 	struct netdev_private *np = dev->priv;
 	u16 *sval = (u16 *)newval;
 	u32 addr;
-	
-	if (np->srr < SRR_REV_D) {
+
+	if (np->srr < SRR_DP83815_D) {
 		return 0;
 	}
 
@@ -2149,10 +2241,10 @@
 
 	writel(addr | 0xc, dev->base_addr + RxFilterAddr);
 	writew(sval[1], dev->base_addr + RxFilterData);
-	
+
 	writel(addr | 0xe, dev->base_addr + RxFilterAddr);
 	writew(sval[2], dev->base_addr + RxFilterData);
-	
+
 	/* re-enable the RX filter */
 	writel(addr | RxFilterEnable, dev->base_addr + RxFilterAddr);
 
@@ -2165,7 +2257,7 @@
 	u16 *sval = (u16 *)data;
 	u32 addr;
 
-	if (np->srr < SRR_REV_D) {
+	if (np->srr < SRR_DP83815_D) {
 		sval[0] = sval[1] = sval[2] = 0;
 		return 0;
 	}
@@ -2178,10 +2270,10 @@
 
 	writel(addr | 0xc, dev->base_addr + RxFilterAddr);
 	sval[1] = readw(dev->base_addr + RxFilterData);
-	
+
 	writel(addr | 0xe, dev->base_addr + RxFilterAddr);
 	sval[2] = readw(dev->base_addr + RxFilterData);
-	
+
 	writel(addr, dev->base_addr + RxFilterAddr);
 
 	return 0;
@@ -2191,11 +2283,11 @@
 {
 	u32 tmp;
 
-	ecmd->supported = 
+	ecmd->supported =
 		(SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full |
 		SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full |
 		SUPPORTED_Autoneg | SUPPORTED_TP | SUPPORTED_MII);
-	
+
 	/* only supports twisted-pair or MII */
 	tmp = readl(dev->base_addr + ChipConfig);
 	if (tmp & CfgExtPhy)
@@ -2234,7 +2326,7 @@
 	} else {
 		ecmd->speed = SPEED_10;
 	}
-		
+
 	if (tmp & CfgFullDuplex) {
 		ecmd->duplex = DUPLEX_FULL;
 	} else {
@@ -2262,9 +2354,9 @@
 	if (ecmd->autoneg != AUTONEG_DISABLE && ecmd->autoneg != AUTONEG_ENABLE)
 		return -EINVAL;
 	/* ignore phy_address, maxtxpkt, maxrxpkt for now */
-	
+
 	/* WHEW! now lets bang some bits */
-	
+
 	tmp = mdio_read(dev, 1, MII_BMCR);
 	if (ecmd->autoneg == AUTONEG_ENABLE) {
 		/* turn on autonegotiation */
@@ -2290,7 +2382,7 @@
 	int j;
 	u32 rfcr;
 	u32 *rbuf = (u32 *)buf;
-	
+
 	/* read all of page 0 of registers */
 	for (i = 0; i < NATSEMI_PG0_NREGS; i++) {
 		rbuf[i] = readl(dev->base_addr + i*4);
@@ -2314,8 +2406,8 @@
 
 	/* the interrupt status is clear-on-read - see if we missed any */
 	if (rbuf[4] & rbuf[5]) {
-		printk(KERN_WARNING 
-			"%s: shoot, we dropped an interrupt (%#08x)\n", 
+		printk(KERN_WARNING
+			"%s: shoot, we dropped an interrupt (%#08x)\n",
 			dev->name, rbuf[4] & rbuf[5]);
 	}
 
@@ -2339,7 +2431,7 @@
 	/* eeprom_read reads 16 bits, and indexes by 16 bits */
 	for (i = 0; i < NATSEMI_EEPROM_SIZE/2; i++) {
 		ebuf[i] = eeprom_read(dev->base_addr, i);
-		/* The EEPROM itself stores data bit-swapped, but eeprom_read 
+		/* The EEPROM itself stores data bit-swapped, but eeprom_read
 		 * reads it back "sanely". So we swap it back here in order to
 		 * present it to userland as it is stored. */
 		ebuf[i] = SWAP_BITS(ebuf[i]);
@@ -2361,7 +2453,7 @@
 
 	case SIOCGMIIREG:		/* Read MII PHY register. */
 	case SIOCDEVPRIVATE+1:		/* for binary compat, remove in 2.5 */
-		data->val_out = mdio_read(dev, data->phy_id & 0x1f, 
+		data->val_out = mdio_read(dev, data->phy_id & 0x1f,
 			data->reg_num & 0x1f);
 		return 0;
 
@@ -2369,7 +2461,7 @@
 	case SIOCDEVPRIVATE+2:		/* for binary compat, remove in 2.5 */
 		if (!capable(CAP_NET_ADMIN))
 			return -EPERM;
-		mdio_write(dev, data->phy_id & 0x1f, data->reg_num & 0x1f, 
+		mdio_write(dev, data->phy_id & 0x1f, data->reg_num & 0x1f,
 			data->val_in);
 		return 0;
 	default:
@@ -2383,7 +2475,7 @@
 	struct netdev_private *np = dev->priv;
 
 	if (netif_msg_wol(np))
-		printk(KERN_INFO "%s: remaining active for wake-on-lan\n", 
+		printk(KERN_INFO "%s: remaining active for wake-on-lan\n",
 			dev->name);
 
 	/* For WOL we must restart the rx process in silent mode.
@@ -2415,51 +2507,56 @@
 	long ioaddr = dev->base_addr;
 	struct netdev_private *np = dev->priv;
 
-	netif_stop_queue(dev);
-	netif_carrier_off(dev);
-
 	if (netif_msg_ifdown(np))
-		printk(KERN_DEBUG 
+		printk(KERN_DEBUG
 			"%s: Shutting down ethercard, status was %#04x.\n",
 			dev->name, (int)readl(ioaddr + ChipCmd));
 	if (netif_msg_pktdata(np))
-		printk(KERN_DEBUG 
+		printk(KERN_DEBUG
 			"%s: Queue pointers were Tx %d / %d,  Rx %d / %d.\n",
-			dev->name, np->cur_tx, np->dirty_tx, 
+			dev->name, np->cur_tx, np->dirty_tx,
 			np->cur_rx, np->dirty_rx);
 
-	del_timer_sync(&np->timer);
+	/*
+	 * FIXME: what if someone tries to close a device
+	 * that is suspended?
+	 * Should we reenable the nic to switch to
+	 * the final WOL settings?
+	 */
 
+	del_timer_sync(&np->timer);
 	disable_irq(dev->irq);
 	spin_lock_irq(&np->lock);
-
-	/* Disable and clear interrupts */
+	/* Disable interrupts, and flush posted writes */
 	writel(0, ioaddr + IntrEnable);
+	readl(ioaddr + IntrEnable);
+	np->hands_off = 1;
+	spin_unlock_irq(&np->lock);
+	enable_irq(dev->irq);
+
+	free_irq(dev->irq, dev);
+
+	/* Interrupt disabled, interrupt handler released,
+	 * queue stopped, timer deleted, rtnl_lock held
+	 * All async codepaths that access the driver are disabled.
+	 */
+	spin_lock_irq(&np->lock);
+	np->hands_off = 0;
 	readl(ioaddr + IntrMask);
 	readw(ioaddr + MIntrStatus);
 
- 	/* Freeze Stats */
+	/* Freeze Stats */
 	writel(StatsFreeze, ioaddr + StatsCtrl);
-	    
+
 	/* Stop the chip's Tx and Rx processes. */
 	natsemi_stop_rxtx(dev);
 
 	__get_stats(dev);
 	spin_unlock_irq(&np->lock);
 
-	/* race: shared irq and as most nics the DP83815
-	 * reports _all_ interrupt conditions in IntrStatus, even
-	 * disabled ones.
-	 * packet received after disable_irq, but before stop_rxtx
-	 * --> race. intr_handler would restart the rx process.
-	 * netif_device_{de,a}tach around {enable,free}_irq.
-	 */
-	netif_device_detach(dev);
-	enable_irq(dev->irq);
-	free_irq(dev->irq, dev);
-	netif_device_attach(dev);
 	/* clear the carrier last - an interrupt could reenable it otherwise */
 	netif_carrier_off(dev);
+	netif_stop_queue(dev);
 
 	dump_ring(dev);
 	drain_ring(dev);
@@ -2480,7 +2577,7 @@
 	return 0;
 }
 
-
+
 static void __devexit natsemi_remove1 (struct pci_dev *pdev)
 {
 	struct net_device *dev = pci_get_drvdata(pdev);
@@ -2495,23 +2592,26 @@
 #ifdef CONFIG_PM
 
 /*
+ * The ns83815 chip doesn't have explicit RxStop bits.
+ * Kicking the Rx or Tx process for a new packet reenables the Rx process
+ * of the nic, thus this function must be very careful:
+ *
  * suspend/resume synchronization:
  * entry points:
  *   netdev_open, netdev_close, netdev_ioctl, set_rx_mode, intr_handler,
  *   start_tx, tx_timeout
- * Reading from some registers can restart the nic!
- * No function accesses the hardware without checking netif_device_present().
- * 	the check occurs under spin_lock_irq(&np->lock);
+ *
+ * No function accesses the hardware without checking np->hands_off.
+ *	the check occurs under spin_lock_irq(&np->lock);
  * exceptions:
- * 	* netdev_ioctl, netdev_open.
- * 		net/core checks netif_device_present() before calling them.
- * 	* netdev_close: doesn't hurt.
+ *	* netdev_ioctl: noncritical access.
+ *	* netdev_open: cannot happen due to the device_detach
+ *	* netdev_close: doesn't hurt.
  *	* netdev_timer: timer stopped by natsemi_suspend.
  *	* intr_handler: doesn't acquire the spinlock. suspend calls
  *		disable_irq() to enforce synchronization.
  *
- * netif_device_detach must occur under spin_unlock_irq(), interrupts from a
- * detached device would cause an irq storm.
+ * Interrupts must be disabled, otherwise hands_off can cause irq storms.
  */
 
 static int natsemi_suspend (struct pci_dev *pdev, u32 state)
@@ -2528,13 +2628,13 @@
 		spin_lock_irq(&np->lock);
 
 		writel(0, ioaddr + IntrEnable);
+		np->hands_off = 1;
 		natsemi_stop_rxtx(dev);
 		netif_stop_queue(dev);
-		netif_device_detach(dev);
 
 		spin_unlock_irq(&np->lock);
 		enable_irq(dev->irq);
-		
+
 		/* Update the error counts. */
 		__get_stats(dev);
 
@@ -2546,7 +2646,7 @@
 			if (wol) {
 				/* restart the NIC in WOL mode.
 				 * The nic must be stopped for this.
-				 * FIXME: use the WOL interupt 
+				 * FIXME: use the WOL interupt
 				 */
 				enable_wol_mode(dev, 0);
 			} else {
@@ -2554,9 +2654,8 @@
 				writel(np->SavedClkRun, ioaddr + ClkRun);
 			}
 		}
-	} else {
-		netif_device_detach(dev);
 	}
+	netif_device_detach(dev);
 	rtnl_unlock();
 	return 0;
 }
@@ -2571,20 +2670,23 @@
 	if (netif_device_present(dev))
 		goto out;
 	if (netif_running(dev)) {
+		BUG_ON(!np->hands_off);
 		pci_enable_device(pdev);
 	/*	pci_power_on(pdev); */
-		
+
 		natsemi_reset(dev);
 		init_ring(dev);
+		disable_irq(dev->irq);
 		spin_lock_irq(&np->lock);
+		np->hands_off = 0;
 		init_registers(dev);
 		netif_device_attach(dev);
 		spin_unlock_irq(&np->lock);
+		enable_irq(dev->irq);
 
 		mod_timer(&np->timer, jiffies + 1*HZ);
-	} else {
-		netif_device_attach(dev);
 	}
+	netif_device_attach(dev);
 out:
 	rtnl_unlock();
 	return 0;
diff -Nru a/drivers/net/ns83820.c b/drivers/net/ns83820.c
--- a/drivers/net/ns83820.c	Mon Oct  7 11:25:39 2002
+++ b/drivers/net/ns83820.c	Mon Oct  7 11:25:39 2002
@@ -93,7 +93,7 @@
 #include <linux/etherdevice.h>
 #include <linux/delay.h>
 #include <linux/smp_lock.h>
-#include <linux/tqueue.h>
+#include <linux/workqueue.h>
 #include <linux/init.h>
 #include <linux/ip.h>	/* for iph */
 #include <linux/in.h>	/* for IPPROTO_... */
@@ -411,7 +411,7 @@
 	struct tasklet_struct	rx_tasklet;
 
 	unsigned		ihr;
-	struct tq_struct	tq_refill;
+	struct work_struct	tq_refill;
 
 	/* protects everything below.  irqsave when using. */
 	spinlock_t		misc_lock;
@@ -784,7 +784,7 @@
 	}
 
 	if (dev->rx_info.up && nr_rx_empty(dev) > NR_RX_DESC*3/4)
-		schedule_task(&dev->tq_refill);
+		schedule_work(&dev->tq_refill);
 	else
 		kick_rx(dev);
 	if (dev->rx_info.idle)
@@ -1438,7 +1438,7 @@
 	dev->ee.lock = &dev->misc_lock;
 	dev->net_dev.owner = THIS_MODULE;
 
-	PREPARE_TQUEUE(&dev->tq_refill, queue_refill, dev);
+	INIT_WORK(&dev->tq_refill, queue_refill, dev);
 	tasklet_init(&dev->rx_tasklet, rx_action, (unsigned long)dev);
 
 	err = pci_enable_device(pci_dev);
diff -Nru a/drivers/net/plip.c b/drivers/net/plip.c
--- a/drivers/net/plip.c	Mon Oct  7 11:25:39 2002
+++ b/drivers/net/plip.c	Mon Oct  7 11:25:39 2002
@@ -110,7 +110,7 @@
 #include <linux/if_plip.h>
 #include <net/neighbour.h>
 
-#include <linux/tqueue.h>
+#include <linux/workqueue.h>
 #include <linux/ioport.h>
 #include <linux/spinlock.h>
 #include <asm/bitops.h>
@@ -211,9 +211,9 @@
 
 struct net_local {
 	struct net_device_stats enet_stats;
-	struct tq_struct immediate;
-	struct tq_struct deferred;
-	struct tq_struct timer;
+	struct work_struct immediate;
+	struct work_struct deferred;
+	struct work_struct timer;
 	struct plip_local snd_data;
 	struct plip_local rcv_data;
 	struct pardevice *pardev;
@@ -348,22 +348,11 @@
 	nl->nibble	= PLIP_NIBBLE_WAIT;
 
 	/* Initialize task queue structures */
-	INIT_LIST_HEAD(&nl->immediate.list);
-	nl->immediate.sync = 0;
-	nl->immediate.routine = (void (*)(void *))plip_bh;
-	nl->immediate.data = dev;
-
-	INIT_LIST_HEAD(&nl->deferred.list);
-	nl->deferred.sync = 0;
-	nl->deferred.routine = (void (*)(void *))plip_kick_bh;
-	nl->deferred.data = dev;
-
-	if (dev->irq == -1) {
-		INIT_LIST_HEAD(&nl->timer.list);
-		nl->timer.sync = 0;
-		nl->timer.routine = (void (*)(void *))plip_timer_bh;
-		nl->timer.data = dev;
-	}
+	INIT_WORK(&nl->immediate, (void (*)(void *))plip_bh, dev);
+	INIT_WORK(&nl->deferred, (void (*)(void *))plip_kick_bh, dev);
+
+	if (dev->irq == -1)
+		INIT_WORK(&nl->timer, (void (*)(void *))plip_timer_bh, dev);
 
 	spin_lock_init(&nl->lock);
 
@@ -378,10 +367,8 @@
 {
 	struct net_local *nl = (struct net_local *)dev->priv;
 
-	if (nl->is_deferred) {
-		queue_task(&nl->immediate, &tq_immediate);
-		mark_bh(IMMEDIATE_BH);
-	}
+	if (nl->is_deferred)
+		schedule_work(&nl->immediate);
 }
 
 /* Forward declarations of internal routines */
@@ -432,7 +419,7 @@
 	if ((r = (*f)(dev, nl, snd, rcv)) != OK
 	    && (r = plip_bh_timeout_error(dev, nl, snd, rcv, r)) != OK) {
 		nl->is_deferred = 1;
-		queue_task(&nl->deferred, &tq_timer);
+		schedule_delayed_work(&nl->deferred, 1);
 	}
 }
 
@@ -444,7 +431,7 @@
 	if (!(atomic_read (&nl->kill_timer))) {
 		plip_interrupt (-1, dev, NULL);
 
-		queue_task (&nl->timer, &tq_timer);
+		schedule_delayed_work(&nl->timer, 1);
 	}
 	else {
 		up (&nl->killed_timer_sem);
@@ -665,7 +652,7 @@
 				rcv->state = PLIP_PK_DONE;
 				nl->is_deferred = 1;
 				nl->connection = PLIP_CN_SEND;
-				queue_task(&nl->deferred, &tq_timer);
+				schedule_delayed_work(&nl->deferred, 1);
 				enable_parport_interrupts (dev);
 				ENABLE(dev->irq);
 				return OK;
@@ -740,8 +727,7 @@
 		if (snd->state != PLIP_PK_DONE) {
 			nl->connection = PLIP_CN_SEND;
 			spin_unlock_irq(&nl->lock);
-			queue_task(&nl->immediate, &tq_immediate);
-			mark_bh(IMMEDIATE_BH);
+			schedule_work(&nl->immediate);
 			enable_parport_interrupts (dev);
 			ENABLE(dev->irq);
 			return OK;
@@ -909,7 +895,7 @@
 			printk(KERN_DEBUG "%s: send end\n", dev->name);
 		nl->connection = PLIP_CN_CLOSING;
 		nl->is_deferred = 1;
-		queue_task(&nl->deferred, &tq_timer);
+		schedule_delayed_work(&nl->deferred, 1);
 		enable_parport_interrupts (dev);
 		ENABLE(dev->irq);
 		return OK;
@@ -953,7 +939,7 @@
 		netif_wake_queue (dev);
 	} else {
 		nl->is_deferred = 1;
-		queue_task(&nl->deferred, &tq_timer);
+		schedule_delayed_work(&nl->deferred, 1);
 	}
 
 	return OK;
@@ -997,8 +983,7 @@
 		rcv->state = PLIP_PK_TRIGGER;
 		nl->connection = PLIP_CN_RECEIVE;
 		nl->timeout_count = 0;
-		queue_task(&nl->immediate, &tq_immediate);
-		mark_bh(IMMEDIATE_BH);
+		schedule_work(&nl->immediate);
 		break;
 
 	case PLIP_CN_RECEIVE:
@@ -1051,8 +1036,7 @@
 		nl->connection = PLIP_CN_SEND;
 		nl->timeout_count = 0;
 	}
-	queue_task(&nl->immediate, &tq_immediate);
-	mark_bh(IMMEDIATE_BH);
+	schedule_work(&nl->immediate);
 	spin_unlock_irq(&nl->lock);
 	
 	return 0;
@@ -1131,7 +1115,7 @@
 	if (dev->irq == -1)
 	{
 		atomic_set (&nl->kill_timer, 0);
-		queue_task (&nl->timer, &tq_timer);
+		schedule_delayed_work(&nl->timer, 1);
 	}
 
 	/* Initialize the state machine. */
diff -Nru a/drivers/net/sk98lin/Makefile b/drivers/net/sk98lin/Makefile
--- a/drivers/net/sk98lin/Makefile	Mon Oct  7 11:25:25 2002
+++ b/drivers/net/sk98lin/Makefile	Mon Oct  7 11:25:25 2002
@@ -55,7 +55,7 @@
 # SK_DBGCAT_DRV_INT_SRC         0x04000000      interrupts sources
 # SK_DBGCAT_DRV_EVENT           0x08000000      driver events
 
-EXTRA_CFLAGS += -I. -DSK_USE_CSUM $(DBGDEF)
+EXTRA_CFLAGS += -Idrivers/net/sk98lin -DSK_USE_CSUM $(DBGDEF)
 
 include $(TOPDIR)/Rules.make
 
diff -Nru a/drivers/net/skfp/Makefile b/drivers/net/skfp/Makefile
--- a/drivers/net/skfp/Makefile	Mon Oct  7 11:25:38 2002
+++ b/drivers/net/skfp/Makefile	Mon Oct  7 11:25:38 2002
@@ -17,7 +17,7 @@
 #   projects. To keep the source common for all those drivers (and
 #   thus simplify fixes to it), please do not clean it up!
 
-EXTRA_CFLAGS += -I. -DPCI -DMEM_MAPPED_IO -Wno-strict-prototypes 
+EXTRA_CFLAGS += -Idrivers/net/skfp -DPCI -DMEM_MAPPED_IO -Wno-strict-prototypes 
 
 include $(TOPDIR)/Rules.make
 
diff -Nru a/drivers/net/sungem.c b/drivers/net/sungem.c
--- a/drivers/net/sungem.c	Mon Oct  7 11:25:39 2002
+++ b/drivers/net/sungem.c	Mon Oct  7 11:25:39 2002
@@ -40,7 +40,7 @@
 #include <linux/ethtool.h>
 #include <linux/crc32.h>
 #include <linux/random.h>
-#include <linux/tqueue.h>
+#include <linux/workqueue.h>
 
 #include <asm/system.h>
 #include <asm/bitops.h>
@@ -584,7 +584,7 @@
 
 do_reset:
 	gp->reset_task_pending = 2;
-	schedule_task(&gp->reset_task);
+	schedule_work(&gp->reset_task);
 
 	return 1;
 }
@@ -821,7 +821,7 @@
 	spin_lock_irq(&gp->lock);
 
 	gp->reset_task_pending = 2;
-	schedule_task(&gp->reset_task);
+	schedule_work(&gp->reset_task);
 
 	spin_unlock_irq(&gp->lock);
 }
@@ -975,10 +975,10 @@
 	spin_lock_irq(&gp->lock);
 	dev->mtu = new_mtu;
 	gp->reset_task_pending = 1;
-	schedule_task(&gp->reset_task);
+	schedule_work(&gp->reset_task);
 	spin_unlock_irq(&gp->lock);
 
-	flush_scheduled_tasks();
+	flush_scheduled_work();
 
 	return 0;
 }
@@ -1384,7 +1384,7 @@
 					printk(KERN_INFO "%s: Link down\n",
 						gp->dev->name);
 				gp->reset_task_pending = 2;
-				schedule_task(&gp->reset_task);
+				schedule_work(&gp->reset_task);
 				restart = 1;
 			} else if (++gp->timer_ticks > 10)
 				restart = gem_mdio_link_not_up(gp);
@@ -2299,7 +2299,7 @@
 {
 	struct gem *gp = (struct gem *) data;
 
-	schedule_task(&gp->pm_task);
+	schedule_work(&gp->pm_task);
 }
 
 static int gem_open(struct net_device *dev)
@@ -2313,7 +2313,7 @@
 
 	/* Stop the PM timer/task */
 	del_timer(&gp->pm_timer);
-	flush_scheduled_tasks();
+	flush_scheduled_work();
 
 	/* The power-management semaphore protects the hw_running
 	 * etc. state so it is safe to do this bit without gp->lock
@@ -2445,7 +2445,7 @@
 	if (gp->hw_running) {
 		/* Kill PM timer if any */
 		del_timer_sync(&gp->pm_timer);
-		flush_scheduled_tasks();
+		flush_scheduled_work();
 
 		gem_shutdown(gp);
 	}
@@ -2955,8 +2955,8 @@
 	gp->pm_timer.function = gem_pm_timer;
 	gp->pm_timer.data = (unsigned long) gp;
 
-	INIT_TQUEUE(&gp->pm_task, gem_pm_task, gp);
-	INIT_TQUEUE(&gp->reset_task, gem_reset_task, gp);
+	INIT_WORK(&gp->pm_task, gem_pm_task, gp);
+	INIT_WORK(&gp->reset_task, gem_reset_task, gp);
 	
 	/* Default link parameters */
 	if (link_mode >= 0 && link_mode <= 6)
@@ -3061,7 +3061,7 @@
 	down(&gp->pm_sem);
 	/* Stop the PM timer & task */
 	del_timer_sync(&gp->pm_timer);
-	flush_scheduled_tasks();
+	flush_scheduled_work();
 	if (gp->hw_running)
 		gem_shutdown(gp);
 	up(&gp->pm_sem);
@@ -3090,7 +3090,7 @@
 		down(&gp->pm_sem);
 		/* Stop the PM timer & task */
 		del_timer_sync(&gp->pm_timer);
-		flush_scheduled_tasks();
+		flush_scheduled_work();
 		if (gp->hw_running)
 			gem_shutdown(gp);
 		up(&gp->pm_sem);
diff -Nru a/drivers/net/sungem.h b/drivers/net/sungem.h
--- a/drivers/net/sungem.h	Mon Oct  7 11:25:35 2002
+++ b/drivers/net/sungem.h	Mon Oct  7 11:25:35 2002
@@ -967,7 +967,7 @@
 	int hw_running;
 	int opened;
 	struct semaphore pm_sem;
-	struct tq_struct pm_task;
+	struct work_struct pm_task;
 	struct timer_list pm_timer;
 
 	struct gem_init_block *init_block;
@@ -999,7 +999,7 @@
 	struct timer_list	link_timer;
 	int			timer_ticks;
 	int			wake_on_lan;
-	struct tq_struct	reset_task;
+	struct work_struct			reset_task;
 	volatile int		reset_task_pending;
 	
 	/* Diagnostic counters and state. */
diff -Nru a/drivers/net/tlan.c b/drivers/net/tlan.c
--- a/drivers/net/tlan.c	Mon Oct  7 11:25:26 2002
+++ b/drivers/net/tlan.c	Mon Oct  7 11:25:26 2002
@@ -174,7 +174,7 @@
 #include <linux/etherdevice.h>
 #include <linux/delay.h>
 #include <linux/spinlock.h>
-#include <linux/tqueue.h>
+#include <linux/workqueue.h>
 #include <linux/mii.h>
 
 #include "tlan.h"
@@ -600,10 +600,7 @@
 	
 	/* This will be used when we get an adapter error from
 	 * within our irq handler */
-	INIT_LIST_HEAD(&priv->tlan_tqueue.list);
-	priv->tlan_tqueue.sync = 0;
-	priv->tlan_tqueue.routine = (void *)(void*)TLan_tx_timeout;
-	priv->tlan_tqueue.data = dev;
+	INIT_WORK(&priv->tlan_tqueue, (void *)(void*)TLan_tx_timeout, dev);
 
 	spin_lock_init(&priv->lock);
 	
@@ -1708,7 +1705,7 @@
 		TLan_ReadAndClearStats( dev, TLAN_RECORD );
 		outl( TLAN_HC_AD_RST, dev->base_addr + TLAN_HOST_CMD );
 
-		schedule_task(&priv->tlan_tqueue);
+		schedule_work(&priv->tlan_tqueue);
 
 		netif_wake_queue(dev);
 		ack = 0;
diff -Nru a/drivers/net/tlan.h b/drivers/net/tlan.h
--- a/drivers/net/tlan.h	Mon Oct  7 11:25:37 2002
+++ b/drivers/net/tlan.h	Mon Oct  7 11:25:37 2002
@@ -208,7 +208,7 @@
 	spinlock_t		lock;
 	u8			link;
 	u8			is_eisa;
-	struct tq_struct	tlan_tqueue;
+	struct work_struct			tlan_tqueue;
 	u8			neg_be_verbose;
 } TLanPrivateInfo;
 
diff -Nru a/drivers/net/tulip/de4x5.c b/drivers/net/tulip/de4x5.c
--- a/drivers/net/tulip/de4x5.c	Mon Oct  7 11:25:25 2002
+++ b/drivers/net/tulip/de4x5.c	Mon Oct  7 11:25:25 2002
@@ -2190,7 +2190,7 @@
 
     if (lastPCI == NO_MORE_PCI) return;
 
-    if (!pcibios_present()) {
+    if (!pci_present()) {
 	lastPCI = NO_MORE_PCI;
 	return;          /* No PCI bus in this machine! */
     }
@@ -5872,7 +5872,7 @@
 	if (EISA_signature(name, EISA_ID)) j++;
     }
 #endif
-    if (!pcibios_present()) return j;
+    if (!pci_present()) return j;
 
     for (i=0; (pdev=pci_find_class(class, pdev))!= NULL; i++) {
 	vendor = pdev->vendor;
diff -Nru a/drivers/net/wan/lmc/lmc_main.c b/drivers/net/wan/lmc/lmc_main.c
--- a/drivers/net/wan/lmc/lmc_main.c	Mon Oct  7 11:25:29 2002
+++ b/drivers/net/wan/lmc/lmc_main.c	Mon Oct  7 11:25:29 2002
@@ -1045,8 +1045,8 @@
     unsigned int pci_irq_line;
     u16 vendor, subvendor, device, subdevice;
     u32 foundaddr = 0;
-    unsigned char pci_bus, pci_device_fn;
     u8 intcf = 0;
+    struct pci_dev *pdev = NULL;
 
     /* The card is only available on PCI, so if we don't have a
      * PCI bus, we are in trouble.
@@ -1057,21 +1057,7 @@
         return -1;
     }
     /* Loop basically until we don't find anymore. */
-    while (pci_index < 0xff){
-    	struct pci_dev *pdev;
-        /* The tulip is considered an ethernet class of card... */
-        if (pcibios_find_class (PCI_CLASS_NETWORK_ETHERNET << 8,
-                                pci_index, &pci_bus,
-                                &pci_device_fn) != PCIBIOS_SUCCESSFUL) {
-            /* No card found on this pass */
-            break;
-        }
-        /* Read the info we need to determine if this is
-         * our card or not
-         */
-	pdev = pci_find_slot (pci_bus, pci_device_fn);
-	if (!pdev) break;
-
+    while ((pdev = pci_find_class (PCI_CLASS_NETWORK_ETHERNET << 8, pdev))) {
 	if (pci_enable_device(pdev))
 		break;
 
diff -Nru a/drivers/net/wan/lmc/lmc_proto.c b/drivers/net/wan/lmc/lmc_proto.c
--- a/drivers/net/wan/lmc/lmc_proto.c	Mon Oct  7 11:25:35 2002
+++ b/drivers/net/wan/lmc/lmc_proto.c	Mon Oct  7 11:25:35 2002
@@ -44,7 +44,7 @@
 #include <linux/skbuff.h>
 #include <net/syncppp.h>
 #include <linux/inet.h>
-#include <linux/tqueue.h>
+#include <linux/workqueue.h>
 #include <linux/proc_fs.h>
 
 #include "lmc_ver.h"
diff -Nru a/drivers/net/wan/pc300_tty.c b/drivers/net/wan/pc300_tty.c
--- a/drivers/net/wan/pc300_tty.c	Mon Oct  7 11:25:34 2002
+++ b/drivers/net/wan/pc300_tty.c	Mon Oct  7 11:25:34 2002
@@ -106,8 +106,8 @@
 	pc300dev_t*	pc300dev;	/* ptr. to info struct in PC300 driver */
 	unsigned char	name[20];	/* interf. name + "-tty" */
 	struct tty_struct *tty;		
-	struct tq_struct tty_tx_task_queue; /* tx task - tx interrupt */
-	struct tq_struct tty_rx_task_queue; /* rx task - rx interrupt */
+	struct work_struct tty_tx_work; /* tx work - tx interrupt */
+	struct work_struct tty_rx_work; /* rx work - rx interrupt */
 	} st_cpc_tty_area;
 
 /* TTY data structures */
@@ -134,8 +134,8 @@
 				unsigned int cmd, unsigned long arg);
 static void cpc_tty_flush_buffer(struct tty_struct *tty);
 static void cpc_tty_hangup(struct tty_struct *tty);
-static void cpc_tty_rx_task(void *data);
-static void cpc_tty_tx_task(void *data);
+static void cpc_tty_rx_work(void *data);
+static void cpc_tty_tx_work(void *data);
 static int cpc_tty_send_to_card(pc300dev_t *dev,void *buf, int len);
 static void cpc_tty_trace(pc300dev_t *dev, char* buf, int len, char rxtx);
 static void cpc_tty_dtr_off(pc300dev_t *pc300dev);
@@ -288,12 +288,9 @@
 	cpc_tty->num_open= 0;
 	cpc_tty->tty_minor = port + CPC_TTY_MINOR_START;
 	cpc_tty->pc300dev = pc300dev; 
-	
-	cpc_tty->tty_tx_task_queue.routine = cpc_tty_tx_task; 
-	cpc_tty->tty_tx_task_queue.data = (void *)cpc_tty; 
-	
-	cpc_tty->tty_rx_task_queue.routine = cpc_tty_rx_task; 
-	cpc_tty->tty_rx_task_queue.data = (void *) port; 
+
+	INIT_WORK(&cpc_tty->tty_tx_work, cpc_tty_tx_work, (void *)cpc_tty);
+	INIT_WORK(&cpc_tty->tty_rx_work, cpc_tty_rx_work, (void *)port);
 	
 	cpc_tty->buf_rx.first = cpc_tty->buf_rx.last = 0;
 
@@ -706,13 +703,13 @@
 }
 
 /*
- * PC300 TTY RX task routine
- * This routine treats RX task
+ * PC300 TTY RX work routine
+ * This routine treats RX work
  * o verify read buffer
  * o call the line disc. read
  * o free memory
  */
-static void cpc_tty_rx_task(void * data)
+static void cpc_tty_rx_work(void * data)
 {
 	int port, i, j;
 	st_cpc_tty_area *cpc_tty; 
@@ -745,7 +742,7 @@
 } 
 
 /*
- * PC300 TTY RX task routine
+ * PC300 TTY RX work routine
  *
  * This routine treats RX interrupt. 
  * o read all frames in card
@@ -912,25 +909,25 @@
 				cpc_tty->buf_rx.last->next = new;
 				cpc_tty->buf_rx.last = new;
 			}
-			schedule_task(&(cpc_tty->tty_rx_task_queue));
+			schedule_work(&(cpc_tty->tty_rx_work));
 			stats->rx_packets++;
 		}
 	} 
 } 
 
 /*
- * PC300 TTY TX task routine
+ * PC300 TTY TX work routine
  * 
  * This routine treats TX interrupt. 
  * o if need call line discipline wakeup
  * o call wake_up_interruptible
  */
-static void cpc_tty_tx_task(void *data)
+static void cpc_tty_tx_work(void *data)
 {
 	st_cpc_tty_area *cpc_tty = (st_cpc_tty_area *) data; 
 	struct tty_struct *tty; 
 
-	CPC_TTY_DBG("%s: cpc_tty_tx_task init\n",cpc_tty->name);
+	CPC_TTY_DBG("%s: cpc_tty_tx_work init\n",cpc_tty->name);
 	
 	if ((tty = cpc_tty->tty) == 0) { 
 		CPC_TTY_DBG("%s: the interface is not opened\n",cpc_tty->name);
@@ -1124,7 +1121,7 @@
 	if (!cpc_tty) {
 		return;
 	}
-	schedule_task(&(cpc_tty->tty_tx_task_queue)); 
+	schedule_work(&(cpc_tty->tty_tx_work)); 
 } 
 
 /*
diff -Nru a/drivers/net/wan/sdla_chdlc.c b/drivers/net/wan/sdla_chdlc.c
--- a/drivers/net/wan/sdla_chdlc.c	Mon Oct  7 11:25:35 2002
+++ b/drivers/net/wan/sdla_chdlc.c	Mon Oct  7 11:25:35 2002
@@ -163,10 +163,10 @@
 	
 	unsigned char interface_down;
 
-	/* Polling task queue. Each interface
-         * has its own task queue, which is used
+	/* Polling work queue entry. Each interface
+         * has its own work queue entry, which is used
          * to defer events from the interrupt */
-	struct tq_struct poll_task;
+	struct work_struct poll_work;
 	struct timer_list poll_delay_timer;
 
 	u8 gateway;
@@ -262,8 +262,8 @@
 
 #if defined(LINUX_2_1) || defined(LINUX_2_4)
   /* Bottom half handlers */
-  static void chdlc_bh (netdevice_t *);
-  static int chdlc_bh_cleanup (netdevice_t *);
+  static void chdlc_work (netdevice_t *);
+  static int chdlc_work_cleanup (netdevice_t *);
   static int bh_enqueue (netdevice_t *, struct sk_buff *);
 #endif
 
@@ -930,13 +930,8 @@
 	dev->init = &if_init;
 	dev->priv = chdlc_priv_area;
 
-	/* Initialize the polling task routine */
-#ifndef LINUX_2_4
-	chdlc_priv_area->poll_task.next = NULL;
-#endif
-	chdlc_priv_area->poll_task.sync=0;
-	chdlc_priv_area->poll_task.routine = (void*)(void*)chdlc_poll;
-	chdlc_priv_area->poll_task.data = dev;
+	/* Initialize the polling work routine */
+	INIT_WORK(&chdlc_priv_area->poll_work, (void*)(void*)chdlc_poll, dev);
 
 	/* Initialize the polling delay timer */
 	init_timer(&chdlc_priv_area->poll_delay_timer);
@@ -1052,15 +1047,11 @@
 		return -EBUSY;
 
 #if defined(LINUX_2_1) || defined(LINUX_2_4)
-	/* Initialize the task queue */
+	/* Initialize the work queue entry */
 	chdlc_priv_area->tq_working=0;
 
-#ifndef LINUX_2_4
-	chdlc_priv_area->common.wanpipe_task.next = NULL;
-#endif
-	chdlc_priv_area->common.wanpipe_task.sync = 0;
-	chdlc_priv_area->common.wanpipe_task.routine = (void *)(void *)chdlc_bh;
-	chdlc_priv_area->common.wanpipe_task.data = dev;
+	INIT_WORK(&chdlc_priv_area->common.wanpipe_work,
+			(void *)(void *)chdlc_work, dev);
 
 	/* Allocate and initialize BH circular buffer */
 	/* Add 1 to MAX_BH_BUFF so we don't have test with (MAX_BH_BUFF-1) */
@@ -1863,7 +1854,7 @@
  *       PREPROCESSOR STATEMENT ABOVE, UNLESS YOU KNOW WHAT YOU ARE
  *       DOING */
 
-static void chdlc_bh (netdevice_t * dev)
+static void chdlc_work (netdevice_t * dev)
 {
 	chdlc_private_area_t* chan = dev->priv;
 	sdla_t *card = chan->card;
@@ -1883,7 +1874,7 @@
 			if (chan->common.sk == NULL || chan->common.func == NULL){
 				++card->wandev.stats.rx_dropped;
 				wan_dev_kfree_skb(skb, FREE_READ);
-				chdlc_bh_cleanup(dev);
+				chdlc_work_cleanup(dev);
 				continue;
 			}
 
@@ -1893,10 +1884,10 @@
 				atomic_set(&chan->common.receive_block,1);
 				return;
 			}else{
-				chdlc_bh_cleanup(dev);
+				chdlc_work_cleanup(dev);
 			}
 		}else{
-			chdlc_bh_cleanup(dev);
+			chdlc_work_cleanup(dev);
 		}
 	}	
 	clear_bit(0, &chan->tq_working);
@@ -1904,7 +1895,7 @@
 	return;
 }
 
-static int chdlc_bh_cleanup (netdevice_t *dev)
+static int chdlc_work_cleanup (netdevice_t *dev)
 {
 	chdlc_private_area_t* chan = dev->priv;
 
@@ -2214,10 +2205,8 @@
 
 		bh_enqueue(dev, skb);
 
-		if (!test_and_set_bit(0,&chdlc_priv_area->tq_working)){
-			wanpipe_queue_tq(&chdlc_priv_area->common.wanpipe_task);
-		        wanpipe_mark_bh();
-		}
+		if (!test_and_set_bit(0,&chdlc_priv_area->tq_working))
+			wanpipe_queue_work(&chdlc_priv_area->common.wanpipe_work);
 #endif
 	}else{
 		/* FIXME: we should check to see if the received packet is a 
@@ -3776,7 +3765,7 @@
  * trigger_chdlc_poll
  *
  * Description:
- * 	Add a chdlc_poll() task into a tq_scheduler bh handler
+ * 	Add a chdlc_poll() work entry into the keventd work queue
  *      for a specific dlci/interface.  This will kick
  *      the fr_poll() routine at a later time. 
  *
@@ -3804,12 +3793,7 @@
 	if (test_bit(PERI_CRIT,&card->wandev.critical)){
 		return; 
 	}
-#ifdef LINUX_2_4
-	schedule_task(&chdlc_priv_area->poll_task);
-#else
-	queue_task(&chdlc_priv_area->poll_task, &tq_scheduler);
-#endif
-	return;
+	schedule_work(&chdlc_priv_area->poll_work);
 }
 
 
@@ -3856,14 +3840,10 @@
 
 static void wanpipe_tty_trigger_poll(sdla_t *card)
 {
-#ifdef LINUX_2_4
-	schedule_task(&card->tty_task_queue);
-#else
-	queue_task(&card->tty_task_queue, &tq_scheduler);
-#endif
+	schedule_work(&card->tty_work);
 }
 
-static void tty_poll_task (void* data)
+static void tty_poll_work (void* data)
 {
 	sdla_t *card = (sdla_t*)data;
 	struct tty_struct *tty;
@@ -4736,8 +4716,7 @@
 	state->icount.overrun = state->icount.brk = 0;
 	state->irq = card->wandev.irq; 
 
-	card->tty_task_queue.routine = tty_poll_task;
-	card->tty_task_queue.data = (void*)card;
+	INIT_WORK(&card->tty_work, tty_poll_work, (void*)card);
 	return 0;
 }
 
diff -Nru a/drivers/net/wan/sdla_ppp.c b/drivers/net/wan/sdla_ppp.c
--- a/drivers/net/wan/sdla_ppp.c	Mon Oct  7 11:25:26 2002
+++ b/drivers/net/wan/sdla_ppp.c	Mon Oct  7 11:25:26 2002
@@ -220,10 +220,10 @@
 
 	unsigned long router_up_time; 
 
-	/* Polling task queue. Each interface
-         * has its own task queue, which is used
+	/* Polling work queue entry. Each interface
+         * has its own work queue entry, which is used
          * to defer events from the interrupt */
-	struct tq_struct poll_task;
+	struct work_struct poll_work;
 	struct timer_list poll_delay_timer;
 
 	u8 gateway;
@@ -631,13 +631,8 @@
 	dev->priv = ppp_priv_area;
 	dev->mtu = min_t(unsigned int, dev->mtu, card->wandev.mtu);
 
-	/* Initialize the polling task routine */
-#ifndef LINUX_2_4
-	ppp_priv_area->poll_task.next = NULL;
-#endif
-	ppp_priv_area->poll_task.sync=0;
-	ppp_priv_area->poll_task.routine = (void*)(void*)ppp_poll;
-	ppp_priv_area->poll_task.data = dev;
+	/* Initialize the polling work routine */
+	INIT_WORK(&ppp_priv_area->poll_work, (void*)(void*)ppp_poll, dev);
 
 	/* Initialize the polling delay timer */
 	init_timer(&ppp_priv_area->poll_delay_timer);
@@ -3667,11 +3662,7 @@
 			return;
 		}
 
-#ifdef LINUX_2_4
-		schedule_task(&ppp_priv_area->poll_task);
-#else
-		queue_task(&ppp_priv_area->poll_task, &tq_scheduler);
-#endif
+		schedule_work(&ppp_priv_area->poll_work);
 	}
 	return;
 }
diff -Nru a/drivers/net/wireless/airo.c b/drivers/net/wireless/airo.c
--- a/drivers/net/wireless/airo.c	Mon Oct  7 11:25:25 2002
+++ b/drivers/net/wireless/airo.c	Mon Oct  7 11:25:25 2002
@@ -32,7 +32,7 @@
 #include <linux/timer.h>
 #include <linux/interrupt.h>
 #include <linux/in.h>
-#include <linux/tqueue.h>
+#include <linux/workqueue.h>
 #include <asm/io.h>
 #include <asm/system.h>
 #include <asm/bitops.h>
@@ -999,17 +999,17 @@
 	tdsRssiEntry *rssi;
 	struct semaphore sem;
 	struct task_struct *task;
-	struct tq_struct promisc_task;
+	struct work_struct promisc_task;
 	struct {
 		struct sk_buff *skb;
 		int fid;
-		struct tq_struct task;
+		struct work_struct task;
 	} xmit, xmit11;
 	struct net_device *wifidev;
 #ifdef WIRELESS_EXT
 	struct iw_statistics	wstats;		// wireless stats
 	unsigned long		scan_timestamp;	/* Time started to scan */
-	struct tq_struct	event_task;
+	struct work_struct	event_task;
 #ifdef WIRELESS_SPY
 	int			spy_number;
 	u_char			spy_address[IW_MAX_SPY][ETH_ALEN];
@@ -1019,7 +1019,7 @@
 	/* MIC stuff */
 	mic_module		mod[2];
 	mic_statistics		micstats;
-	struct tq_struct 	mic_task;
+	struct work_struct 	mic_task;
 };
 
 static inline int bap_read(struct airo_info *ai, u16 *pu16Dst, int bytelen,
@@ -1316,9 +1316,9 @@
 
 	if (down_trylock(&priv->sem) != 0) {
 		netif_stop_queue(dev);
-		priv->xmit.task.routine = (void (*)(void *))airo_do_xmit;
+		priv->xmit.task.func = (void (*)(void *))airo_do_xmit;
 		priv->xmit.task.data = (void *)dev;
-		schedule_task(&priv->xmit.task);
+		schedule_work(&priv->xmit.task);
 		return;
 	}
 	status = transmit_802_3_packet (priv, fids[fid], skb->data);
@@ -1378,9 +1378,9 @@
 
 	if (down_trylock(&priv->sem) != 0) {
 		netif_stop_queue(dev);
-		priv->xmit11.task.routine = (void (*)(void *))airo_do_xmit11;
+		priv->xmit11.task.func = (void (*)(void *))airo_do_xmit11;
 		priv->xmit11.task.data = (void *)dev;
-		schedule_task(&priv->xmit11.task);
+		schedule_work(&priv->xmit11.task);
 		return;
 	}
 	status = transmit_802_11_packet (priv, fids[fid], skb->data);
@@ -1464,9 +1464,9 @@
 		completecommand(ai, &rsp);
 		up(&ai->sem);
 	} else {
-		ai->promisc_task.routine = (void (*)(void *))airo_end_promisc;
+		ai->promisc_task.func = (void (*)(void *))airo_end_promisc;
 		ai->promisc_task.data = (void *)ai;
-		schedule_task(&ai->promisc_task);
+		schedule_work(&ai->promisc_task);
 	}
 }
 
@@ -1480,9 +1480,9 @@
 		sendcommand(ai, &cmd);
 		airo_end_promisc(ai);
 	} else {
-		ai->promisc_task.routine = (void (*)(void *))airo_set_promisc;
+		ai->promisc_task.func = (void (*)(void *))airo_set_promisc;
 		ai->promisc_task.data = (void *)ai;
-		schedule_task(&ai->promisc_task);
+		schedule_work(&ai->promisc_task);
 	}
 }
 
@@ -1550,7 +1550,7 @@
 void stop_airo_card( struct net_device *dev, int freeres )
 {
 	struct airo_info *ai = dev->priv;
-	flush_scheduled_tasks();
+	flush_scheduled_work();
 	if (ai->flash)
 		kfree(ai->flash);
 	if (ai->rssi)
@@ -1812,9 +1812,9 @@
 		/* Send event to user space */
 		wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL);
 	} else {
-		ai->event_task.routine = (void (*)(void *))airo_send_event;
+		ai->event_task.func = (void (*)(void *))airo_send_event;
 		ai->event_task.data = (void *)dev;
-		schedule_task(&ai->event_task);
+		schedule_work(&ai->event_task);
 	}
 }
 #endif
@@ -1831,9 +1831,9 @@
 		micinit (ai, &mic_rid);
 #endif
 	} else {
-		ai->mic_task.routine = (void (*)(void *))airo_read_mic;
+		ai->mic_task.func = (void (*)(void *))airo_read_mic;
 		ai->mic_task.data = (void *)ai;
-		schedule_task(&ai->mic_task);
+		schedule_work(&ai->mic_task);
 	}
 }
 
diff -Nru a/drivers/net/wireless/orinoco.c b/drivers/net/wireless/orinoco.c
--- a/drivers/net/wireless/orinoco.c	Mon Oct  7 11:25:35 2002
+++ b/drivers/net/wireless/orinoco.c	Mon Oct  7 11:25:35 2002
@@ -323,7 +323,7 @@
  *	  the card from hard sleep.
  *
  * v0.13beta1 -> v0.13 - 27 Sep 2002 - David Gibson
- *	o Re-introduced full resets (via schedule_task()) on Tx
+ *	o Re-introduced full resets (via schedule_work()) on Tx
  *	  timeout.
  *
  * v0.13 -> v0.13a - 30 Sep 2002 - David Gibson
@@ -378,7 +378,7 @@
 #include <linux/if_arp.h>
 #include <linux/etherdevice.h>
 #include <linux/wireless.h>
-#include <linux/tqueue.h>
+#include <linux/workqueue.h>
 
 #include "hermes.h"
 #include "hermes_rid.h"
@@ -889,7 +889,7 @@
 }
 
 /* This must be called from user context, without locks held - use
- * schedule_task() */
+ * schedule_work() */
 static void orinoco_reset(struct net_device *dev)
 {
 	struct orinoco_private *priv = dev->priv;
@@ -2317,7 +2317,7 @@
 
 	stats->tx_errors++;
 
-	schedule_task(&priv->timeout_task);
+	schedule_work(&priv->timeout_task);
 }
 
 static int
@@ -3708,7 +3708,7 @@
 		
 		printk(KERN_DEBUG "%s: Force scheduling reset!\n", dev->name);
 
-		schedule_task(&priv->timeout_task);
+		schedule_work(&priv->timeout_task);
 		break;
 
 	case SIOCIWFIRSTPRIV + 0x2: /* set_port3 */
@@ -4241,7 +4241,7 @@
 	priv->hw_unavailable = 1; /* orinoco_init() must clear this
 				   * before anything else touches the
 				   * hardware */
-	INIT_TQUEUE(&priv->timeout_task, (void (*)(void *))orinoco_reset, dev);
+	INIT_WORK(&priv->timeout_task, (void (*)(void *))orinoco_reset, dev);
 
 	return dev;
 
diff -Nru a/drivers/net/wireless/orinoco.h b/drivers/net/wireless/orinoco.h
--- a/drivers/net/wireless/orinoco.h	Mon Oct  7 11:25:29 2002
+++ b/drivers/net/wireless/orinoco.h	Mon Oct  7 11:25:29 2002
@@ -11,7 +11,7 @@
 #include <linux/spinlock.h>
 #include <linux/netdevice.h>
 #include <linux/wireless.h>
-#include <linux/tqueue.h>
+#include <linux/workqueue.h>
 #include "hermes.h"
 
 /* To enable debug messages */
@@ -42,7 +42,7 @@
 	/* Synchronisation stuff */
 	spinlock_t lock;
 	int hw_unavailable;
-	struct tq_struct timeout_task;
+	struct work_struct timeout_task;
 
 	int open;
 
diff -Nru a/drivers/pci/Makefile b/drivers/pci/Makefile
--- a/drivers/pci/Makefile	Mon Oct  7 11:25:38 2002
+++ b/drivers/pci/Makefile	Mon Oct  7 11:25:38 2002
@@ -40,6 +40,6 @@
 # And that's how to generate them
 
 $(obj)/devlist.h: $(src)/pci.ids $(obj)/gen-devlist
-	$(obj)/gen-devlist < $<
+	( cd $(obj); ./gen-devlist ) < $<
 
 $(obj)/classlist.h: $(obj)/devlist.h
diff -Nru a/drivers/pci/compat.c b/drivers/pci/compat.c
--- a/drivers/pci/compat.c	Mon Oct  7 11:25:30 2002
+++ b/drivers/pci/compat.c	Mon Oct  7 11:25:30 2002
@@ -13,44 +13,6 @@
 
 /* Obsolete functions, these will be going away... */
 
-int
-pcibios_present(void)
-{
-	return !list_empty(&pci_devices);
-}
-
-int
-pcibios_find_class(unsigned int class, unsigned short index, unsigned char *bus, unsigned char *devfn)
-{
-	const struct pci_dev *dev = NULL;
-	int cnt = 0;
-
-	while ((dev = pci_find_class(class, dev)))
-		if (index == cnt++) {
-			*bus = dev->bus->number;
-			*devfn = dev->devfn;
-			return PCIBIOS_SUCCESSFUL;
-		}
-	return PCIBIOS_DEVICE_NOT_FOUND;
-}
-
-
-int
-pcibios_find_device(unsigned short vendor, unsigned short device, unsigned short index,
-		    unsigned char *bus, unsigned char *devfn)
-{
-	const struct pci_dev *dev = NULL;
-	int cnt = 0;
-
-	while ((dev = pci_find_device(vendor, device, dev)))
-		if (index == cnt++) {
-			*bus = dev->bus->number;
-			*devfn = dev->devfn;
-			return PCIBIOS_SUCCESSFUL;
-		}
-	return PCIBIOS_DEVICE_NOT_FOUND;
-}
-
 #define PCI_OP(rw,size,type)							\
 int pcibios_##rw##_config_##size (unsigned char bus, unsigned char dev_fn,	\
 				  unsigned char where, unsigned type val)	\
@@ -67,13 +29,9 @@
 PCI_OP(write, word, short)
 PCI_OP(write, dword, int)
 
-
-EXPORT_SYMBOL(pcibios_present);
 EXPORT_SYMBOL(pcibios_read_config_byte);
 EXPORT_SYMBOL(pcibios_read_config_word);
 EXPORT_SYMBOL(pcibios_read_config_dword);
 EXPORT_SYMBOL(pcibios_write_config_byte);
 EXPORT_SYMBOL(pcibios_write_config_word);
 EXPORT_SYMBOL(pcibios_write_config_dword);
-EXPORT_SYMBOL(pcibios_find_class);
-EXPORT_SYMBOL(pcibios_find_device);
diff -Nru a/drivers/pci/pci.ids b/drivers/pci/pci.ids
--- a/drivers/pci/pci.ids	Mon Oct  7 11:25:35 2002
+++ b/drivers/pci/pci.ids	Mon Oct  7 11:25:35 2002
@@ -2617,6 +2617,7 @@
 10fc  I-O Data Device, Inc.
 # What's in the cardbus end of a Sony ACR-A01 card, comes with newer Vaio CD-RW drives
 	0003  Cardbus IDE Controller
+	0005  Cardbus SCSI CBSC II
 10fd  Soyo Computer, Inc
 10fe  Fast Multimedia AG
 10ff  NCube
@@ -3027,6 +3028,12 @@
 1144  Cincinnati Milacron
 	0001  Noservo controller
 1145  Workbit Corporation
+	f007  NinjaSCSI-32 KME
+	8007  NinjaSCSI-32 Workbit
+	f010  NinjaSCSI-32 Workbit
+	f012  NinjaSCSI-32 Logitec
+	f013  NinjaSCSI-32 Logitec
+	f015  NinjaSCSI-32 Melco
 1146  Force Computers
 1147  Interface Corp
 1148  Syskonnect (Schneider & Koch)
diff -Nru a/drivers/pci/pool.c b/drivers/pci/pool.c
--- a/drivers/pci/pool.c	Mon Oct  7 11:25:39 2002
+++ b/drivers/pci/pool.c	Mon Oct  7 11:25:39 2002
@@ -31,13 +31,12 @@
 #define	POOL_TIMEOUT_JIFFIES	((100 /* msec */ * HZ) / 1000)
 #define	POOL_POISON_BYTE	0xa7
 
-static spinlock_t pools_lock = SPIN_LOCK_UNLOCKED;
+DECLARE_MUTEX (pools_lock);
 
 static ssize_t
 show_pools (struct device *dev, char *buf, size_t count, loff_t off)
 {
 	struct pci_dev		*pdev;
-	unsigned long		flags;
 	unsigned		temp, size;
 	char			*next;
 	struct list_head	*i, *j;
@@ -53,7 +52,7 @@
 	size -= temp;
 	next += temp;
 
-	spin_lock_irqsave (&pools_lock, flags);
+	down (&pools_lock);
 	list_for_each (i, &pdev->pools) {
 		struct pci_pool	*pool;
 		unsigned	pages = 0, blocks = 0;
@@ -76,7 +75,7 @@
 		size -= temp;
 		next += temp;
 	}
-	spin_unlock_irqrestore (&pools_lock, flags);
+	up (&pools_lock);
 
 	return count - size;
 }
@@ -89,7 +88,7 @@
  * @size: size of the blocks in this pool.
  * @align: alignment requirement for blocks; must be a power of two
  * @allocation: returned blocks won't cross this boundary (or zero)
- * @mem_flags: SLAB_* flags.
+ * Context: !in_interrupt()
  *
  * Returns a pci allocation pool with the requested characteristics, or
  * null if one can't be created.  Given one of these pools, pci_pool_alloc()
@@ -105,10 +104,9 @@
  */
 struct pci_pool *
 pci_pool_create (const char *name, struct pci_dev *pdev,
-	size_t size, size_t align, size_t allocation, int mem_flags)
+	size_t size, size_t align, size_t allocation)
 {
 	struct pci_pool		*retval;
-	unsigned long		flags;
 
 	if (align == 0)
 		align = 1;
@@ -130,7 +128,7 @@
 	} else if (allocation < size)
 		return 0;
 
-	if (!(retval = kmalloc (sizeof *retval, mem_flags)))
+	if (!(retval = kmalloc (sizeof *retval, SLAB_KERNEL)))
 		return retval;
 
 	strncpy (retval->name, name, sizeof retval->name);
@@ -138,18 +136,6 @@
 
 	retval->dev = pdev;
 
-	if (pdev) {
-		int	do_add;
-		spin_lock_irqsave (&pools_lock, flags);
-		do_add = list_empty (&pdev->pools);
-		/* note:  not currently insisting "name" be unique */
-		list_add (&retval->pools, &pdev->pools);
-		spin_unlock_irqrestore (&pools_lock, flags);
-		if (do_add)
-			device_create_file (&pdev->dev, &dev_attr_pools);
-	} else
-		INIT_LIST_HEAD (&retval->pools);
-
 	INIT_LIST_HEAD (&retval->page_list);
 	spin_lock_init (&retval->lock);
 	retval->size = size;
@@ -157,6 +143,16 @@
 	retval->blocks_per_page = allocation / size;
 	init_waitqueue_head (&retval->waitq);
 
+	if (pdev) {
+		down (&pools_lock);
+		if (list_empty (&pdev->pools))
+			device_create_file (&pdev->dev, &dev_attr_pools);
+		/* note:  not currently insisting "name" be unique */
+		list_add (&retval->pools, &pdev->pools);
+		up (&pools_lock);
+	} else
+		INIT_LIST_HEAD (&retval->pools);
+
 	return retval;
 }
 
@@ -220,6 +216,7 @@
 /**
  * pci_pool_destroy - destroys a pool of pci memory blocks.
  * @pool: pci pool that will be destroyed
+ * Context: !in_interrupt()
  *
  * Caller guarantees that no more memory from the pool is in use,
  * and that nothing will try to use the pool after this call.
@@ -227,9 +224,12 @@
 void
 pci_pool_destroy (struct pci_pool *pool)
 {
-	unsigned long		flags;
+	down (&pools_lock);
+	list_del (&pool->pools);
+	if (pool->dev && list_empty (&pool->dev->pools))
+		device_remove_file (&pool->dev->dev, &dev_attr_pools);
+	up (&pools_lock);
 
-	spin_lock_irqsave (&pool->lock, flags);
 	while (!list_empty (&pool->page_list)) {
 		struct pci_page		*page;
 		page = list_entry (pool->page_list.next,
@@ -245,13 +245,6 @@
 			pool_free_page (pool, page);
 	}
 
-	spin_lock (&pools_lock);
-	list_del (&pool->pools);
-	if (pool->dev && list_empty (&pool->dev->pools))
-		device_remove_file (&pool->dev->dev, &dev_attr_pools);
-	spin_unlock (&pools_lock);
-
-	spin_unlock_irqrestore (&pool->lock, flags);
 	kfree (pool);
 }
 
@@ -296,7 +289,7 @@
 			}
 		}
 	}
-	if (!(page = pool_alloc_page (pool, mem_flags))) {
+	if (!(page = pool_alloc_page (pool, SLAB_ATOMIC))) {
 		if (mem_flags == SLAB_KERNEL) {
 			DECLARE_WAITQUEUE (wait, current);
 
diff -Nru a/drivers/pci/syscall.c b/drivers/pci/syscall.c
--- a/drivers/pci/syscall.c	Mon Oct  7 11:25:26 2002
+++ b/drivers/pci/syscall.c	Mon Oct  7 11:25:26 2002
@@ -98,7 +98,7 @@
 
 	if (!capable(CAP_SYS_ADMIN))
 		return -EPERM;
-	if (!pcibios_present())
+	if (!pci_present())
 		return -ENOSYS;
 
 	dev = pci_find_slot(bus, dfn);
diff -Nru a/drivers/pcmcia/cistpl.c b/drivers/pcmcia/cistpl.c
--- a/drivers/pcmcia/cistpl.c	Mon Oct  7 11:25:29 2002
+++ b/drivers/pcmcia/cistpl.c	Mon Oct  7 11:25:29 2002
@@ -264,11 +264,11 @@
 	(s->cis_mem.sys_start == 0)) {
 	int low = !(s->cap.features & SS_CAP_PAGE_REGS);
 	vs = s;
-	validate_mem(cis_readable, checksum_match, low);
+	validate_mem(cis_readable, checksum_match, low, s);
 	s->cis_mem.sys_start = 0;
 	vs = NULL;
 	if (find_mem_region(&s->cis_mem.sys_start, s->cap.map_size,
-			    s->cap.map_size, low, "card services")) {
+			    s->cap.map_size, low, "card services", s)) {
 	    printk(KERN_NOTICE "cs: unable to map card memory!\n");
 	    return CS_OUT_OF_RESOURCE;
 	}
diff -Nru a/drivers/pcmcia/cs.c b/drivers/pcmcia/cs.c
--- a/drivers/pcmcia/cs.c	Mon Oct  7 11:25:28 2002
+++ b/drivers/pcmcia/cs.c	Mon Oct  7 11:25:28 2002
@@ -809,7 +809,7 @@
 	    return 1;
     for (i = 0; i < MAX_IO_WIN; i++) {
 	if (s->io[i].NumPorts == 0) {
-	    if (find_io_region(base, num, align, name) == 0) {
+	    if (find_io_region(base, num, align, name, s) == 0) {
 		s->io[i].Attributes = attr;
 		s->io[i].BasePort = *base;
 		s->io[i].NumPorts = s->io[i].InUse = num;
@@ -821,7 +821,7 @@
 	/* Try to extend top of window */
 	try = s->io[i].BasePort + s->io[i].NumPorts;
 	if ((*base == 0) || (*base == try))
-	    if (find_io_region(&try, num, 0, name) == 0) {
+	    if (find_io_region(&try, num, 0, name, s) == 0) {
 		*base = try;
 		s->io[i].NumPorts += num;
 		s->io[i].InUse += num;
@@ -830,7 +830,7 @@
 	/* Try to extend bottom of window */
 	try = s->io[i].BasePort - num;
 	if ((*base == 0) || (*base == try))
-	    if (find_io_region(&try, num, 0, name) == 0) {
+	    if (find_io_region(&try, num, 0, name, s) == 0) {
 		s->io[i].BasePort = *base = try;
 		s->io[i].NumPorts += num;
 		s->io[i].InUse += num;
@@ -1974,7 +1974,7 @@
 	find_mem_region(&win->base, win->size, align,
 			(req->Attributes & WIN_MAP_BELOW_1MB) ||
 			!(s->cap.features & SS_CAP_PAGE_REGS),
-			(*handle)->dev_info))
+			(*handle)->dev_info, s))
 	return CS_IN_USE;
     (*handle)->state |= CLIENT_WIN_REQ(w);
 
diff -Nru a/drivers/pcmcia/cs_internal.h b/drivers/pcmcia/cs_internal.h
--- a/drivers/pcmcia/cs_internal.h	Mon Oct  7 11:25:25 2002
+++ b/drivers/pcmcia/cs_internal.h	Mon Oct  7 11:25:25 2002
@@ -238,11 +238,11 @@
 
 /* In rsrc_mgr */
 void validate_mem(int (*is_valid)(u_long), int (*do_cksum)(u_long),
-		  int force_low);
+		  int force_low, socket_info_t *s);
 int find_io_region(ioaddr_t *base, ioaddr_t num, ioaddr_t align,
-		   char *name);
+		   char *name, socket_info_t *s);
 int find_mem_region(u_long *base, u_long num, u_long align,
-		    int force_low, char *name);
+		    int force_low, char *name, socket_info_t *s);
 int try_irq(u_int Attributes, int irq, int specific);
 void undo_irq(u_int Attributes, int irq);
 int adjust_resource_info(client_handle_t handle, adjust_t *adj);
diff -Nru a/drivers/pcmcia/ds.c b/drivers/pcmcia/ds.c
--- a/drivers/pcmcia/ds.c	Mon Oct  7 11:25:36 2002
+++ b/drivers/pcmcia/ds.c	Mon Oct  7 11:25:36 2002
@@ -895,7 +895,7 @@
      * We really should let the drivers themselves drive some of this..
      */
     current->state = TASK_INTERRUPTIBLE;
-    schedule_timeout(HZ/10);
+    schedule_timeout(HZ/4);
 
     pcmcia_get_card_services_info(&serv);
     if (serv.Revision != CS_RELEASE_CODE) {
diff -Nru a/drivers/pcmcia/hd64465_ss.c b/drivers/pcmcia/hd64465_ss.c
--- a/drivers/pcmcia/hd64465_ss.c	Mon Oct  7 11:25:35 2002
+++ b/drivers/pcmcia/hd64465_ss.c	Mon Oct  7 11:25:35 2002
@@ -37,7 +37,7 @@
 #include <linux/vmalloc.h>
 #include <asm/errno.h>
 #include <linux/irq.h>
-#include <linux/tqueue.h>
+#include <linux/workqueue.h>
 
 #include <asm/io.h>
 #include <asm/hd64465.h>
@@ -791,7 +791,7 @@
 /*
  * Interrupt handling routine.
  *
- * This uses the schedule_task() technique to cause reportable events
+ * This uses the schedule_work() technique to cause reportable events
  * such as card insertion and removal to be handled in keventd's
  * process context.
  */
@@ -816,9 +816,7 @@
 	}
 }
 
-static struct tq_struct hs_events_task = {
-	routine:	hs_events_bh
-};
+static DECLARE_WORK(hs_events_task, hs_events_bh, NULL);
 
 static void hs_interrupt(int irq, void *dev, struct pt_regs *regs)
 {
@@ -885,7 +883,7 @@
 	    sp->pending_events |= events;
 	    spin_unlock(&hs_pending_event_lock);
 	    
-	    schedule_task(&hs_events_task);
+	    schedule_work(&hs_events_task);
 	}
 }
 
diff -Nru a/drivers/pcmcia/i82092.c b/drivers/pcmcia/i82092.c
--- a/drivers/pcmcia/i82092.c	Mon Oct  7 11:25:40 2002
+++ b/drivers/pcmcia/i82092.c	Mon Oct  7 11:25:40 2002
@@ -14,7 +14,7 @@
 #include <linux/module.h>
 #include <linux/pci.h>
 #include <linux/init.h>
-#include <linux/tqueue.h>
+#include <linux/workqueue.h>
 
 #include <pcmcia/cs_types.h>
 #include <pcmcia/ss.h>
@@ -315,9 +315,7 @@
 }
                                                                                                                                         
 
-static struct tq_struct i82092aa_task = {
-        routine:        i82092aa_bh
-};
+static DECLARE_WORK(i82092aa_task, i82092aa_bh, NULL);
         
 
 static void i82092aa_interrupt(int irq, void *dev, struct pt_regs *regs)
@@ -367,7 +365,7 @@
 			
 			if (events) {
 				sockets[i].pending_events |= events;
-				schedule_task(&i82092aa_task);
+				schedule_work(&i82092aa_task);
 			}
 			active |= events;
 		}
diff -Nru a/drivers/pcmcia/i82365.c b/drivers/pcmcia/i82365.c
--- a/drivers/pcmcia/i82365.c	Mon Oct  7 11:25:38 2002
+++ b/drivers/pcmcia/i82365.c	Mon Oct  7 11:25:38 2002
@@ -46,7 +46,7 @@
 #include <linux/ioport.h>
 #include <linux/delay.h>
 #include <linux/proc_fs.h>
-#include <linux/tqueue.h>
+#include <linux/workqueue.h>
 #include <asm/irq.h>
 #include <asm/io.h>
 #include <asm/bitops.h>
@@ -931,9 +931,7 @@
 	}
 }
 
-static struct tq_struct pcic_task = {
-	routine:	pcic_bh
-};
+static DECLARE_WORK(pcic_task, pcic_bh, NULL);
 
 static unsigned long last_detect_jiffies;
 
@@ -990,7 +988,7 @@
 		    spin_lock(&pending_event_lock);
 		    pending_events[i] |= events;
 		    spin_unlock(&pending_event_lock);
-		    schedule_task(&pcic_task);
+		    schedule_work(&pcic_task);
 	    }
 	    active |= events;
 	}
diff -Nru a/drivers/pcmcia/pci_socket.c b/drivers/pcmcia/pci_socket.c
--- a/drivers/pcmcia/pci_socket.c	Mon Oct  7 11:25:39 2002
+++ b/drivers/pcmcia/pci_socket.c	Mon Oct  7 11:25:39 2002
@@ -20,7 +20,7 @@
 #include <linux/init.h>
 #include <linux/pci.h>
 #include <linux/sched.h>
-#include <linux/tqueue.h>
+#include <linux/workqueue.h>
 #include <linux/interrupt.h>
 
 #include <pcmcia/ss.h>
diff -Nru a/drivers/pcmcia/pci_socket.h b/drivers/pcmcia/pci_socket.h
--- a/drivers/pcmcia/pci_socket.h	Mon Oct  7 11:25:26 2002
+++ b/drivers/pcmcia/pci_socket.h	Mon Oct  7 11:25:26 2002
@@ -21,7 +21,7 @@
 	spinlock_t event_lock;
 	unsigned int events;
 	struct socket_info_t *pcmcia_socket;
-	struct tq_struct tq_task;
+	struct work_struct tq_task;
 	struct timer_list poll_timer;
 
 	/* A few words of private data for the low-level driver.. */
diff -Nru a/drivers/pcmcia/rsrc_mgr.c b/drivers/pcmcia/rsrc_mgr.c
--- a/drivers/pcmcia/rsrc_mgr.c	Mon Oct  7 11:25:36 2002
+++ b/drivers/pcmcia/rsrc_mgr.c	Mon Oct  7 11:25:36 2002
@@ -44,6 +44,7 @@
 #include <linux/ioport.h>
 #include <linux/timer.h>
 #include <linux/proc_fs.h>
+#include <linux/pci.h>
 #include <asm/irq.h>
 #include <asm/io.h>
 
@@ -103,8 +104,82 @@
 
 ======================================================================*/
 
-#define check_io_resource(b,n)	check_resource(&ioport_resource, (b), (n))
-#define check_mem_resource(b,n)	check_resource(&iomem_resource, (b), (n))
+static struct resource *resource_parent(unsigned long b, unsigned long n,
+					int flags, struct pci_dev *dev)
+{
+#ifdef CONFIG_PCI
+	struct resource res, *pr;
+
+	if (dev != NULL) {
+		res.start = b;
+		res.end = b + n - 1;
+		res.flags = flags;
+		pr = pci_find_parent_resource(dev, &res);
+		if (pr)
+			return pr;
+	}
+#endif /* CONFIG_PCI */
+	if (flags & IORESOURCE_MEM)
+		return &iomem_resource;
+	return &ioport_resource;
+}
+
+static inline int check_io_resource(unsigned long b, unsigned long n,
+				    struct pci_dev *dev)
+{
+	return check_resource(resource_parent(b, n, IORESOURCE_IO, dev), b, n);
+}
+
+static inline int check_mem_resource(unsigned long b, unsigned long n,
+				     struct pci_dev *dev)
+{
+	return check_resource(resource_parent(b, n, IORESOURCE_MEM, dev), b, n);
+}
+
+static struct resource *make_resource(unsigned long b, unsigned long n,
+				      int flags, char *name)
+{
+	struct resource *res = kmalloc(sizeof(*res), GFP_KERNEL);
+
+	if (res) {
+		memset(res, 0, sizeof(*res));
+		res->name = name;
+		res->start = b;
+		res->end = b + n - 1;
+		res->flags = flags | IORESOURCE_BUSY;
+	}
+	return res;
+}
+
+static int request_io_resource(unsigned long b, unsigned long n,
+			       char *name, struct pci_dev *dev)
+{
+	struct resource *res = make_resource(b, n, IORESOURCE_IO, name);
+	struct resource *pr = resource_parent(b, n, IORESOURCE_IO, dev);
+	int err = -ENOMEM;
+
+	if (res) {
+		err = request_resource(pr, res);
+		if (err)
+			kfree(res);
+	}
+	return err;
+}
+
+static int request_mem_resource(unsigned long b, unsigned long n,
+				char *name, struct pci_dev *dev)
+{
+	struct resource *res = make_resource(b, n, IORESOURCE_MEM, name);
+	struct resource *pr = resource_parent(b, n, IORESOURCE_MEM, dev);
+	int err = -ENOMEM;
+
+	if (res) {
+		err = request_resource(pr, res);
+		if (err)
+			kfree(res);
+	}
+	return err;
+}
 
 /*======================================================================
 
@@ -194,7 +269,7 @@
     }   
     memset(b, 0, 256);
     for (i = base, most = 0; i < base+num; i += 8) {
-	if (check_io_resource(i, 8))
+	if (check_io_resource(i, 8, NULL))
 	    continue;
 	hole = inb(i);
 	for (j = 1; j < 8; j++)
@@ -207,7 +282,7 @@
 
     bad = any = 0;
     for (i = base; i < base+num; i += 8) {
-	if (check_io_resource(i, 8))
+	if (check_io_resource(i, 8, NULL))
 	    continue;
 	for (j = 0; j < 8; j++)
 	    if (inb(i+j) != most) break;
@@ -247,7 +322,8 @@
 ======================================================================*/
 
 static int do_mem_probe(u_long base, u_long num,
-			int (*is_valid)(u_long), int (*do_cksum)(u_long))
+			int (*is_valid)(u_long), int (*do_cksum)(u_long),
+			socket_info_t *s)
 {
     u_long i, j, bad, fail, step;
 
@@ -258,13 +334,14 @@
     for (i = j = base; i < base+num; i = j + step) {
 	if (!fail) {	
 	    for (j = i; j < base+num; j += step)
-		if ((check_mem_resource(j, step) == 0) && is_valid(j))
+		if ((check_mem_resource(j, step, s->cap.cb_dev) == 0) &&
+		    is_valid(j))
 		    break;
 	    fail = ((i == base) && (j == base+num));
 	}
 	if (fail) {
 	    for (j = i; j < base+num; j += 2*step)
-		if ((check_mem_resource(j, 2*step) == 0) &&
+		if ((check_mem_resource(j, 2*step, s->cap.cb_dev) == 0) &&
 		    do_cksum(j) && do_cksum(j+step))
 		    break;
 	}
@@ -283,12 +360,12 @@
 
 static u_long inv_probe(int (*is_valid)(u_long),
 			int (*do_cksum)(u_long),
-			resource_map_t *m)
+			resource_map_t *m, socket_info_t *s)
 {
     u_long ok;
     if (m == &mem_db)
 	return 0;
-    ok = inv_probe(is_valid, do_cksum, m->next);
+    ok = inv_probe(is_valid, do_cksum, m->next, s);
     if (ok) {
 	if (m->base >= 0x100000)
 	    sub_interval(&mem_db, m->base, m->num);
@@ -296,11 +373,11 @@
     }
     if (m->base < 0x100000)
 	return 0;
-    return do_mem_probe(m->base, m->num, is_valid, do_cksum);
+    return do_mem_probe(m->base, m->num, is_valid, do_cksum, s);
 }
 
 void validate_mem(int (*is_valid)(u_long), int (*do_cksum)(u_long),
-		  int force_low)
+		  int force_low, socket_info_t *s)
 {
     resource_map_t *m, *n;
     static u_char order[] = { 0xd0, 0xe0, 0xc0, 0xf0 };
@@ -310,7 +387,7 @@
     if (!probe_mem) return;
     /* We do up to four passes through the list */
     if (!force_low) {
-	if (hi++ || (inv_probe(is_valid, do_cksum, mem_db.next) > 0))
+	if (hi++ || (inv_probe(is_valid, do_cksum, mem_db.next, s) > 0))
 	    return;
 	printk(KERN_NOTICE "cs: warning: no high memory space "
 	       "available!\n");
@@ -321,7 +398,7 @@
 	/* Only probe < 1 MB */
 	if (m->base >= 0x100000) continue;
 	if ((m->base | m->num) & 0xffff) {
-	    ok += do_mem_probe(m->base, m->num, is_valid, do_cksum);
+	    ok += do_mem_probe(m->base, m->num, is_valid, do_cksum, s);
 	    continue;
 	}
 	/* Special probe for 64K-aligned block */
@@ -331,7 +408,7 @@
 		if (ok >= mem_limit)
 		    sub_interval(&mem_db, b, 0x10000);
 		else
-		    ok += do_mem_probe(b, 0x10000, is_valid, do_cksum);
+		    ok += do_mem_probe(b, 0x10000, is_valid, do_cksum, s);
 	    }
 	}
     }
@@ -340,7 +417,7 @@
 #else /* CONFIG_ISA */
 
 void validate_mem(int (*is_valid)(u_long), int (*do_cksum)(u_long),
-		  int force_low)
+		  int force_low, socket_info_t *s)
 {
     resource_map_t *m;
     static int done = 0;
@@ -348,7 +425,7 @@
     if (!probe_mem || done++)
 	return;
     for (m = mem_db.next; m != &mem_db; m = m->next)
-	if (do_mem_probe(m->base, m->num, is_valid, do_cksum))
+	if (do_mem_probe(m->base, m->num, is_valid, do_cksum, s))
 	    return;
 }
 
@@ -368,7 +445,7 @@
 ======================================================================*/
 
 int find_io_region(ioaddr_t *base, ioaddr_t num, ioaddr_t align,
-		   char *name)
+		   char *name, socket_info_t *s)
 {
     ioaddr_t try;
     resource_map_t *m;
@@ -378,9 +455,8 @@
 	for (try = (try >= m->base) ? try : try+align;
 	     (try >= m->base) && (try+num <= m->base+m->num);
 	     try += align) {
-	    if (check_io_resource(try, num) == 0) {
+	    if (request_io_resource(try, num, name, s->cap.cb_dev) == 0) {
 		*base = try;
-		request_region(try, num, name);
 		return 0;
 	    }
 	    if (!align) break;
@@ -390,7 +466,7 @@
 }
 
 int find_mem_region(u_long *base, u_long num, u_long align,
-		    int force_low, char *name)
+		    int force_low, char *name, socket_info_t *s)
 {
     u_long try;
     resource_map_t *m;
@@ -403,8 +479,7 @@
 	    for (try = (try >= m->base) ? try : try+align;
 		 (try >= m->base) && (try+num <= m->base+m->num);
 		 try += align) {
-		if (check_mem_resource(try, num) == 0) {
-		    request_mem_region(try, num, name);
+		if (request_mem_resource(try, num, name, s->cap.cb_dev) == 0) {
 		    *base = try;
 		    return 0;
 		}
diff -Nru a/drivers/pcmcia/sa1100_generic.c b/drivers/pcmcia/sa1100_generic.c
--- a/drivers/pcmcia/sa1100_generic.c	Mon Oct  7 11:25:36 2002
+++ b/drivers/pcmcia/sa1100_generic.c	Mon Oct  7 11:25:36 2002
@@ -41,7 +41,7 @@
 #include <linux/delay.h>
 #include <linux/ioport.h>
 #include <linux/kernel.h>
-#include <linux/tqueue.h>
+#include <linux/workqueue.h>
 #include <linux/timer.h>
 #include <linux/mm.h>
 #include <linux/notifier.h>
@@ -80,7 +80,7 @@
 static struct pcmcia_low_level *pcmcia_low_level;
 
 static struct timer_list poll_timer;
-static struct tq_struct sa1100_pcmcia_task;
+static struct work_struct sa1100_pcmcia_task;
 
 /*
  * sa1100_pcmcia_default_mecr_timing
@@ -324,9 +324,7 @@
   } while(all_events);
 }  /* sa1100_pcmcia_task_handler() */
 
-static struct tq_struct sa1100_pcmcia_task = {
-	routine: sa1100_pcmcia_task_handler
-};
+static DECLARE_WORK(sa1100_pcmcia_task, sa1100_pcmcia_task_handler, NULL);
 
 
 /* sa1100_pcmcia_poll_event()
@@ -339,7 +337,7 @@
   poll_timer.function = sa1100_pcmcia_poll_event;
   poll_timer.expires = jiffies + SA1100_PCMCIA_POLL_PERIOD;
   add_timer(&poll_timer);
-  schedule_task(&sa1100_pcmcia_task);
+  schedule_work(&sa1100_pcmcia_task);
 }
 
 
@@ -355,7 +353,7 @@
 static void sa1100_pcmcia_interrupt(int irq, void *dev, struct pt_regs *regs)
 {
   DEBUG(3, "%s(): servicing IRQ %d\n", __FUNCTION__, irq);
-  schedule_task(&sa1100_pcmcia_task);
+  schedule_work(&sa1100_pcmcia_task);
 }
 
 
@@ -1087,7 +1085,7 @@
 
 	ops->shutdown();
 
-	flush_scheduled_tasks();
+	flush_scheduled_work();
 
 	pcmcia_low_level = NULL;
 }
diff -Nru a/drivers/pcmcia/tcic.c b/drivers/pcmcia/tcic.c
--- a/drivers/pcmcia/tcic.c	Mon Oct  7 11:25:35 2002
+++ b/drivers/pcmcia/tcic.c	Mon Oct  7 11:25:35 2002
@@ -49,7 +49,7 @@
 #include <linux/ioport.h>
 #include <linux/delay.h>
 #include <linux/proc_fs.h>
-#include <linux/tqueue.h>
+#include <linux/workqueue.h>
 
 #include <pcmcia/version.h>
 #include <pcmcia/cs_types.h>
@@ -550,9 +550,7 @@
 	}
 }
 
-static struct tq_struct tcic_task = {
-	routine:	tcic_bh
-};
+static DECLARE_WORK(tcic_task, tcic_bh, NULL);
 
 static void tcic_interrupt(int irq, void *dev, struct pt_regs *regs)
 {
@@ -596,7 +594,7 @@
 		spin_lock(&pending_event_lock);
 		pending_events[i] |= events;
 		spin_unlock(&pending_event_lock);
-		schedule_task(&tcic_task);
+		schedule_work(&tcic_task);
 	}
     }
 
diff -Nru a/drivers/pcmcia/yenta.c b/drivers/pcmcia/yenta.c
--- a/drivers/pcmcia/yenta.c	Mon Oct  7 11:25:36 2002
+++ b/drivers/pcmcia/yenta.c	Mon Oct  7 11:25:36 2002
@@ -6,7 +6,7 @@
 #include <linux/init.h>
 #include <linux/pci.h>
 #include <linux/sched.h>
-#include <linux/tqueue.h>
+#include <linux/workqueue.h>
 #include <linux/interrupt.h>
 #include <linux/delay.h>
 #include <linux/module.h>
@@ -492,7 +492,7 @@
 		spin_lock(&socket->event_lock);
 		socket->events |= events;
 		spin_unlock(&socket->event_lock);
-		schedule_task(&socket->tq_task);
+		schedule_work(&socket->tq_task);
 	}
 }
 
@@ -583,7 +583,7 @@
 	pci_socket_t * socket = (pci_socket_t *) data;
 
 	/* It's OK to overwrite this now */
-	socket->tq_task.routine = yenta_bh;
+	INIT_WORK(&socket->tq_task, yenta_bh, NULL);
 
 	if (!socket->cb_irq || request_irq(socket->cb_irq, yenta_interrupt, SA_SHIRQ, socket->dev->name, socket)) {
 		/* No IRQ or request_irq failed. Poll */
@@ -876,11 +876,10 @@
 	   initialisation later. We can't do this here,
 	   because, er, because Linus says so :)
 	*/
-	socket->tq_task.routine = yenta_open_bh;
-	socket->tq_task.data = socket;
+	INIT_WORK(&socket->tq_task, yenta_open_bh, socket);
 
 	MOD_INC_USE_COUNT;
-	schedule_task(&socket->tq_task);
+	schedule_work(&socket->tq_task);
 
 	return 0;
 }
diff -Nru a/drivers/pnp/isapnp.c b/drivers/pnp/isapnp.c
--- a/drivers/pnp/isapnp.c	Mon Oct  7 11:25:35 2002
+++ b/drivers/pnp/isapnp.c	Mon Oct  7 11:25:35 2002
@@ -1048,7 +1048,7 @@
 	isapnp_wait();
 	isapnp_key();
 	isapnp_wake(csn);
-#if 1
+#if 0
 	/* to avoid malfunction when the isapnptools package is used */
 	/* we must set RDP to our value again */
 	/* it is possible to set RDP only in the isolation phase */ 
diff -Nru a/drivers/s390/Makefile b/drivers/s390/Makefile
--- a/drivers/s390/Makefile	Mon Oct  7 11:25:35 2002
+++ b/drivers/s390/Makefile	Mon Oct  7 11:25:35 2002
@@ -7,6 +7,9 @@
 obj-$(CONFIG_QDIO) += qdio.o
 
 obj-y += s390mach.o s390dyn.o sysinfo.o
-obj-y += block/ char/ misc/ net/ cio/
+obj-y += cio/ block/ char/ misc/ net/
+
+drivers-y += drivers/s390/built-in.o
 
 include $(TOPDIR)/Rules.make
+
diff -Nru a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c
--- a/drivers/s390/block/dasd.c	Mon Oct  7 11:25:35 2002
+++ b/drivers/s390/block/dasd.c	Mon Oct  7 11:25:35 2002
@@ -203,20 +203,24 @@
 dasd_alloc_device(dasd_devmap_t *devmap)
 {
 	dasd_device_t *device;
+	struct gendisk *gdp;
 	int rc;
 
-	/* Make sure the gendisk structure for this device exists. */
-	while (dasd_gendisk_from_devindex(devmap->devindex) == NULL) {
-		rc = dasd_gendisk_new_major();
-		if (rc)
-			return ERR_PTR(rc);
-	}
-
 	device = kmalloc(sizeof (dasd_device_t), GFP_ATOMIC);
 	if (device == NULL)
 		return ERR_PTR(-ENOMEM);
 	memset(device, 0, sizeof (dasd_device_t));
 
+	/* Get devinfo from the common io layer. */
+	rc = get_dev_info_by_devno(devmap->devno, &device->devinfo);
+	if (rc) {
+		kfree(device);
+		return ERR_PTR(rc);
+	}
+	DBF_EVENT(DBF_NOTICE, "got devinfo CU-type %04x and dev-type %04x",
+		  device->devinfo.sid_data.cu_type,
+		  device->devinfo.sid_data.dev_type);
+
 	/* Get two pages for normal block device operations. */
 	device->ccw_mem = (void *) __get_free_pages(GFP_ATOMIC | GFP_DMA, 1);
 	if (device->ccw_mem == NULL) {
@@ -231,17 +235,15 @@
 		return ERR_PTR(-ENOMEM);
 	}
 
-	/* Get devinfo from the common io layer. */
-	rc = get_dev_info_by_devno(devmap->devno, &device->devinfo);
-	if (rc) {
- 		free_page((unsigned long) device->erp_mem);
+	/* Allocate gendisk structure for device. */
+	gdp = dasd_gendisk_alloc(devmap->devindex);
+	if (IS_ERR(gdp)) {
+		free_page((unsigned long) device->erp_mem);
 		free_pages((unsigned long) device->ccw_mem, 1);
 		kfree(device);
-		return ERR_PTR(rc);
+		return (dasd_device_t *) gdp;
 	}
-	DBF_EVENT(DBF_NOTICE, "got devinfo CU-type %04x and dev-type %04x",
-		  device->devinfo.sid_data.cu_type,
-		  device->devinfo.sid_data.dev_type);
+	device->gdp = gdp;
 
 	dasd_init_chunklist(&device->ccw_chunks, device->ccw_mem, PAGE_SIZE*2);
 	dasd_init_chunklist(&device->erp_chunks, device->erp_mem, PAGE_SIZE);
@@ -268,6 +270,7 @@
 		kfree(device->private);
 	free_page((unsigned long) device->erp_mem);
 	free_pages((unsigned long) device->ccw_mem, 1);
+	put_disk(device->gdp);
 	kfree(device);
 }
 
@@ -278,21 +281,18 @@
 dasd_state_new_to_known(dasd_device_t *device)
 {
 	char buffer[5];
-	struct gendisk *gdp;
 	dasd_devmap_t *devmap;
 	umode_t devfs_perm;
 	devfs_handle_t dir;
-	int minor, rc;
+	int major, minor, rc;
 
 	devmap = dasd_devmap_from_devno(device->devinfo.devno);
 	if (devmap == NULL)
 		return -ENODEV;
-	gdp = dasd_gendisk_from_devindex(devmap->devindex);
-	if (gdp == NULL)
+	major = dasd_gendisk_index_major(devmap->devindex);
+	if (major < 0)
 		return -ENODEV;
-	/* Set kdev and the device name. */
-	device->kdev = mk_kdev(gdp->major, gdp->first_minor);
-	strcpy(device->name, gdp->disk_name);
+	minor = devmap->devindex % DASD_PER_MAJOR;
 
 	/* Find a discipline for the device. */
 	rc = dasd_find_disc(device);
@@ -302,14 +302,13 @@
 	/* Add a proc directory and the dasd device entry to devfs. */
 	sprintf(buffer, "%04x", device->devinfo.devno);
 	dir = devfs_mk_dir(dasd_devfs_handle, buffer, device);
-	gdp->de = dir;
+	device->gdp->de = dir;
 	if (devmap->features & DASD_FEATURE_READONLY)
 		devfs_perm = S_IFBLK | S_IRUSR;
 	else
 		devfs_perm = S_IFBLK | S_IRUSR | S_IWUSR;
 	device->devfs_entry = devfs_register(dir, "device", DEVFS_FL_DEFAULT,
-					     major(device->kdev),
-					     minor(device->kdev),
+					     major, minor << DASD_PARTN_BITS,
 					     devfs_perm,
 					     &dasd_device_operations, NULL);
 	device->state = DASD_STATE_KNOWN;
@@ -322,17 +321,25 @@
 static inline void
 dasd_state_known_to_new(dasd_device_t * device)
 {
-	dasd_devmap_t *devmap = dasd_devmap_from_devno(device->devinfo.devno);
-	struct gendisk *gdp = dasd_gendisk_from_devindex(devmap->devindex);
-	if (gdp == NULL)
-		return;
+	dasd_devmap_t *devmap;
+	struct block_device *bdev;
+	int minor;
+
+	devmap = dasd_devmap_from_devno(device->devinfo.devno);
+	minor = devmap->devindex % DASD_PER_MAJOR;
+
 	/* Remove device entry and devfs directory. */
 	devfs_unregister(device->devfs_entry);
-	devfs_unregister(gdp->de);
+	devfs_unregister(device->gdp->de);
 
 	/* Forget the discipline information. */
 	device->discipline = NULL;
 	device->state = DASD_STATE_NEW;
+
+	/* Forget the block device */
+	bdev = bdget(MKDEV(device->gdp->major, device->gdp->first_minor));
+	bdput(bdev);
+	bdput(bdev);
 }
 
 /*
@@ -344,7 +351,7 @@
 	int rc;
 
 	/* register 'device' debug area, used for all DBF_DEV_XXX calls */
-	device->debug_area = debug_register(device->name, 0, 2,
+	device->debug_area = debug_register(device->gdp->disk_name, 0, 2,
 					    8 * sizeof (long));
 	debug_register_view(device->debug_area, &debug_sprintf_view);
 	debug_set_level(device->debug_area, DBF_ERR);
@@ -419,21 +426,30 @@
 }
 
 /*
+ * get the kdev_t of a device 
+ * FIXME: remove this when no longer needed
+ */
+static inline kdev_t
+dasd_partition_to_kdev_t(dasd_device_t *device, unsigned int partition)
+{
+	return mk_kdev(device->gdp->major, device->gdp->first_minor+partition);
+}
+
+
+/*
  * Setup block device.
  */
 static inline int
 dasd_state_accept_to_ready(dasd_device_t * device)
 {
 	dasd_devmap_t *devmap;
-	int major, minor;
 	int rc, i;
 
 	devmap = dasd_devmap_from_devno(device->devinfo.devno);
 	if (devmap->features & DASD_FEATURE_READONLY) {
-		major = major(device->kdev);
-		minor = minor(device->kdev);
 		for (i = 0; i < (1 << DASD_PARTN_BITS); i++)
-			set_device_ro(mk_kdev(major, minor+i), 1);
+			set_device_ro(dasd_partition_to_kdev_t(device, i), 1);
+		device->ro_flag = 1;
 		DEV_MESSAGE (KERN_WARNING, device, "%s",
 			     "setting read-only mode ");
 	}
@@ -1539,11 +1555,9 @@
 			goto restart;
 		}
 
-		/* Dechain request from device request queue ... */
+		/* Rechain request on device device request queue */
 		cqr->endclk = get_clock();
-		list_del(&cqr->list);
-		/* ... and add it to list of final requests. */
-		list_add_tail(&cqr->list, final_queue);
+		list_move_tail(&cqr->list, final_queue);
 	}
 }
 
@@ -1565,7 +1579,6 @@
 static inline void
 __dasd_process_blk_queue(dasd_device_t * device)
 {
-	struct block_device *bdev;
 	request_queue_t *queue;
 	struct list_head *l;
 	struct request *req;
@@ -1594,14 +1607,11 @@
 		if (cqr->status == DASD_CQR_QUEUED)
 			nr_queued++;
 	}
-	bdev = bdget(kdev_t_to_nr(device->kdev));
-	if (!bdev)
-		return;
 	while (!blk_queue_plugged(queue) &&
 	       !blk_queue_empty(queue) &&
 		nr_queued < DASD_CHANQ_MAX_SIZE) {
 		req = elv_next_request(queue);
-		if (bdev_read_only(bdev) && rq_data_dir(req) == WRITE) {
+		if (device->ro_flag && rq_data_dir(req) == WRITE) {
 			DBF_EVENT(DBF_ERR,
 				  "(%04x) Rejecting write request %p",
 				  device->devinfo.devno, req);
@@ -1628,7 +1638,6 @@
 		dasd_profile_start(device, cqr, req);
 		nr_queued++;
 	}
-	bdput(bdev);
 }
 
 /*
@@ -1707,11 +1716,9 @@
 			__dasd_process_erp(device, cqr);
 			continue;
 		}
-		/* Dechain request from device request queue ... */
+		/* Rechain request on device request queue */
 		cqr->endclk = get_clock();
-		list_del(&cqr->list);
-		/* ... and add it to list of flushed requests. */
-		list_add_tail(&cqr->list, &flush_queue);
+		list_move_tail(&cqr->list, &flush_queue);
 	}
 	spin_unlock_irq(get_irq_lock(device->devinfo.irq));
 	/* Now call the callback function of flushed requests */
@@ -2046,10 +2053,8 @@
 			    &device->request_queue_lock);
 	if (rc)
 		return rc;
-	elevator_exit(device->request_queue, &device->request_queue->elevator);
-	rc = elevator_init(device->request_queue,
-			   &device->request_queue->elevator,
-			   elevator_noop);
+	elevator_exit(device->request_queue);
+	rc = elevator_init(device->request_queue, &elevator_noop);
 	if (rc) {
 		blk_cleanup_queue(device->request_queue);
 		return rc;
diff -Nru a/drivers/s390/block/dasd_devmap.c b/drivers/s390/block/dasd_devmap.c
--- a/drivers/s390/block/dasd_devmap.c	Mon Oct  7 11:25:35 2002
+++ b/drivers/s390/block/dasd_devmap.c	Mon Oct  7 11:25:35 2002
@@ -449,6 +449,15 @@
 }
 
 /*
+ * Find the devmap for a device corresponding to a block_device.
+ */
+dasd_devmap_t *
+dasd_devmap_from_bdev(struct block_device *bdev)
+{
+	return dasd_devmap_from_kdev(to_kdev_t(bdev->bd_dev));
+}
+
+/*
  * Find the device structure for device number devno. If it does not
  * exists yet, allocate it. Increase the reference counter in the device
  * structure and return a pointer to it.
diff -Nru a/drivers/s390/block/dasd_diag.c b/drivers/s390/block/dasd_diag.c
--- a/drivers/s390/block/dasd_diag.c	Mon Oct  7 11:25:25 2002
+++ b/drivers/s390/block/dasd_diag.c	Mon Oct  7 11:25:25 2002
@@ -21,6 +21,7 @@
 #include <linux/kernel.h>
 #include <linux/slab.h>
 #include <linux/hdreg.h>	/* HDIO_GETGEO			    */
+#include <linux/bio.h>
 
 #include <asm/dasd.h>
 #include <asm/debug.h>
@@ -53,31 +54,6 @@
 	diag_bio_t bio[0];
 } dasd_diag_req_t;
 
-
-static __inline__ int
-dia210(void *devchar)
-{
-	int rc;
-
-	__asm__ __volatile__("	  diag	%1,0,0x210\n"
-			     "0:  ipm	%0\n"
-			     "	  srl	%0,28\n"
-			     "1:\n"
-			     ".section .fixup,\"ax\"\n"
-			     "2:  lhi	%0,3\n"
-			     "	  bras	1,3f\n"
-			     "	  .long 1b\n"
-			     "3:  l	1,0(1)\n"
-			     "	  br	1\n"
-			     ".previous\n"
-			     ".section __ex_table,\"a\"\n"
-			     "	  .align 4\n"
-			     "	  .long 0b,2b\n" ".previous\n":"=d"(rc)
-			     :"d"((void *) __pa(devchar))
-			     :"1");
-	return rc;
-}
-
 static __inline__ int
 dia250(void *iob, int cmd)
 {
@@ -155,7 +131,7 @@
 	private->iob.key = 0;
 	private->iob.flags = 2;	/* do asynchronous io */
 	private->iob.block_count = dreq->block_count;
-	private->iob.interrupt_params = (u32) cqr;
+	private->iob.interrupt_params = (u32)(addr_t) cqr;
 	private->iob.bio_list = __pa(dreq->bio);
 
 	cqr->startclk = get_clock();
@@ -196,21 +172,21 @@
 	ip = S390_lowcore.ext_params;
 
 	cpu = smp_processor_id();
-	irq_enter(cpu, -1);
+	irq_enter();
 
 	if (!ip) {		/* no intparm: unsolicited interrupt */
 		MESSAGE(KERN_DEBUG, "%s", "caught unsolicited interrupt");
-		irq_exit(cpu, -1);
+		irq_exit();
 		return;
 	}
-	cqr = (dasd_ccw_req_t *) ip;
+	cqr = (dasd_ccw_req_t *)(addr_t) ip;
 	device = (dasd_device_t *) cqr->device;
 	if (strncmp(device->discipline->ebcname, (char *) &cqr->magic, 4)) {
 		DEV_MESSAGE(KERN_WARNING, device,
 			    " magic number of dasd_ccw_req_t 0x%08X doesn't"
 			    " match discipline 0x%08X",
 			    cqr->magic, *(int *) (&device->discipline->name));
-		irq_exit(cpu, -1);
+		irq_exit();
 		return;
 	}
 
@@ -244,8 +220,7 @@
 	dasd_schedule_bh(device);
 
 	spin_unlock_irqrestore(get_irq_lock(device->devinfo.irq), flags);
-	irq_exit(cpu, -1);
-
+	irq_exit();
 }
 
 static int
@@ -273,7 +248,7 @@
 	rdc_data->dev_nr = device->devinfo.devno;
 	rdc_data->rdc_len = sizeof (dasd_diag_characteristics_t);
 
-	rc = dia210(rdc_data);
+	rc = diag210((diag210_t *) rdc_data);
 	if (rc)
 		return -ENOTSUPP;
 
diff -Nru a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c
--- a/drivers/s390/block/dasd_eckd.c	Mon Oct  7 11:25:25 2002
+++ b/drivers/s390/block/dasd_eckd.c	Mon Oct  7 11:25:25 2002
@@ -29,6 +29,7 @@
 #include <linux/kernel.h>
 #include <linux/slab.h>
 #include <linux/hdreg.h>	/* HDIO_GETGEO			    */
+#include <linux/bio.h>
 
 #include <asm/debug.h>
 #include <asm/idals.h>
@@ -69,7 +70,6 @@
 	attrib_data_t attrib;	/* e.g. cache operations */
 } dasd_eckd_private_t;
 
-#ifdef CONFIG_DASD_DYNAMIC
 static
 devreg_t dasd_eckd_known_devices[] = {
 	{
@@ -94,7 +94,6 @@
 		oper_func:dasd_oper_handler
 	}
 };
-#endif
 
 static const int sizes_trk0[] = { 28, 148, 84 };
 #define LABEL_SIZE 140
@@ -1092,7 +1091,8 @@
  * Buils a channel programm to releases a prior reserved 
  * (see dasd_eckd_reserve) device.
  */
-static int dasd_eckd_release(void *inp, int no, long args)
+static int
+dasd_eckd_release(struct block_device *bdev, int no, long args)
 {
 	dasd_devmap_t *devmap;
 	dasd_device_t *device;
@@ -1101,7 +1101,7 @@
 
 	if (!capable(CAP_SYS_ADMIN))
 		return -EACCES;
-	devmap = dasd_devmap_from_kdev(((struct inode *) inp)->i_rdev);
+	devmap = dasd_devmap_from_bdev(bdev);
 	device = (devmap != NULL) ?
 		dasd_get_device(devmap) : ERR_PTR(-ENODEV);
 	if (IS_ERR(device))
@@ -1134,7 +1134,8 @@
  * 'timeout the request'. This leads to an terminate IO if 
  * the interrupt is outstanding for a certain time. 
  */
-static int dasd_eckd_reserve(void *inp, int no, long args)
+static int
+dasd_eckd_reserve(struct block_device *bdev, int no, long args)
 {
 	dasd_devmap_t *devmap;
 	dasd_device_t *device;
@@ -1143,7 +1144,7 @@
 
 	if (!capable(CAP_SYS_ADMIN))
 		return -EACCES;
-	devmap = dasd_devmap_from_kdev(((struct inode *) inp)->i_rdev);
+	devmap = dasd_devmap_from_bdev(bdev);
 	device = (devmap != NULL) ?
 		dasd_get_device(devmap) : ERR_PTR(-ENODEV);
 	if (IS_ERR(device))
@@ -1168,7 +1169,7 @@
 
 	if (rc == -EIO) {
 		/* Request got an eror or has been timed out. */
-		dasd_eckd_release(inp, no, args);
+		dasd_eckd_release(bdev, no, args);
 	}
 	dasd_kfree_request(cqr, cqr->device);
 	dasd_put_device(devmap);
@@ -1180,7 +1181,8 @@
  * Buils a channel programm to break a device's reservation. 
  * (unconditional reserve)
  */
-static int dasd_eckd_steal_lock(void *inp, int no, long args)
+static int
+dasd_eckd_steal_lock(struct block_device *bdev, int no, long args)
 {
 	dasd_devmap_t *devmap;
 	dasd_device_t *device;
@@ -1189,7 +1191,7 @@
 
 	if (!capable(CAP_SYS_ADMIN))
 		return -EACCES;
-	devmap = dasd_devmap_from_kdev(((struct inode *) inp)->i_rdev);
+	devmap = dasd_devmap_from_bdev(bdev);
 	device = (devmap != NULL) ?
 		dasd_get_device(devmap) : ERR_PTR(-ENODEV);
 	if (IS_ERR(device))
@@ -1213,7 +1215,7 @@
 
 	if (rc == -EIO) {
 		/* Request got an eror or has been timed out. */
-		dasd_eckd_release(inp, no, args);
+		dasd_eckd_release(bdev, no, args);
 	}
 	dasd_kfree_request(cqr, cqr->device);
 	dasd_put_device(devmap);
@@ -1223,7 +1225,8 @@
 /*
  * Read performance statistics
  */
-static int dasd_eckd_performance(void *inp, int no, long args)
+static int
+dasd_eckd_performance(struct block_device *bdev, int no, long args)
 {
 	dasd_devmap_t *devmap;
 	dasd_device_t *device;
@@ -1233,7 +1236,7 @@
 	ccw1_t *ccw;
 	int rc;
 
-	devmap = dasd_devmap_from_kdev(((struct inode *) inp)->i_rdev);
+	devmap = dasd_devmap_from_bdev(bdev);
 	device = (devmap != NULL) ?
 		dasd_get_device(devmap) : ERR_PTR(-ENODEV);
 	if (IS_ERR(device))
@@ -1292,7 +1295,8 @@
  * Set attributes (cache operations)
  * Stores the attributes for cache operation to be used in Define Extend (DE).
  */
-static int dasd_eckd_set_attrib(void *inp, int no, long args)
+static int
+dasd_eckd_set_attrib(struct block_device *bdev, int no, long args)
 {
 	dasd_devmap_t *devmap;
 	dasd_device_t *device;
@@ -1304,7 +1308,7 @@
 	if (!args)
 		return -EINVAL;
 
-	devmap = dasd_devmap_from_kdev(((struct inode *) inp)->i_rdev);
+	devmap = dasd_devmap_from_bdev(bdev);
 	device = (devmap != NULL) ?
 		dasd_get_device(devmap) : ERR_PTR(-ENODEV);
 	if (IS_ERR(device))
@@ -1452,10 +1456,8 @@
 	ASCEBC(dasd_eckd_discipline.ebcname, 4);
 	dasd_discipline_add(&dasd_eckd_discipline);
 
-#ifdef CONFIG_DASD_DYNAMIC
 	for (i = 0; i < sizeof(dasd_eckd_known_devices)/sizeof(devreg_t); i++)
 			s390_device_register(&dasd_eckd_known_devices[i]);
-#endif
 	return 0;
 }
 
@@ -1464,10 +1466,8 @@
 {
 	int i;
 
-#ifdef CONFIG_DASD_DYNAMIC
 	for (i = 0; i < sizeof(dasd_eckd_known_devices)/sizeof(devreg_t); i++)
 		s390_device_unregister(&dasd_eckd_known_devices[i]);
-#endif				/* CONFIG_DASD_DYNAMIC */
 
 	dasd_discipline_del(&dasd_eckd_discipline);
 
diff -Nru a/drivers/s390/block/dasd_fba.c b/drivers/s390/block/dasd_fba.c
--- a/drivers/s390/block/dasd_fba.c	Mon Oct  7 11:25:36 2002
+++ b/drivers/s390/block/dasd_fba.c	Mon Oct  7 11:25:36 2002
@@ -16,6 +16,7 @@
 
 #include <linux/slab.h>
 #include <linux/hdreg.h>	/* HDIO_GETGEO			    */
+#include <linux/bio.h>
 
 #include <asm/idals.h>
 #include <asm/ebcdic.h>
@@ -43,7 +44,6 @@
 	dasd_fba_characteristics_t rdc_data;
 } dasd_fba_private_t;
 
-#ifdef CONFIG_DASD_DYNAMIC
 static
 devreg_t dasd_fba_known_devices[] = {
 	{
@@ -59,7 +59,6 @@
 		oper_func:dasd_oper_handler
 	}
 };
-#endif
 
 static inline void
 define_extent(ccw1_t * ccw, DE_fba_data_t *data, int rw,
@@ -417,10 +416,8 @@
 
 	ASCEBC(dasd_fba_discipline.ebcname, 4);
 	dasd_discipline_add(&dasd_fba_discipline);
-#ifdef CONFIG_DASD_DYNAMIC
 	for (i = 0; i < sizeof(dasd_fba_known_devices) / sizeof(devreg_t); i++)
 		s390_device_register(&dasd_fba_known_devices[i]);
-#endif
 	return 0;
 }
 
@@ -429,10 +426,8 @@
 {
 	int i;
 
-#ifdef CONFIG_DASD_DYNAMIC
 	for (i = 0; i < sizeof(dasd_fba_known_devices) / sizeof(devreg_t); i++)
 		s390_device_unregister(&dasd_fba_known_devices[i]);
-#endif
 	dasd_discipline_del(&dasd_fba_discipline);
 }
 
diff -Nru a/drivers/s390/block/dasd_genhd.c b/drivers/s390/block/dasd_genhd.c
--- a/drivers/s390/block/dasd_genhd.c	Mon Oct  7 11:25:36 2002
+++ b/drivers/s390/block/dasd_genhd.c	Mon Oct  7 11:25:36 2002
@@ -33,7 +33,6 @@
 struct major_info {
 	struct list_head list;
 	int major;
-	struct gendisk disks[DASD_PER_MAJOR];
 };
 
 /*
@@ -65,12 +64,8 @@
 dasd_register_major(int major)
 {
 	struct major_info *mi;
-	int new_major, rc;
-	struct list_head *l;
-	int index;
-	int i;
+	int new_major;
 
-	rc = 0;
 	/* Allocate major info structure. */
 	mi = kmalloc(sizeof(struct major_info), GFP_KERNEL);
 
@@ -79,66 +74,39 @@
 		MESSAGE(KERN_WARNING, "%s",
 			"Cannot get memory to allocate another "
 			"major number");
-		rc = -ENOMEM;
-		goto out_error;
+		return -ENOMEM;
 	}
 
 	/* Register block device. */
 	new_major = register_blkdev(major, "dasd", &dasd_device_operations);
 	if (new_major < 0) {
 		MESSAGE(KERN_WARNING,
-			"Cannot register to major no %d, rc = %d", major, rc);
-		rc = new_major;
-		goto out_error;
+			"Cannot register to major no %d, rc = %d",
+			major, new_major);
+		kfree(mi);
+		return new_major;
 	}
 	if (major != 0)
 		new_major = major;
-	
+
 	/* Initialize major info structure. */
-	memset(mi, 0, sizeof(struct major_info));
 	mi->major = new_major;
-	for (i = 0; i < DASD_PER_MAJOR; i++) {
-		struct gendisk *disk = mi->disks + i;
-		disk->major = new_major;
-		disk->first_minor = i << DASD_PARTN_BITS;
-		disk->minor_shift = DASD_PARTN_BITS;
-		disk->fops = &dasd_device_operations;
-		disk->flags = GENHD_FL_DEVFS;
-	}
 
 	/* Setup block device pointers for the new major. */
 	blk_dev[new_major].queue = dasd_get_queue;
 
+	/* Insert the new major info structure into dasd_major_info list. */
 	spin_lock(&dasd_major_lock);
-	index = 0;
-	list_for_each(l, &dasd_major_info)
-		index += DASD_PER_MAJOR;
-	for (i = 0; i < DASD_PER_MAJOR; i++, index++) {
-		name = mi->disks[i].disk_name;
-		sprintf(name, "dasd");
-		name += 4;
-		if (index > 701)
-			*name++ = 'a' + (((index - 702) / 676) % 26);
-		if (index > 25)
-			*name++ = 'a' + (((index - 26) / 26) % 26);
-		sprintf(name, "%c", 'a' + (index % 26));
-	}
 	list_add_tail(&mi->list, &dasd_major_info);
 	spin_unlock(&dasd_major_lock);
 
 	return 0;
-
-	/* Something failed. Do the cleanup and return rc. */
-out_error:
-	/* We rely on kfree to do the != NULL check. */
-	kfree(mi);
-	return rc;
 }
 
 static void
 dasd_unregister_major(struct major_info * mi)
 {
-	int major, rc;
+	int rc;
 
 	if (mi == NULL)
 		return;
@@ -149,99 +117,151 @@
 	spin_unlock(&dasd_major_lock);
 
 	/* Clear block device pointers. */
-	major = mi->major;
-	blk_dev[major].queue = NULL;
+	blk_dev[mi->major].queue = NULL;
 
-	rc = unregister_blkdev(major, "dasd");
+	rc = unregister_blkdev(mi->major, "dasd");
 	if (rc < 0)
 		MESSAGE(KERN_WARNING,
 			"Cannot unregister from major no %d, rc = %d",
-			major, rc);
+			mi->major, rc);
 
 	/* Free memory. */
 	kfree(mi);
 }
 
 /*
- * Dynamically allocate a new major for dasd devices.
+ * This one is needed for naming 18000+ possible dasd devices.
+ *   dasda - dasdz : 26 devices
+ *   dasdaa - dasdzz : 676 devices, added up = 702
+ *   dasdaaa - dasdzzz : 17576 devices, added up = 18278
  */
 int
-dasd_gendisk_new_major(void)
+dasd_device_name(char *str, int index, int partition)
 {
-	int rc;
-	
-	rc = dasd_register_major(0);
-	if (rc)
-		DBF_EXC(DBF_ALERT, "%s", "out of major numbers!");
-	return rc;
+	int len;
+
+	if (partition > DASD_PARTN_MASK)
+		return -EINVAL;
+
+	len = sprintf(str, "dasd");
+	if (index > 25) {
+		if (index > 701)
+			len += sprintf(str + len, "%c",
+				       'a' + (((index - 702) / 676) % 26));
+		len += sprintf(str + len, "%c",
+			       'a' + (((index - 26) / 26) % 26));
+	}
+	len += sprintf(str + len, "%c", 'a' + (index % 26));
+
+	if (partition)
+		len += sprintf(str + len, "%d", partition);
+	return 0;
 }
 
 /*
- * Return pointer to gendisk structure by kdev.
+ * Allocate gendisk structure for devindex.
  */
-static struct gendisk *dasd_gendisk_by_dev(kdev_t dev)
+struct gendisk *
+dasd_gendisk_alloc(int devindex)
 {
 	struct list_head *l;
 	struct major_info *mi;
 	struct gendisk *gdp;
-	int major = major(dev);
+	int index, len, rc;
 
-	spin_lock(&dasd_major_lock);
-	gdp = NULL;
-	list_for_each(l, &dasd_major_info) {
-		mi = list_entry(l, struct major_info, list);
-		if (mi->major == major) {
-			gdp = &mi->disks[minor(dev) >> DASD_PARTN_BITS];
+	/* Make sure the major for this device exists. */
+	mi = NULL;
+	while (1) {
+		spin_lock(&dasd_major_lock);
+		index = devindex;
+		list_for_each(l, &dasd_major_info) {
+			mi = list_entry(l, struct major_info, list);
+			if (index < DASD_PER_MAJOR)
+				break;
+			index -= DASD_PER_MAJOR;
+		}
+		spin_unlock(&dasd_major_lock);
+		if (index < DASD_PER_MAJOR)
 			break;
+		rc = dasd_register_major(0);
+		if (rc) {
+			DBF_EXC(DBF_ALERT, "%s", "out of major numbers!");
+			return ERR_PTR(rc);
 		}
 	}
-	spin_unlock(&dasd_major_lock);
+
+	gdp = alloc_disk();
+	if (!gdp)
+		return ERR_PTR(-ENOMEM);
+
+	/* Initialize gendisk structure. */
+	memset(gdp, 0, sizeof(struct gendisk));
+	gdp->major = mi->major;
+	gdp->first_minor = index << DASD_PARTN_BITS;
+	gdp->minor_shift = DASD_PARTN_BITS;
+	gdp->fops = &dasd_device_operations;
+
+	/*
+	 * Set device name.
+	 *   dasda - dasdz : 26 devices
+	 *   dasdaa - dasdzz : 676 devices, added up = 702
+	 *   dasdaaa - dasdzzz : 17576 devices, added up = 18278
+	 */
+	len = sprintf(gdp->disk_name, "dasd");
+	if (devindex > 25) {
+		if (devindex > 701)
+			len += sprintf(gdp->disk_name + len, "%c",
+				       'a' + (((devindex - 702) / 676) % 26));
+		len += sprintf(gdp->disk_name + len, "%c",
+			       'a' + (((devindex - 26) / 26) % 26));
+	}
+	len += sprintf(gdp->disk_name + len, "%c", 'a' + (devindex % 26));
+
 	return gdp;
 }
 
 /*
- * Return pointer to gendisk structure by devindex.
+ * Return devindex of first device using a specific major number.
  */
-struct gendisk *
-dasd_gendisk_from_devindex(int devindex)
+int dasd_gendisk_major_index(int major)
 {
 	struct list_head *l;
 	struct major_info *mi;
-	struct gendisk *gdp;
+	int devindex, rc;
 
 	spin_lock(&dasd_major_lock);
-	gdp = NULL;
+	rc = -EINVAL;
+	devindex = 0;
 	list_for_each(l, &dasd_major_info) {
 		mi = list_entry(l, struct major_info, list);
-		if (devindex < DASD_PER_MAJOR) {
-			gdp = &mi->disks[devindex];
+		if (mi->major == major) {
+			rc = devindex;
 			break;
 		}
-		devindex -= DASD_PER_MAJOR;
+		devindex += DASD_PER_MAJOR;
 	}
 	spin_unlock(&dasd_major_lock);
-	return gdp;
+	return rc;
 }
 
 /*
- * Return devindex of first device using a specifiy major number.
+ * Return major number for device with device index devindex.
  */
-int dasd_gendisk_major_index(int major)
+int dasd_gendisk_index_major(int devindex)
 {
 	struct list_head *l;
 	struct major_info *mi;
-	int devindex, rc;
+	int rc;
 
 	spin_lock(&dasd_major_lock);
-	rc = -EINVAL;
-	devindex = 0;
+	rc = -ENODEV;
 	list_for_each(l, &dasd_major_info) {
 		mi = list_entry(l, struct major_info, list);
-		if (mi->major == major) {
-			rc = devindex;
+		if (devindex < DASD_PER_MAJOR) {
+			rc = mi->major;
 			break;
 		}
-		devindex += DASD_PER_MAJOR;
+		devindex -= DASD_PER_MAJOR;
 	}
 	spin_unlock(&dasd_major_lock);
 	return rc;
@@ -253,11 +273,9 @@
 void
 dasd_setup_partitions(dasd_device_t * device)
 {
-	struct gendisk *disk = dasd_gendisk_by_dev(device->kdev);
-	if (disk == NULL)
-		return;
-	set_capacity(disk, device->blocks << device->s2b_shift);
-	add_disk(disk);
+	/* Make the disk known. */
+	set_capacity(device->gdp, device->blocks << device->s2b_shift);
+	add_disk(device->gdp);
 }
 
 /*
@@ -267,13 +285,7 @@
 void
 dasd_destroy_partitions(dasd_device_t * device)
 {
-	struct gendisk *disk = dasd_gendisk_by_dev(device->kdev);
-	int minor, i;
-
-	if (disk == NULL)
-		return;
-
-	del_gendisk(disk);
+	del_gendisk(device->gdp);
 }
 
 int
@@ -294,6 +306,7 @@
 dasd_gendisk_exit(void)
 {
 	struct list_head *l, *n;
+
 	spin_lock(&dasd_major_lock);
 	list_for_each_safe(l, n, &dasd_major_info)
 		dasd_unregister_major(list_entry(l, struct major_info, list));
diff -Nru a/drivers/s390/block/dasd_int.h b/drivers/s390/block/dasd_int.h
--- a/drivers/s390/block/dasd_int.h	Mon Oct  7 11:25:36 2002
+++ b/drivers/s390/block/dasd_int.h	Mon Oct  7 11:25:36 2002
@@ -64,12 +64,12 @@
 #include <asm/irq.h>
 #include <asm/s390dyn.h>
 
-#define CONFIG_DASD_DYNAMIC
-
 /*
  * SECTION: Type definitions
  */
-typedef int (*dasd_ioctl_fn_t) (void *inp, int no, long args);
+struct dasd_device_t;
+
+typedef int (*dasd_ioctl_fn_t) (struct block_device *bdev, int no, long args);
 
 typedef struct {
 	struct list_head list;
@@ -139,9 +139,8 @@
 /* messages to be written via klogd and dbf */
 #define DEV_MESSAGE(d_loglevel,d_device,d_string,d_args...)\
 do { \
-	printk(d_loglevel PRINTK_HEADER " /dev/%-7s(%3d:%3d),%04x@%02x: " \
-	       d_string "\n", d_device->name, \
-	       major(d_device->kdev), minor(d_device->kdev), \
+	printk(d_loglevel PRINTK_HEADER " %s,%04x@%02x: " \
+	       d_string "\n", d_device->gdp->disk_name, \
 	       d_device->devinfo.devno, d_device->devinfo.irq, \
 	       d_args); \
 	DBF_DEV_EVENT(DBF_ALERT, d_device, d_string, d_args); \
@@ -153,8 +152,6 @@
 	DBF_EVENT(DBF_ALERT, d_string, d_args); \
 } while(0)
 
-struct dasd_device_t;
-
 typedef struct dasd_ccw_req_t {
 	unsigned int magic;		/* Eye catcher */
         struct list_head list;		/* list_head for request queueing. */
@@ -261,14 +258,14 @@
 
 typedef struct dasd_device_t {
 	/* Block device stuff. */
-	char name[16];			/* The device name in /dev. */
-	kdev_t kdev;
+	struct gendisk *gdp;
 	devfs_handle_t devfs_entry;
 	request_queue_t *request_queue;
 	spinlock_t request_queue_lock;
 	unsigned long blocks;		/* size of volume in blocks */
 	unsigned int bp_block;		/* bytes per block */
 	unsigned int s2b_shift;		/* log2 (bp_block/512) */
+	int ro_flag;			/* read-only flag */
 
 	/* Device discipline stuff. */
 	dasd_discipline_t *discipline;
@@ -467,6 +464,7 @@
 dasd_devmap_t *dasd_devmap_from_devindex(int);
 dasd_devmap_t *dasd_devmap_from_irq(int);
 dasd_devmap_t *dasd_devmap_from_kdev(kdev_t);
+dasd_devmap_t *dasd_devmap_from_bdev(struct block_device *bdev);
 dasd_device_t *dasd_get_device(dasd_devmap_t *);
 void dasd_put_device(dasd_devmap_t *);
 
@@ -478,9 +476,9 @@
 /* externals in dasd_gendisk.c */
 int  dasd_gendisk_init(void);
 void dasd_gendisk_exit(void);
-int  dasd_gendisk_new_major(void);
 int  dasd_gendisk_major_index(int);
-struct gendisk *dasd_gendisk_from_devindex(int);
+int  dasd_gendisk_index_major(int);
+struct gendisk *dasd_gendisk_alloc(int);
 void dasd_setup_partitions(dasd_device_t *);
 void dasd_destroy_partitions(dasd_device_t *);
 
diff -Nru a/drivers/s390/block/dasd_ioctl.c b/drivers/s390/block/dasd_ioctl.c
--- a/drivers/s390/block/dasd_ioctl.c	Mon Oct  7 11:25:35 2002
+++ b/drivers/s390/block/dasd_ioctl.c	Mon Oct  7 11:25:35 2002
@@ -91,6 +91,7 @@
 	dasd_devmap_t *devmap;
 	dasd_device_t *device;
 	dasd_ioctl_list_t *ioctl;
+	struct block_device *bdev;
 	struct list_head *l;
 	const char *dir;
 	int rc;
@@ -101,13 +102,17 @@
 		PRINT_DEBUG("empty data ptr");
 		return -EINVAL;
 	}
-	devmap = dasd_devmap_from_kdev(inp->i_rdev);
+	bdev = bdget(kdev_t_to_nr(inp->i_rdev));
+	if (!bdev)
+		return -EINVAL;
+
+	devmap = dasd_devmap_from_bdev(bdev);
 	device = (devmap != NULL) ?
 		dasd_get_device(devmap) : ERR_PTR(-ENODEV);
 	if (IS_ERR(device)) {
 		MESSAGE(KERN_WARNING,
-			"No device registered as device (%d:%d)",
-			major(inp->i_rdev), minor(inp->i_rdev));
+			"No device registered as device %s", bdevname(bdev));
+		bdput(bdev);
 		return -EINVAL;
 	}
 	dir = _IOC_DIR (no) == _IOC_NONE ? "0" :
@@ -125,11 +130,12 @@
 			if (ioctl->owner) {
 				if (try_inc_mod_count(ioctl->owner) != 0)
 					continue;
-				rc = ioctl->handler(inp, no, data);
+				rc = ioctl->handler(bdev, no, data);
 				__MOD_DEC_USE_COUNT(ioctl->owner);
 			} else
-				rc = ioctl->handler(inp, no, data);
+				rc = ioctl->handler(bdev, no, data);
 			dasd_put_device(devmap);
+			bdput(bdev);
 			return rc;
 		}
 	}
@@ -138,10 +144,12 @@
 		      "unknown ioctl 0x%08x=%s'0x%x'%d(%d) data %8lx", no,
 		      dir, _IOC_TYPE(no), _IOC_NR(no), _IOC_SIZE(no), data);
 	dasd_put_device(devmap);
+	bdput(bdev);
 	return -ENOTTY;
 }
 
-static int dasd_ioctl_api_version(void *inp, int no, long args)
+static int
+dasd_ioctl_api_version(struct block_device *bdev, int no, long args)
 {
 	int ver = DASD_API_VERSION;
 	return put_user(ver, (int *) args);
@@ -150,7 +158,8 @@
 /*
  * Enable device.
  */
-static int dasd_ioctl_enable(void *inp, int no, long args)
+static int
+dasd_ioctl_enable(struct block_device *bdev, int no, long args)
 {
 	dasd_devmap_t *devmap;
 	dasd_device_t *device;
@@ -158,7 +167,7 @@
 
 	if (!capable(CAP_SYS_ADMIN))
 		return -EACCES;
-	devmap = dasd_devmap_from_kdev(((struct inode *) inp)->i_rdev);
+	devmap = dasd_devmap_from_bdev(bdev);
 	device = (devmap != NULL) ?
 		dasd_get_device(devmap) : ERR_PTR(-ENODEV);
 	if (IS_ERR(device))
@@ -172,14 +181,15 @@
 /*
  * Disable device.
  */
-static int dasd_ioctl_disable(void *inp, int no, long args)
+static int
+dasd_ioctl_disable(struct block_device *bdev, int no, long args)
 {
 	dasd_devmap_t *devmap;
 	dasd_device_t *device;
 
 	if (!capable(CAP_SYS_ADMIN))
 		return -EACCES;
-	devmap = dasd_devmap_from_kdev(((struct inode *) inp)->i_rdev);
+	devmap = dasd_devmap_from_bdev(bdev);
 	device = (devmap != NULL) ?
 		dasd_get_device(devmap) : ERR_PTR(-ENODEV);
 	if (IS_ERR(device))
@@ -245,7 +255,8 @@
 /*
  * Format device.
  */
-static int dasd_ioctl_format(void *inp, int no, long args)
+static int
+dasd_ioctl_format(struct block_device *bdev, int no, long args)
 {
 	dasd_devmap_t *devmap;
 	dasd_device_t *device;
@@ -257,8 +268,8 @@
 	if (!args)
 		return -EINVAL;
 	/* fdata == NULL is no longer a valid arg to dasd_format ! */
-	partn = minor(((struct inode *) inp)->i_rdev) & DASD_PARTN_MASK;
-	devmap = dasd_devmap_from_kdev(((struct inode *) inp)->i_rdev);
+	partn = MINOR(bdev->bd_dev) & DASD_PARTN_MASK;
+	devmap = dasd_devmap_from_bdev(bdev);
 	device = (devmap != NULL) ?
 		dasd_get_device(devmap) : ERR_PTR(-ENODEV);
 	if (IS_ERR(device))
@@ -283,14 +294,15 @@
 /*
  * Reset device profile information
  */
-static int dasd_ioctl_reset_profile(void *inp, int no, long args)
+static int
+dasd_ioctl_reset_profile(struct block_device *bdev, int no, long args)
 {
 	dasd_devmap_t *devmap;
 	dasd_device_t *device;
 
 	if (!capable(CAP_SYS_ADMIN))
 		return -EACCES;
-	devmap = dasd_devmap_from_kdev(((struct inode *) inp)->i_rdev);
+	devmap = dasd_devmap_from_bdev(bdev);
 	device = (devmap != NULL) ?
 		dasd_get_device(devmap) : ERR_PTR(-ENODEV);
 	if (IS_ERR(device))
@@ -303,13 +315,14 @@
 /*
  * Return device profile information
  */
-static int dasd_ioctl_read_profile(void *inp, int no, long args)
+static int
+dasd_ioctl_read_profile(struct block_device *bdev, int no, long args)
 {
 	dasd_devmap_t *devmap;
 	dasd_device_t *device;
 	int rc;
 
-	devmap = dasd_devmap_from_kdev(((struct inode *) inp)->i_rdev);
+	devmap = dasd_devmap_from_bdev(bdev);
 	device = (devmap != NULL) ?
 		dasd_get_device(devmap) : ERR_PTR(-ENODEV);
 	if (IS_ERR(device))
@@ -322,12 +335,14 @@
 	return rc;
 }
 #else
-static int dasd_ioctl_reset_profile(void *inp, int no, long args)
+static int
+dasd_ioctl_reset_profile(struct block_device *bdev, int no, long args)
 {
 	return -ENOSYS;
 }
 
-static int dasd_ioctl_read_profile(void *inp, int no, long args)
+static int
+dasd_ioctl_read_profile(struct block_device *bdev, int no, long args)
 {
 	return -ENOSYS;
 }
@@ -336,15 +351,16 @@
 /*
  * Return dasd information. Used for BIODASDINFO and BIODASDINFO2.
  */
-static int dasd_ioctl_information(void *inp, int no, long args)
+static int
+dasd_ioctl_information(struct block_device *bdev, int no, long args)
 {
 	dasd_devmap_t *devmap;
 	dasd_device_t *device;
-	dasd_information2_t dasd_info;
+	dasd_information2_t *dasd_info;
 	unsigned long flags;
 	int rc;
 
-	devmap = dasd_devmap_from_kdev(((struct inode *) inp)->i_rdev);
+	devmap = dasd_devmap_from_bdev(bdev);
 	device = (devmap != NULL) ?
 		dasd_get_device(devmap) : ERR_PTR(-ENODEV);
 	if (IS_ERR(device))
@@ -354,20 +370,26 @@
 		return -EINVAL;
 	}
 
-	rc = device->discipline->fill_info(device, &dasd_info);
+	dasd_info = kmalloc(sizeof(dasd_information2_t), GFP_KERNEL);
+	if (dasd_info == NULL) {
+		dasd_put_device(devmap);
+		return -ENOMEM;
+	}
+	rc = device->discipline->fill_info(device, dasd_info);
 	if (rc) {
 		dasd_put_device(devmap);
+		kfree(dasd_info);
 		return rc;
 	}
 
-	dasd_info.devno = device->devinfo.devno;
-	dasd_info.schid = device->devinfo.irq;
-	dasd_info.cu_type = device->devinfo.sid_data.cu_type;
-	dasd_info.cu_model = device->devinfo.sid_data.cu_model;
-	dasd_info.dev_type = device->devinfo.sid_data.dev_type;
-	dasd_info.dev_model = device->devinfo.sid_data.dev_model;
-	dasd_info.open_count = atomic_read(&device->open_count);
-	dasd_info.status = device->state;
+	dasd_info->devno = device->devinfo.devno;
+	dasd_info->schid = device->devinfo.irq;
+	dasd_info->cu_type = device->devinfo.sid_data.cu_type;
+	dasd_info->cu_model = device->devinfo.sid_data.cu_model;
+	dasd_info->dev_type = device->devinfo.sid_data.dev_type;
+	dasd_info->dev_model = device->devinfo.sid_data.dev_model;
+	dasd_info->open_count = atomic_read(&device->open_count);
+	dasd_info->status = device->state;
 	
 	/*
 	 * check if device is really formatted
@@ -375,16 +397,16 @@
 	 */
 	if ((device->state < DASD_STATE_READY) ||
 	    (dasd_check_blocksize(device->bp_block)))
-		dasd_info.format = DASD_FORMAT_NONE;
+		dasd_info->format = DASD_FORMAT_NONE;
 	
-	dasd_info.features = devmap->features;
+	dasd_info->features = devmap->features;
 	
 	if (device->discipline)
-		memcpy(dasd_info.type, device->discipline->name, 4);
+		memcpy(dasd_info->type, device->discipline->name, 4);
 	else
-		memcpy(dasd_info.type, "none", 4);
-	dasd_info.req_queue_len = 0;
-	dasd_info.chanq_len = 0;
+		memcpy(dasd_info->type, "none", 4);
+	dasd_info->req_queue_len = 0;
+	dasd_info->chanq_len = 0;
 	if (device->request_queue->request_fn) {
 		struct list_head *l;
 #ifdef DASD_EXTENDED_PROFILING
@@ -392,45 +414,46 @@
 			struct list_head *l;
 			spin_lock_irqsave(&device->lock, flags);
 			list_for_each(l, &device->request_queue->queue_head)
-				dasd_info.req_queue_len++;
+				dasd_info->req_queue_len++;
 			spin_unlock_irqrestore(&device->lock, flags);
 		}
 #endif				/* DASD_EXTENDED_PROFILING */
 		spin_lock_irqsave(get_irq_lock(device->devinfo.irq), flags);
 		list_for_each(l, &device->ccw_queue)
-			dasd_info.chanq_len++;
+			dasd_info->chanq_len++;
 		spin_unlock_irqrestore(get_irq_lock(device->devinfo.irq),
 				       flags);
 	}
 	
 	rc = 0;
-	if (copy_to_user((long *) args, (long *) &dasd_info,
+	if (copy_to_user((long *) args, (long *) dasd_info,
 			 ((no == (unsigned int) BIODASDINFO2) ?
 			  sizeof (dasd_information2_t) :
 			  sizeof (dasd_information_t))))
 		rc = -EFAULT;
 	dasd_put_device(devmap);
+	kfree(dasd_info);
 	return rc;
 }
 
 /*
  * Set read only
  */
-static int dasd_ioctl_set_ro(void *inp, int no, long args)
+static int
+dasd_ioctl_set_ro(struct block_device *bdev, int no, long args)
 {
 	dasd_devmap_t *devmap;
 	dasd_device_t *device;
-	int major, minor;
 	int intval, i;
 
 	if (!capable(CAP_SYS_ADMIN))
 		return -EACCES;
-	if (minor(((struct inode *) inp)->i_rdev) & DASD_PARTN_MASK)
+	if (MINOR(bdev->bd_dev) & DASD_PARTN_MASK)
 		// ro setting is not allowed for partitions
 		return -EINVAL;
 	if (get_user(intval, (int *) args))
 		return -EFAULT;
-	devmap = dasd_devmap_from_kdev(((struct inode *) inp)->i_rdev);
+	devmap = dasd_devmap_from_bdev(bdev);
 	device = (devmap != NULL) ?
 		dasd_get_device(devmap) : ERR_PTR(-ENODEV);
 	if (IS_ERR(device))
@@ -439,10 +462,9 @@
 		devmap->features |= DASD_FEATURE_READONLY;
 	else
 		devmap->features &= ~DASD_FEATURE_READONLY;
-	major = major(device->kdev);
-	minor = minor(device->kdev);
 	for (i = 0; i < (1 << DASD_PARTN_BITS); i++)
-		set_device_ro(mk_kdev(major, minor + i), intval);
+		set_device_ro(to_kdev_t(bdev->bd_dev + i), intval);
+	device->ro_flag = intval;
 	dasd_put_device(devmap);
 	return 0;
 }
@@ -450,16 +472,15 @@
 /*
  * Return disk geometry.
  */
-static int dasd_ioctl_getgeo(void *inp, int no, long args)
+static int
+dasd_ioctl_getgeo(struct block_device *bdev, int no, long args)
 {
 	struct hd_geometry geo = { 0, };
-	struct inode *inode = inp;
 	dasd_devmap_t *devmap;
 	dasd_device_t *device;
-	kdev_t kdev = inode->i_rdev;
 	int rc;
 
-	devmap = dasd_devmap_from_kdev(kdev);
+	devmap = dasd_devmap_from_bdev(bdev);
 	device = (devmap != NULL) ?
 		dasd_get_device(devmap) : ERR_PTR(-ENODEV);
 	if (IS_ERR(device))
@@ -468,7 +489,7 @@
 	if (device != NULL && device->discipline != NULL &&
 	    device->discipline->fill_geometry != NULL) {
 		device->discipline->fill_geometry(device, &geo);
-		geo.start = get_start_sect(inode->i_bdev);
+		geo.start = get_start_sect(bdev);
 		if (copy_to_user((struct hd_geometry *) args, &geo,
 				 sizeof (struct hd_geometry)))
 			rc = -EFAULT;
diff -Nru a/drivers/s390/block/dasd_proc.c b/drivers/s390/block/dasd_proc.c
--- a/drivers/s390/block/dasd_proc.c	Mon Oct  7 11:25:26 2002
+++ b/drivers/s390/block/dasd_proc.c	Mon Oct  7 11:25:26 2002
@@ -15,6 +15,7 @@
 #include <linux/config.h>
 #include <linux/version.h>
 #include <linux/ctype.h>
+#include <linux/vmalloc.h>
 
 #include <asm/debug.h>
 #include <asm/irq.h>
@@ -123,7 +124,7 @@
 	}
 	features = dasd_feature_list(str, &str);
 	/* Negative numbers in from/to/features indicate errors */
-	if (from < 0 || to < 0 || features < 0)
+	if (from < 0 || to < 0 || from > 65546 || to > 65536 || features < 0)
 		goto out_error;
 
 	if (add_or_set == 0) {
@@ -152,10 +153,7 @@
 dasd_devices_print(dasd_devmap_t *devmap, char *str)
 {
 	dasd_device_t *device;
-	struct gendisk *gdp;
-	char buffer[7];
 	char *substr;
-	int minor;
 	int len;
 
 	device = dasd_get_device(devmap);
@@ -169,11 +167,10 @@
 	else
 		len += sprintf(str + len, "(none)");
 	/* Print kdev. */
-	gdp = dasd_gendisk_from_devindex(devmap->devindex);
-	minor = devmap->devindex % DASD_PER_MAJOR;
-	len += sprintf(str + len, " at (%3d:%3d)", gdp->major, minor);
+	len += sprintf(str + len, " at (%3d:%3d)",
+		       device->gdp->major, device->gdp->first_minor);
 	/* Print device name. */
-	len += sprintf(str + len, " is %-7s", gdp->disk_name);
+	len += sprintf(str + len, " is %-7s", device->gdp->disk_name);
 	/* Print devices features. */
 	substr = (devmap->features & DASD_FEATURE_READONLY) ? "(ro)" : " ";
 	len += sprintf(str + len, "%4s: ", substr);
diff -Nru a/drivers/s390/block/xpram.c b/drivers/s390/block/xpram.c
--- a/drivers/s390/block/xpram.c	Mon Oct  7 11:25:36 2002
+++ b/drivers/s390/block/xpram.c	Mon Oct  7 11:25:36 2002
@@ -15,7 +15,6 @@
  *   Device specific file operations
  *        xpram_iotcl
  *        xpram_open
- *        xpram_release
  *
  * "ad-hoc" partitioning:
  *    the expanded memory can be partitioned among several devices 
@@ -36,6 +35,7 @@
 #include <linux/blkpg.h>
 #include <linux/hdreg.h>  /* HDIO_GETGEO */
 #include <linux/device.h>
+#include <linux/bio.h>
 #include <asm/uaccess.h>
 
 #define XPRAM_NAME	"xpram"
@@ -47,10 +47,13 @@
 #define PRINT_WARN(x...)	printk(KERN_WARNING XPRAM_NAME " warning:" x)
 #define PRINT_ERR(x...)		printk(KERN_ERR XPRAM_NAME " error:" x)
 
-static struct device xpram_sys_device = {
-	name: "S/390 expanded memory RAM disk",
-	bus_id: "xpram",
-};
+static struct sys_device xpram_sys_device = {
+	.name = "S/390 expanded memory RAM disk",
+	.dev  = {
+		.name   = "S/390 expanded memory RAM disk",
+		.bus_id = "xpram",
+	},
+}; 
 
 typedef struct {
 	unsigned long	size;		/* size of xpram segment in pages */
@@ -59,7 +62,7 @@
 
 static xpram_device_t xpram_devices[XPRAM_MAX_DEVS];
 static int xpram_sizes[XPRAM_MAX_DEVS];
-static struct gendisk xpram_disks[XPRAM_MAX_DEVS];
+static struct gendisk *xpram_disks[XPRAM_MAX_DEVS];
 static unsigned long xpram_pages;
 static int xpram_devs;
 static devfs_handle_t xpram_devfs_handle;
@@ -312,10 +315,12 @@
 		}
 	}
 	set_bit(BIO_UPTODATE, &bio->bi_flags);
-	bio->bi_end_io(bio);
+	bytes = bio->bi_size;
+	bio->bi_size = 0;
+	bio->bi_end_io(bio, bytes, 0);
 	return 0;
 fail:
-	bio_io_error(bio);
+	bio_io_error(bio, bio->bi_size);
 	return 0;
 }
 
@@ -329,7 +334,6 @@
 	return 0;
 }
 
-
 static int xpram_ioctl (struct inode *inode, struct file *filp,
 		 unsigned int cmd, unsigned long arg)
 {
@@ -338,7 +342,7 @@
 	int idx = minor(inode->i_rdev);
 	if (idx >= xpram_devs)
 		return -ENODEV;
-	if (cmd != HDIO_GETGEO)
+ 	if (cmd != HDIO_GETGEO)
 		return -EINVAL;
 	/*
 	 * get geometry: we have to fake one...  trim the size to a
@@ -355,14 +359,12 @@
 	put_user(4, &geo->start);
 	return 0;
 }
-}
 
 static struct block_device_operations xpram_devops =
 {
 	owner:   THIS_MODULE,
 	ioctl:   xpram_ioctl,
 	open:    xpram_open,
-	release: xpram_release,
 };
 
 /*
@@ -436,7 +438,14 @@
 {
 	request_queue_t *q;
 	unsigned long offset;
-	int i, rc;
+	int i, rc = -ENOMEM;
+
+	for (i = 0; i < xpram_devs; i++) {
+		struct gendisk *disk = alloc_disk();
+		if (!disk)
+			goto out;
+		xpram_disks[i] = disk;
+	}
 
 	/*
 	 * Register xpram major.
@@ -444,7 +453,7 @@
 	rc = register_blkdev(XPRAM_MAJOR, XPRAM_NAME, &xpram_devops);
 	if (rc < 0) {
 		PRINT_ERR("Can't get xpram major %d\n", XPRAM_MAJOR);
-		return rc;
+		goto out;
 	}
 
 	xpram_devfs_handle = devfs_mk_dir (NULL, "slram", NULL);
@@ -466,7 +475,7 @@
 	 */
 	offset = 0;
 	for (i = 0; i < xpram_devs; i++) {
-		struct gendisk *disk = xpram_disks + i;
+		struct gendisk *disk = xpram_disks[i];
 		xpram_devices[i].size = xpram_sizes[i] / 4;
 		xpram_devices[i].offset = offset;
 		offset += xpram_devices[i].size;
@@ -480,6 +489,9 @@
 	}
 
 	return 0;
+out:
+	while (i--)
+		put_disk(xpram_disks[i]);
 }
 
 /*
@@ -488,11 +500,13 @@
 static void __exit xpram_exit(void)
 {
 	int i;
-	for (i = 0; i < xpram_devs; i++)
-		del_gendisk(xpram_disks + i);
+	for (i = 0; i < xpram_devs; i++) {
+		del_gendisk(xpram_disks[i]);
+		put_disk(xpram_disks[i]);
+	}
 	unregister_blkdev(XPRAM_MAJOR, XPRAM_NAME);
 	devfs_unregister(xpram_devfs_handle);
-	unregister_sys_device(&xpram_sys_device);
+	sys_device_unregister(&xpram_sys_device);
 }
 
 static int __init xpram_init(void)
@@ -510,12 +524,12 @@
 	rc = xpram_setup_sizes(xpram_pages);
 	if (rc)
 		return rc;
-	rc = register_sys_device(&xpram_sys_device);
+	rc = sys_device_register(&xpram_sys_device);
 	if (rc)
 		return rc;
 	rc = xpram_setup_blkdev();
 	if (rc)
-		unregister_sys_device(&xpram_sys_device);
+		sys_device_unregister(&xpram_sys_device);
 	return rc;
 }
 
diff -Nru a/drivers/s390/char/con3215.c b/drivers/s390/char/con3215.c
--- a/drivers/s390/char/con3215.c	Mon Oct  7 11:25:26 2002
+++ b/drivers/s390/char/con3215.c	Mon Oct  7 11:25:26 2002
@@ -89,7 +89,7 @@
         int written;                  /* number of bytes in write requests */
 	devstat_t devstat;	      /* device status structure for do_IO */
 	struct tty_struct *tty;	      /* pointer to tty structure if present */
-	struct tq_struct tqueue;      /* task queue to bottom half */
+	struct work_struct tqueue;      /* task queue to bottom half */
 	raw3215_req *queued_read;     /* pointer to queued read requests */
 	raw3215_req *queued_write;    /* pointer to queued write requests */
 	wait_queue_head_t empty_wait; /* wait queue for flushing */
@@ -341,7 +341,7 @@
  * The bottom half handler routine for 3215 devices. It tries to start
  * the next IO and wakes up processes waiting on the tty.
  */
-static void raw3215_softint(void *data)
+static void raw3215_tasklet(void *data)
 {
 	raw3215_info *raw;
 	struct tty_struct *tty;
@@ -377,12 +377,8 @@
         if (raw->flags & RAW3215_BH_PENDING)
                 return;       /* already pending */
         raw->flags |= RAW3215_BH_PENDING;
-	INIT_LIST_HEAD(&raw->tqueue.list);
-	raw->tqueue.sync = 0;
-        raw->tqueue.routine = raw3215_softint;
-        raw->tqueue.data = raw;
-        queue_task(&raw->tqueue, &tq_immediate);
-        mark_bh(IMMEDIATE_BH);
+	INIT_WORK(&raw->tqueue, raw3215_softint, raw);
+        schedule_work(&raw->tqueue);
 }
 
 /*
@@ -460,7 +456,7 @@
                 if ((raw = req->info) == NULL)
                         return;              /* That shouldn't happen ... */
 		if (req->type == RAW3215_READ && raw->tty != NULL) {
-			char *cchar;
+			unsigned int cchar;
 
 			tty = raw->tty;
                         count = 160 - req->residual;
@@ -472,14 +468,19 @@
 			if (count >= TTY_FLIPBUF_SIZE - tty->flip.count)
 				count = TTY_FLIPBUF_SIZE - tty->flip.count - 1;
 			EBCASC(raw->inbuf, count);
-			if ((cchar = ctrlchar_handle(raw->inbuf, count, tty))) {
-				if (cchar == (char *)-1)
-					goto in_out;
+			cchar = ctrlchar_handle(raw->inbuf, count, tty);
+			switch (cchar & CTRLCHAR_MASK) {
+			case CTRLCHAR_SYSRQ:
+				break;
+
+			case CTRLCHAR_CTRL:
 				tty->flip.count++;
 				*tty->flip.flag_buf_ptr++ = TTY_NORMAL;
-				*tty->flip.char_buf_ptr++ = *cchar;
+				*tty->flip.char_buf_ptr++ = cchar;
 				tty_flip_buffer_push(raw->tty);
-			} else {
+				break;
+
+			case CTRLCHAR_NONE:
 				memcpy(tty->flip.char_buf_ptr,
 				       raw->inbuf, count);
 				if (count < 2 ||
@@ -496,12 +497,13 @@
 				tty->flip.flag_buf_ptr += count;
 				tty->flip.count += count;
 				tty_flip_buffer_push(raw->tty);
+				break;
 			}
 		} else if (req->type == RAW3215_WRITE) {
 			raw->count -= req->len;
                         raw->written -= req->len;
 		} 
-in_out:
+
 		raw->flags &= ~RAW3215_WORKING;
 		raw3215_free_req(req);
 		/* check for empty wait */
@@ -698,13 +700,12 @@
 
 	if (raw->flags & RAW3215_ACTIVE)
 		return 0;
-	if (request_irq(raw->irq, raw3215_irq, SA_INTERRUPT,
-			"3215 terminal driver", &raw->devstat) != 0)
+	if (s390_request_console_irq(raw->irq, raw3215_irq, SA_INTERRUPT,
+				"3215 terminal driver", &raw->devstat) != 0)
 		return -1;
 	raw->line_pos = 0;
 	raw->flags |= RAW3215_ACTIVE;
 	s390irq_spin_lock_irqsave(raw->irq, flags);
-        set_cons_dev(raw->irq);
 	raw3215_try_io(raw);
 	s390irq_spin_unlock_irqrestore(raw->irq, flags);
 
@@ -867,8 +868,7 @@
 			kfree(raw);
 			return -ENOMEM;
 		}
-		raw->tqueue.routine = raw3215_softint;
-		raw->tqueue.data = raw;
+		INIT_WORK(&raw->tqueue, raw3215_softint, raw);
                 init_waitqueue_head(&raw->empty_wait);
 		raw3215[line] = raw;
 	}
@@ -1068,7 +1068,6 @@
 	/* Check if 3215 is to be the console */
 	if (!CONSOLE_IS_3215)
 		return;
-	irq = raw3215_find_dev(0);
 
 	/* Set the console mode for VM */
 	if (MACHINE_IS_VM) {
@@ -1076,6 +1075,22 @@
 		cpcmd("TERM AUTOCR OFF", NULL, 0);
 	}
 
+	if (console_device != -1) {
+		/* use the device number that was specified on the
+		 * command line */
+		irq = raw3215_find_dev(0);
+	} else if (MACHINE_IS_VM) {
+		/* under VM, the console is at device number 0009
+		 * by default, so try that */
+		irq = get_irq_by_devno(0x0009);
+	} else {
+		/* unlike in 2.4, we cannot autoprobe here, since
+		 * the channel subsystem is not fully initialized.
+		 * With some luck, the HWC console can take over */
+		printk(KERN_WARNING "3215 console not found\n");
+		return;
+	}
+
 	/* allocate 3215 request structures */
 	raw3215_freelist = NULL;
 	spin_lock_init(&raw3215_freelist_lock);
@@ -1085,8 +1100,6 @@
 		raw3215_freelist = req;
 	}
 
-	ctrlchar_init();
-
 #ifdef CONFIG_TN3215_CONSOLE
         raw3215[0] = raw = (raw3215_info *)
                 alloc_bootmem_low(sizeof(raw3215_info));
@@ -1095,10 +1108,9 @@
         raw->inbuf = (char *) alloc_bootmem_low(RAW3215_INBUF_SIZE);
 
 	/* Find the first console */
-	raw->irq = raw3215_find_dev(0);
+	raw->irq = irq;
 	raw->flags |= RAW3215_FIXED;
-	raw->tqueue.routine = raw3215_softint;
-	raw->tqueue.data = raw;
+	INIT_WORK(&raw->tqueue, raw3215_softint, raw);
         init_waitqueue_head(&raw->empty_wait);
 
 	/* Request the console irq */
diff -Nru a/drivers/s390/char/ctrlchar.c b/drivers/s390/char/ctrlchar.c
--- a/drivers/s390/char/ctrlchar.c	Mon Oct  7 11:25:39 2002
+++ b/drivers/s390/char/ctrlchar.c	Mon Oct  7 11:25:39 2002
@@ -8,39 +8,27 @@
  */
 
 #include <linux/config.h>
-#include <linux/types.h>
-#include <linux/tty.h>
-#include <linux/interrupt.h>
-
+#include <linux/stddef.h>
 #include <linux/sysrq.h>
+#include <linux/ctype.h>
 
-#include <asm/io.h>
-#include <asm/uaccess.h>
-#include <asm/delay.h>
-#include <asm/cpcmd.h>
-#include <asm/irq.h>
+#include "ctrlchar.h"
 
 #ifdef CONFIG_MAGIC_SYSRQ
 static int ctrlchar_sysrq_key;
-static struct tq_struct ctrlchar_tq;
 
 static void
-ctrlchar_handle_sysrq(struct tty_struct *tty) {
-	handle_sysrq(ctrlchar_sysrq_key, NULL, NULL, tty);
+ctrlchar_handle_sysrq(void *tty)
+{
+	handle_sysrq(ctrlchar_sysrq_key, NULL, tty);
 }
-#endif
-
-void ctrlchar_init(void) {
-#ifdef CONFIG_MAGIC_SYSRQ
-	static int init_done = 0;
 
-	if (init_done++)
-		return;
-	INIT_LIST_HEAD(&ctrlchar_tq.list);
-	ctrlchar_tq.sync = 0;
-	ctrlchar_tq.routine = (void (*)(void *)) ctrlchar_handle_sysrq;
+static struct tq_struct ctrlchar_tq = {
+	.list = LIST_HEAD_INIT(ctrlchar_tq.list),
+	.routine = ctrlchar_handle_sysrq,
+};
 #endif
-}
+
 
 /**
  * Check for special chars at start of input.
@@ -48,49 +36,42 @@
  * @param buf Console input buffer.
  * @param len Length of valid data in buffer.
  * @param tty The tty struct for this console.
- * @return NULL, if nothing matched, (char *)-1, if buffer contents
- *         should be ignored, otherwise pointer to char to be inserted.
+ * @return CTRLCHAR_NONE, if nothing matched,
+ *         CTRLCHAR_SYSRQ, if sysrq was encountered
+ *         otherwise char to be inserted logically or'ed
+ *         with CTRLCHAR_CTRL
  */
-char *ctrlchar_handle(const char *buf, int len, struct tty_struct *tty) {
-
-	static char ret;
-
+unsigned int
+ctrlchar_handle(const char *buf, int len, struct tty_struct *tty)
+{
 	if ((len < 2) || (len > 3))
-		return NULL;
+		return CTRLCHAR_NONE;
+
 	/* hat is 0xb1 in codepage 037 (US etc.) and thus */
 	/* converted to 0x5e in ascii ('^') */
 	if ((buf[0] != '^') && (buf[0] != '\252'))
-		return NULL;
-	switch (buf[1]) {
+		return CTRLCHAR_NONE;
+
 #ifdef CONFIG_MAGIC_SYSRQ
-		case '-':
-			if (len == 3) {
-				ctrlchar_sysrq_key = buf[2];
-				ctrlchar_tq.data = tty;
-				queue_task(&ctrlchar_tq, &tq_immediate);
-				mark_bh(IMMEDIATE_BH);
-				return (char *)-1;
-			}
-			break;
+	/* racy */
+	if (len == 3 && buf[1] == '-') {
+		ctrlchar_sysrq_key = buf[2];
+		ctrlchar_tq.data = tty;
+		schedule_task(&ctrlchar_tq);
+		return CTRLCHAR_SYSRQ;
+	}
 #endif
-		case 'c':
-			if (len == 2) {
-				ret = INTR_CHAR(tty);
-				return &ret;
-			}
-			break;
-		case 'd':
-			if (len == 2) {
-				ret = EOF_CHAR(tty);
-				return &ret;
-			}
-			break;
-		case 'z':
-			if (len == 2) {
-				ret = SUSP_CHAR(tty);
-				return &ret;
-			}
-			break;
+
+	if (len != 2)
+		return CTRLCHAR_NONE;
+
+	switch (tolower(buf[1])) {
+	case 'c':
+		return INTR_CHAR(tty) | CTRLCHAR_CTRL;
+	case 'd':
+		return EOF_CHAR(tty)  | CTRLCHAR_CTRL;
+	case 'z':
+		return SUSP_CHAR(tty) | CTRLCHAR_CTRL;
 	}
-	return NULL;
+	return CTRLCHAR_NONE;
 }
diff -Nru a/drivers/s390/char/ctrlchar.h b/drivers/s390/char/ctrlchar.h
--- a/drivers/s390/char/ctrlchar.h	Mon Oct  7 11:25:39 2002
+++ b/drivers/s390/char/ctrlchar.h	Mon Oct  7 11:25:39 2002
@@ -7,9 +7,14 @@
  *
  */
 
-#include <linux/config.h>
-#include <linux/types.h>
 #include <linux/tty.h>
 
-extern void ctrlchar_init(void);
-extern char *ctrlchar_handle(const char *buf, int len, struct tty_struct *tty);
+extern unsigned int
+ctrlchar_handle(const char *buf, int len, struct tty_struct *tty);
+
+
+#define CTRLCHAR_NONE  (1 << 8)
+#define CTRLCHAR_CTRL  (2 << 8)
+#define CTRLCHAR_SYSRQ (3 << 8)
+
+#define CTRLCHAR_MASK (~0xffu)
diff -Nru a/drivers/s390/char/hwc.h b/drivers/s390/char/hwc.h
--- a/drivers/s390/char/hwc.h	Mon Oct  7 11:25:36 2002
+++ b/drivers/s390/char/hwc.h	Mon Oct  7 11:25:36 2002
@@ -22,6 +22,7 @@
 #define ET_PMsgCmd		0x09
 #define ET_CntlProgOpCmd	0x20
 #define ET_CntlProgIdent	0x0B
+#define ET_SigQuiesce	0x1D
 
 #define ET_OpCmd_Mask	0x80000000
 #define ET_Msg_Mask		0x40000000
@@ -29,6 +30,7 @@
 #define ET_PMsgCmd_Mask	0x00800000
 #define ET_CtlProgOpCmd_Mask	0x00000001
 #define ET_CtlProgIdent_Mask	0x00200000
+#define ET_SigQuiesce_Mask	0x00000008
 
 #define GMF_DOM		0x8000
 #define GMF_SndAlrm	0x4000
@@ -218,7 +220,8 @@
 	0x0000,
 	0x0000,
 	sizeof (_hwcb_mask_t),
-	ET_OpCmd_Mask | ET_PMsgCmd_Mask | ET_StateChange_Mask,
+	ET_OpCmd_Mask | ET_PMsgCmd_Mask |
+	ET_StateChange_Mask | ET_SigQuiesce_Mask,
 	ET_Msg_Mask | ET_PMsgCmd_Mask | ET_CtlProgIdent_Mask
 };
 
diff -Nru a/drivers/s390/char/hwc_rw.c b/drivers/s390/char/hwc_rw.c
--- a/drivers/s390/char/hwc_rw.c	Mon Oct  7 11:25:26 2002
+++ b/drivers/s390/char/hwc_rw.c	Mon Oct  7 11:25:26 2002
@@ -35,6 +35,8 @@
 #define MIN(a,b) (((a<b) ? a : b))
 #endif
 
+extern void ctrl_alt_del (void);
+
 #define HWC_RW_PRINT_HEADER "hwc low level driver: "
 
 #define  USE_VM_DETECTION
@@ -172,6 +174,7 @@
 	unsigned char read_nonprio:1;
 	unsigned char read_prio:1;
 	unsigned char read_statechange:1;
+	unsigned char sig_quiesce:1;
 
 	unsigned char flags;
 
@@ -222,6 +225,7 @@
 	    0,
 	    0,
 	    0,
+	    0,
 	    NULL,
 	    NULL
 
@@ -1529,6 +1533,19 @@
 				       HWC_RW_PRINT_HEADER
 				 "can not read state change notifications\n");
 
+	hwc_data.sig_quiesce
+	    = ((mask & ET_SigQuiesce_Mask) == ET_SigQuiesce_Mask);
+	if (hwc_data.sig_quiesce)
+		internal_print (
+				       DELAYED_WRITE,
+				       HWC_RW_PRINT_HEADER
+				       "can receive signal quiesce\n");
+	else
+		internal_print (
+				       DELAYED_WRITE,
+				       HWC_RW_PRINT_HEADER
+				       "can not receive signal quiesce\n");
+
 	hwc_data.read_nonprio
 	    = ((mask & ET_OpCmd_Mask) == ET_OpCmd_Mask);
 	if (hwc_data.read_nonprio)
@@ -1609,6 +1626,47 @@
 	return retval;
 }
 
+#ifdef CONFIG_SMP
+static volatile unsigned long cpu_quiesce_map;
+
+static void 
+do_load_quiesce_psw (void)
+{
+	psw_t quiesce_psw;
+
+	clear_bit (smp_processor_id (), &cpu_quiesce_map);
+	if (smp_processor_id () == 0) {
+
+		while (cpu_quiesce_map != 0) ;
+
+		quiesce_psw.mask = PSW_BASE_BITS | PSW_MASK_WAIT;
+		quiesce_psw.addr = 0xfff;
+		__load_psw (quiesce_psw);
+	}
+	signal_processor (smp_processor_id (), sigp_stop);
+}
+
+static void 
+do_machine_quiesce (void)
+{
+	cpu_quiesce_map = cpu_online_map;
+	smp_call_function (do_load_quiesce_psw, NULL, 0, 0);
+	do_load_quiesce_psw ();
+}
+
+#else
+static void 
+do_machine_quiesce (void)
+{
+	psw_t quiesce_psw;
+
+	quiesce_psw.mask = PSW_BASE_BITS | PSW_MASK_WAIT;
+	queisce_psw.addr = 0xfff;
+	__load_psw (quiesce_psw);
+}
+
+#endif
+
 static int 
 process_evbufs (void *start, void *end)
 {
@@ -1644,6 +1702,13 @@
 			retval += eval_statechangebuf
 			    ((statechangebuf_t *) evbuf);
 			break;
+		case ET_SigQuiesce:
+
+			_machine_restart = do_machine_quiesce;
+			_machine_halt = do_machine_quiesce;
+			_machine_power_off = do_machine_quiesce;
+			ctrl_alt_del ();
+			break;
 		default:
 			internal_print (
 					       DELAYED_WRITE,
@@ -2215,11 +2280,9 @@
 void 
 hwc_interrupt_handler (struct pt_regs *regs, __u16 code)
 {
-	int cpu = smp_processor_id ();
-
 	u32 ext_int_param = hwc_ext_int_param ();
 
-	irq_enter (cpu, 0x2401);
+	irq_enter ();
 
 	if (hwc_data.flags & HWC_INIT) {
 
@@ -2240,7 +2303,7 @@
 		hwc_do_interrupt (ext_int_param);
 		spin_unlock (&hwc_data.lock);
 	}
-	irq_exit (cpu, 0x2401);
+	irq_exit ();
 }
 
 void 
diff -Nru a/drivers/s390/char/hwc_tty.c b/drivers/s390/char/hwc_tty.c
--- a/drivers/s390/char/hwc_tty.c	Mon Oct  7 11:25:31 2002
+++ b/drivers/s390/char/hwc_tty.c	Mon Oct  7 11:25:31 2002
@@ -188,15 +188,19 @@
 	struct tty_struct *tty = hwc_tty_data.tty;
 
 	if (tty != NULL) {
-		char *cchar;
-		if ((cchar = ctrlchar_handle (buf, count, tty))) {
-			if (cchar == (char *) -1)
-				return;
+		unsigned int cchar = ctrlchar_handle(buf, count, tty);
+
+		switch (cchar & CTRLCHAR_MASK) {
+		case CTRLCHAR_SYSRQ:
+			return;
+
+		case CTRLCHAR_CTRL:
 			tty->flip.count++;
 			*tty->flip.flag_buf_ptr++ = TTY_NORMAL;
-			*tty->flip.char_buf_ptr++ = *cchar;
-		} else {
+			*tty->flip.char_buf_ptr++ = cchar;
+			break;
 
+		case CTRLCHAR_NONE:
 			memcpy (tty->flip.char_buf_ptr, buf, count);
 			if (count < 2 || (
 					 strncmp (buf + count - 2, "^n", 2) ||
@@ -209,6 +213,7 @@
 			tty->flip.char_buf_ptr += count;
 			tty->flip.flag_buf_ptr += count;
 			tty->flip.count += count;
+			break;
 		}
 		tty_flip_buffer_push (tty);
 		hwc_tty_wake_up ();
@@ -220,8 +225,6 @@
 {
 	if (!CONSOLE_IS_HWC)
 		return;
-
-	ctrlchar_init ();
 
 	memset (&hwc_tty_driver, 0, sizeof (struct tty_driver));
 	memset (&hwc_tty_data, 0, sizeof (hwc_tty_data_struct));
diff -Nru a/drivers/s390/char/tubfs.c b/drivers/s390/char/tubfs.c
--- a/drivers/s390/char/tubfs.c	Mon Oct  7 11:25:36 2002
+++ b/drivers/s390/char/tubfs.c	Mon Oct  7 11:25:36 2002
@@ -229,12 +229,12 @@
  * fs3270_bh(tubp) -- Perform back-half processing
  */
 static void
-fs3270_bh(void *data)
+fs3270_tasklet(unsigned long data)
 {
 	long flags;
 	tub_t *tubp;
 
-	tubp = data;
+	tubp = (tub_t *) data;
 	TUBLOCK(tubp->irq, flags);
 	tubp->flags &= ~TUB_BHPENDING;
 
@@ -265,10 +265,8 @@
 	if (tubp->flags & TUB_BHPENDING)
 		return;
 	tubp->flags |= TUB_BHPENDING;
-	tubp->tqueue.routine = fs3270_bh;
-	tubp->tqueue.data = tubp;
-	queue_task(&tubp->tqueue, &tq_immediate);
-	mark_bh(IMMEDIATE_BH);
+	INIT_WORK(&tubp->tqueue, fs3270_bh, tubp);
+	schedule_work(&tubp->tqueue);
 }
 
 /*
diff -Nru a/drivers/s390/char/tubio.h b/drivers/s390/char/tubio.h
--- a/drivers/s390/char/tubio.h	Mon Oct  7 11:25:25 2002
+++ b/drivers/s390/char/tubio.h	Mon Oct  7 11:25:25 2002
@@ -234,7 +234,7 @@
 	enum tubstat    stat;
 	enum tubcmd     cmd;
 	int             flags;		/* See below for values */
-	struct tq_struct tqueue;
+	struct tasklet_struct tasklet;
 
 	/* Stuff for fs-driver support */
 	pid_t           fs_pid;         /* Pid if TBM_FS */
diff -Nru a/drivers/s390/char/tubtty.c b/drivers/s390/char/tubtty.c
--- a/drivers/s390/char/tubtty.c	Mon Oct  7 11:25:37 2002
+++ b/drivers/s390/char/tubtty.c	Mon Oct  7 11:25:37 2002
@@ -35,7 +35,7 @@
 	unsigned long, void *);
 
 /* tty3270 utility functions */
-static void tty3270_bh(void *);
+static void tty3270_tasklet(unsigned long);
        void tty3270_sched_bh(tub_t *);
 static int tty3270_wait(tub_t *, long *);
        void tty3270_int(tub_t *, devstat_t *);
@@ -598,17 +598,18 @@
 
 
 /*
- * tty3270_bh(tubp) -- Perform back-half processing
+ * tty3270_tasklet(tubp) -- Perform back-half processing
  */
 static void
-tty3270_bh(void *data)
+tty3270_tasklet(unsigned long data)
 {
 	tub_t *tubp;
 	ioinfo_t *ioinfop;
 	long flags;
 	struct tty_struct *tty;
 
-	ioinfop = ioinfo[(tubp = data)->irq];
+	tubp = (tub_t *) data;
+	ioinfop = ioinfo[tubp->irq];
 	while (TUBTRYLOCK(tubp->irq, flags) == 0) {
 		if (ioinfop->ui.flags.unready == 1)
 			return;
@@ -663,10 +664,9 @@
 	if (tubp->flags & TUB_BHPENDING)
 		return;
 	tubp->flags |= TUB_BHPENDING;
-	tubp->tqueue.routine = tty3270_bh;
-	tubp->tqueue.data = tubp;
-	queue_task(&tubp->tqueue, &tq_immediate);
-	mark_bh(IMMEDIATE_BH);
+	tasklet_init(&tubp->tasklet, tty3270_tasklet,
+		     (unsigned long) tubp);
+	tasklet_schedule(&tubp->tasklet);
 }
 
 /*
diff -Nru a/drivers/s390/cio/Makefile b/drivers/s390/cio/Makefile
--- a/drivers/s390/cio/Makefile	Mon Oct  7 11:25:25 2002
+++ b/drivers/s390/cio/Makefile	Mon Oct  7 11:25:25 2002
@@ -3,9 +3,8 @@
 #
 
 obj-y := cio_debug.o # make sure this always comes first
-obj-y += airq.o blacklist.o cio.o ioinfo.o misc.o requestirq.o s390io.o
+obj-y += airq.o blacklist.o cio.o ioinfo.o misc.o requestirq.o s390io.o chsc.o
 
-obj-$(CONFIG_CHSC) += chsc.o
 obj-$(CONFIG_PROC_FS) += proc.o
 
 export-objs += airq.o cio.o ioinfo.o requestirq.o s390io.o
diff -Nru a/drivers/s390/cio/airq.c b/drivers/s390/cio/airq.c
--- a/drivers/s390/cio/airq.c	Mon Oct  7 11:25:36 2002
+++ b/drivers/s390/cio/airq.c	Mon Oct  7 11:25:36 2002
@@ -3,7 +3,7 @@
  *   S/390 common I/O routines -- special interrupt registration
  *   currently used only by qdio
  *
- *   $Revision: 1.2 $
+ *   $Revision: 1.3 $
  *
  *    Copyright (C) 1999-2002 IBM Deutschland Entwicklung GmbH,
  *                            IBM Corporation
@@ -60,7 +60,7 @@
 	sprintf (dbf_txt, "ret:%d", ret);
 	CIO_TRACE_EVENT (4, dbf_txt);
 
-	return (ret);
+	return ret;
 }
 
 int
@@ -85,7 +85,7 @@
 	sprintf (dbf_txt, "ret:%d", ret);
 	CIO_TRACE_EVENT (4, dbf_txt);
 
-	return (ret);
+	return ret;
 }
 
 void
diff -Nru a/drivers/s390/cio/blacklist.c b/drivers/s390/cio/blacklist.c
--- a/drivers/s390/cio/blacklist.c	Mon Oct  7 11:25:29 2002
+++ b/drivers/s390/cio/blacklist.c	Mon Oct  7 11:25:29 2002
@@ -1,7 +1,7 @@
 /*
  *  drivers/s390/cio/blacklist.c
  *   S/390 common I/O routines -- blacklisting of specific devices
- *   $Revision: 1.5 $
+ *   $Revision: 1.7 $
  *
  *    Copyright (C) 1999-2002 IBM Deutschland Entwicklung GmbH,
  *                            IBM Corporation
@@ -134,7 +134,7 @@
 int
 is_blacklisted (int devno)
 {
-	return (test_bit (devno, &bl_dev));
+	return test_bit (devno, &bl_dev);
 }
 
 #ifdef CONFIG_PROC_FS
@@ -243,10 +243,10 @@
 		return -EFAULT;
 	}
 	buf[user_len] = '\0';
-
+#if 0
 	CIO_DEBUG(KERN_DEBUG, 2, 
 		  "/proc/cio_ignore: '%s'\n", buf);
-
+#endif
 	blacklist_parse_proc_parameters (buf);
 
 	vfree (buf);
diff -Nru a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c
--- a/drivers/s390/cio/chsc.c	Mon Oct  7 11:25:25 2002
+++ b/drivers/s390/cio/chsc.c	Mon Oct  7 11:25:25 2002
@@ -1,7 +1,7 @@
 /*
  *  drivers/s390/cio/chsc.c
  *   S/390 common I/O routines -- channel subsystem call
- *   $Revision: 1.9 $
+ *   $Revision: 1.12 $
  *
  *    Copyright (C) 1999-2002 IBM Deutschland Entwicklung GmbH,
  *                            IBM Corporation
@@ -52,6 +52,49 @@
 	return test_bit (ioinfo[irq]->schib.pmcw.chpid[chp], &chpids_logical);
 }
 
+static inline void
+chsc_clear_chpid(int irq, int chp)
+{
+	clear_bit(ioinfo[irq]->schib.pmcw.chpid[chp], &chpids);
+}
+
+void
+chsc_validate_chpids(int irq)
+{
+	int mask, chp;
+
+	if (ioinfo[irq]->opm) {
+		for (chp=0;chp<=7;chp++) {
+			mask = 0x80 >> chp;
+			if (ioinfo[irq]->opm & mask) {
+				if (!chsc_chpid_logical(irq,chp))
+					/* disable using this path */
+					ioinfo[irq]->opm &= ~mask;
+			} else {
+				/* This chpid is not
+				 * available to us */
+				chsc_clear_chpid(irq,chp);
+			}
+		}
+		
+	}
+	
+}
+
+void
+switch_off_chpids(int irq, __u8 mask)
+{
+	int i;
+	pmcw_t *pmcw = &ioinfo[irq]->schib.pmcw;
+
+	for (i=0;i<8;i++)
+		if ((0x80>>i) & mask
+		    & pmcw->pim
+		    & pmcw->pam
+		    & pmcw->pom)
+			clear_bit(pmcw->chpid[i], &chpids);
+}
+
 /* FIXME: this is _always_ called for every subchannel. shouldn't we
  * 	  process more than one at a time?*/
 static int
@@ -213,11 +256,9 @@
 {
 	int irq;
 	int j;
-	int mask;
 	char dbf_txt[15];
 	int ccode;
 	int was_oper;
-	int chp = 0;
 	int mask2;
 
 	sprintf(dbf_txt, "chpr%x", chpid);
@@ -266,21 +307,7 @@
 				ioinfo[irq]->schib.pmcw.pam &
 				ioinfo[irq]->schib.pmcw.pom;
 
-			if (ioinfo[irq]->opm) {
-				for (chp=0;chp<=7;chp++) {
-					mask2 = 0x80 >> chp;
-					if (ioinfo[irq]->opm & mask2) {
-						if (!test_bit
-						    (ioinfo[irq]->
-						     schib.pmcw.chpid[chp], 
-						     &chpids_logical)) {
-							/* disable using this path */
-							ioinfo[irq]->opm 
-								&= ~mask2;
-						}
-					}
-				}
-			}
+			chsc_validate_chpids(irq);
 
 			if (!ioinfo[irq]->opm) {
 				/*
@@ -307,20 +334,7 @@
 						nopfunc(irq, DEVSTAT_DEVICE_GONE);
 				}
 
-			} else if (ioinfo[irq]->ui.flags.ready) {
-				/* 
-				 * Re-do path verification for the chpid in question
-				 * FIXME: is this neccessary?
-				 */
-				mask = 0x80 >> j;
-
-				if (!s390_DevicePathVerification(irq,mask)) {
-					CHSC_DEBUG (KERN_DEBUG, CRW, 2,
-						"DevicePathVerification "
-						"successful for Subchannel %x, "
-						"chpid %x\n", irq, chpid);
-				}
-			}
+			} 
 
 			s390irq_spin_unlock(irq);
 			break;
@@ -432,18 +446,7 @@
 				   ioinfo[irq]->schib.pmcw.pam &
 				   ioinfo[irq]->schib.pmcw.pom;
 
-		if (ioinfo[irq]->opm) {
-			for (chp=0;chp<=7;chp++) {
-				mask = 0x80 >> chp;
-				if ((ioinfo[irq]->opm & mask) &&
-				    !test_bit (ioinfo[irq]->schib.pmcw.chpid[chp],
-					       &chpids_logical)) {
-
-					/* disable using this path */
-					ioinfo[irq]->opm &= ~mask;
-				}
-			}
-		}
+		chsc_validate_chpids(irq);
 
 		if ((ioinfo[irq]->ui.flags.ready) && (chpid & ioinfo[irq]->opm))
 			s390_DevicePathVerification(irq, chpid);
@@ -456,8 +459,7 @@
 	char dbf_txt[15];
 	int irq = 0;
 	int ccode;
-	int chp;
-	int mask, mask2;
+	int mask2;
 	int ret;
 	int j;
 
@@ -517,18 +519,7 @@
 						   ioinfo[irq]->schib.pmcw.pam &
 						   ioinfo[irq]->schib.pmcw.pom;
 
-				if (ioinfo[irq]->opm) {
-					for (chp=0;chp<=7;chp++) {
-						mask = 0x80 >> chp;
-						if ((ioinfo[irq]->opm & mask)
-						    && (!test_bit (ioinfo[irq]->
-							     schib.pmcw.chpid[chp], 
-							     &chpids_logical))) {
-							/* disable using this path */
-							ioinfo[irq]->opm &= ~mask;
-						}
-					}
-				}
+				chsc_validate_chpids(irq);
 
 				if (ioinfo[irq]->ui.flags.ready)
 					s390_DevicePathVerification(irq, chpid);
@@ -782,11 +773,20 @@
 	}
 
 	while (chp < NR_CHPIDS && len + entry_size < count) {
-		if ((test_bit( chp, &chpids)) && test_bit(chp, &chpids_logical))
-			len += sprintf(page+len, "0x%02X online\n", chp);
-		else if (test_bit(chp, &chpids_known))
-			len += sprintf(page+len, "0x%02X logically offline\n",
-					chp);
+		if (test_bit(chp, &chpids_known)) {
+
+			if (!test_bit(chp, &chpids))
+				len += sprintf(page+len,
+					       "0x%02X n/a\n", chp);
+			
+			else if (test_bit(chp, &chpids_logical))
+				len += sprintf(page+len,
+					       "0x%02X online\n", chp);
+			else
+				len += sprintf(page+len,
+					       "0x%02X logically offline\n", 
+					       chp);
+		}
 		chp++;
 	}
 
diff -Nru a/drivers/s390/cio/chsc.h b/drivers/s390/cio/chsc.h
--- a/drivers/s390/cio/chsc.h	Mon Oct  7 11:25:37 2002
+++ b/drivers/s390/cio/chsc.h	Mon Oct  7 11:25:37 2002
@@ -3,4 +3,6 @@
 
 extern void s390_process_css( void );
 extern int chsc_chpid_logical (int irq, int chp);
+extern void chsc_validate_chpids(int irq);
+extern void switch_off_chpids(int irq, __u8 mask);
 #endif
diff -Nru a/drivers/s390/cio/cio.c b/drivers/s390/cio/cio.c
--- a/drivers/s390/cio/cio.c	Mon Oct  7 11:25:39 2002
+++ b/drivers/s390/cio/cio.c	Mon Oct  7 11:25:39 2002
@@ -1,7 +1,7 @@
 /*
  *  drivers/s390/cio/cio.c
  *   S/390 common I/O routines -- low level i/o calls
- *   $Revision: 1.15 $
+ *   $Revision: 1.26 $
  *
  *    Copyright (C) 1999-2002 IBM Deutschland Entwicklung GmbH,
  *                            IBM Corporation
@@ -52,7 +52,7 @@
 	}
 	DBG ("%s\n", buffer);
 	if (cio_debug_initialized) 
-		debug_text_event (cio_debug_trace_id, level, buffer);
+		debug_text_event (cio_debug_msg_id, level, buffer);
 }
 
 
@@ -98,7 +98,6 @@
 s390_do_sync_wait(int irq, int do_tpi)
 {
 	unsigned long psw_mask;
-	int ccode;
 	uint64_t time_start;
 	uint64_t time_curr;
 	
@@ -116,31 +115,7 @@
 	 *  sync. interrupt arrived we reset the I/O old PSW to
 	 *  its original value.
 	 */
-	
-	ccode = iac ();
-	
-	switch (ccode) {
-	case 0:	/* primary-space */
-		psw_mask = _IO_PSW_MASK
-			| _PSW_PRIM_SPACE_MODE | _PSW_IO_WAIT;
-		break;
-	case 1:	/* secondary-space */
-		psw_mask = _IO_PSW_MASK
-			| _PSW_SEC_SPACE_MODE | _PSW_IO_WAIT;
-		break;
-	case 2:	/* access-register */
-		psw_mask = _IO_PSW_MASK
-			| _PSW_ACC_REG_MODE | _PSW_IO_WAIT;
-		break;
-	case 3:	/* home-space */
-		psw_mask = _IO_PSW_MASK
-			| _PSW_HOME_SPACE_MODE | _PSW_IO_WAIT;
-		break;
-	default:
-		panic ("start_IO() : unexpected "
-		       "address-space-control %d\n", ccode);
-		break;
-	}
+	psw_mask = PSW_KERNEL_BITS | PSW_MASK_IO | PSW_MASK_WAIT;
 	
 	/*
 	 * Martin didn't like modifying the new PSW, now we take
@@ -201,7 +176,6 @@
 	int io_sub;
 	__u32 io_parm;
 	unsigned long psw_mask;
-	int ccode;
 	
 	int ready = 0;
 	
@@ -212,32 +186,7 @@
 	 * FIXME: Are there case where we can't rely on an interrupt
 	 *        to occurr? Need to check...
 	 */
-
-	ccode = iac ();
-	
-	switch (ccode) {
-	case 0:	/* primary-space */
-		psw_mask = _IO_PSW_MASK
-			| _PSW_PRIM_SPACE_MODE | _PSW_IO_WAIT;
-		break;
-	case 1:	/* secondary-space */
-		psw_mask = _IO_PSW_MASK
-			| _PSW_SEC_SPACE_MODE | _PSW_IO_WAIT;
-		break;
-	case 2:	/* access-register */
-		psw_mask = _IO_PSW_MASK
-			| _PSW_ACC_REG_MODE | _PSW_IO_WAIT;
-		break;
-	case 3:	/* home-space */
-		psw_mask = _IO_PSW_MASK
-			| _PSW_HOME_SPACE_MODE | _PSW_IO_WAIT;
-		break;
-	default: /* FIXME: isn't ccode only 2 bits anyway? */
-		panic (halt?"halt":"clear"
-		       "_IO() : unexpected address-space-control %d\n", 
-		       ccode);
-		break;
-	}
+	psw_mask = PSW_KERNEL_BITS | PSW_MASK_IO | PSW_MASK_WAIT;
 	
 	/*
 	 * Martin didn't like modifying the new PSW, now we take
@@ -393,6 +342,7 @@
 
 	if (valid_lpm) {
 		ioinfo[irq]->opm &= ~lpm;
+		switch_off_chpids(irq, lpm);
 	} else {
 		ioinfo[irq]->opm = 0;
 		
@@ -433,8 +383,6 @@
 	int ret = 0;
 	char dbf_txt[15];
 
-	SANITY_CHECK (irq);
-
 	/*
 	 * The flag usage is mutal exclusive ...
 	 */
@@ -456,7 +404,7 @@
 		ret = enable_cpu_sync_isc (irq);
 
 		if (ret) 
-			return (ret);
+			return ret;
 
 	}
 
@@ -556,7 +504,7 @@
 	if (flag & DOIO_DONT_CALL_INTHDLR) 
 		ioinfo[irq]->ui.flags.repnone = 0;
 
-	return (ret);
+	return ret;
 }
 
 int
@@ -618,7 +566,7 @@
 
 	}
 
-	return (ret);
+	return ret;
 
 }
 
@@ -677,7 +625,7 @@
 
 	}
 
-	return (ret);
+	return ret;
 }
 
 /*
@@ -720,7 +668,7 @@
 		ret = enable_cpu_sync_isc (irq);
 
 		if (ret)
-			return (ret);
+			return ret;
 	}
 
 	/*
@@ -784,7 +732,7 @@
 	if (flag & DOIO_WAIT_FOR_INTERRUPT) 
 		disable_cpu_sync_isc (irq);
 
-	return (ret);
+	return ret;
 }
 
 /*
@@ -803,7 +751,7 @@
 	SANITY_CHECK (irq);
 
 	if (ioinfo[irq] == INVALID_STORAGE_AREA)
-		return (-ENODEV);
+		return -ENODEV;
 
 	/*
 	 * we only allow for clear_IO if the device has an I/O handler associated
@@ -829,7 +777,7 @@
 		ret = enable_cpu_sync_isc (irq);
 
 		if (ret)
-			return (ret);
+			return ret;
 	}
 
 	/*
@@ -891,7 +839,7 @@
 	if (flag & DOIO_WAIT_FOR_INTERRUPT) 
 		disable_cpu_sync_isc (irq);
 
-	return (ret);
+	return ret;
 }
 
 /*
@@ -908,8 +856,6 @@
 	char dbf_txt[15];
 	int ret = 0;
 
-	SANITY_CHECK (irq);
-
 	sprintf (dbf_txt, "cancelIO%x", irq);
 	CIO_TRACE_EVENT (2, dbf_txt);
 
@@ -955,7 +901,6 @@
 	 * Get interrupt info from lowcore
 	 */
 	volatile tpi_info_t *tpi_info = (tpi_info_t *) (__LC_SUBCHANNEL_ID);
-	int cpu = smp_processor_id ();
 
 	/*
 	 * take fast exit if CPU is in sync. I/O state
@@ -965,22 +910,19 @@
 	 *       entry condition to synchronous I/O.
 	 */
 	if (*(__u32 *) __LC_SYNC_IO_WORD) {
-		regs.psw.mask &= ~(_PSW_WAIT_MASK_BIT | _PSW_IO_MASK_BIT);
+		regs.psw.mask &= ~(PSW_MASK_WAIT | PSW_MASK_IO);
 		return;
 	}
 	/* endif */
-#ifdef CONFIG_FAST_IRQ
 	do {
-#endif				/* CONFIG_FAST_IRQ */
-
 		/*
 		 * Non I/O-subchannel thin interrupts are processed differently
 		 */
 		if (tpi_info->adapter_IO == 1 &&
 		    tpi_info->int_type == IO_INTERRUPT_TYPE) {
-			irq_enter (cpu, -1);
+			irq_enter ();
 			do_adapter_IO (tpi_info->intparm);
-			irq_exit (cpu, -1);
+			irq_exit ();
 		} else {
 			unsigned int irq = tpi_info->irq;
 
@@ -1001,23 +943,21 @@
 				return;
 			}
 
-			irq_enter (cpu, irq);
+			irq_enter ();
 			s390irq_spin_lock (irq);
 			s390_process_IRQ (irq);
 			s390irq_spin_unlock (irq);
-			irq_exit (cpu, irq);
+			irq_exit ();
 		}
 
-#ifdef CONFIG_FAST_IRQ
-
 		/*
 		 * Are more interrupts pending?
 		 * If so, the tpi instruction will update the lowcore 
 		 * to hold the info for the next interrupt.
+		 * We don't do this for VM because a tpi drops the cpu
+		 * out of the sie which costs more cycles than it saves.
 		 */
-	} while (tpi (NULL) != 0);
-
-#endif				/* CONFIG_FAST_IRQ */
+	} while (!MACHINE_IS_VM && tpi (NULL) != 0);
 
 	return;
 }
@@ -1126,9 +1066,9 @@
  * for cc=0 and cc=1 after tsch
  */
 static inline int
-s390_process_IRQ_normal(unsigned int irq,
-			int ending_status)
+s390_process_IRQ_normal(unsigned int irq)
 {
+	int ending_status;
 	unsigned int fctl;	/* function control */
 	unsigned int stctl;	/* status   control */
 	unsigned int actl;	/* activity control */
@@ -1188,7 +1128,7 @@
 	 * take fast exit if no handler is available
 	 */
 	if (!ioinfo[irq]->ui.flags.ready)
-		return (ending_status);
+		return ending_status;
 	
 	/*
 	 * Check whether we must issue a SENSE CCW ourselves if there is no
@@ -1376,8 +1316,7 @@
  * for cc=3 after tsch
  */
 static inline int
-s390_process_IRQ_notoper(unsigned int irq,
-			 int ending_status)
+s390_process_IRQ_notoper(unsigned int irq)
 {
 	devstat_t *dp;
 	devstat_t *udp;
@@ -1440,7 +1379,7 @@
 	 * take fast exit if no handler is available
 	 */
 	if (!ioinfo[irq]->ui.flags.ready)
-		return (ending_status);
+		return 0;
 	
 	memcpy (udp, &(ioinfo[irq]->devstat), 
 		sizeof(devstat_t) - 
@@ -1448,7 +1387,7 @@
 	
 	ioinfo[irq]->devstat.intparm = 0;
 	
-	if (!ioinfo[irq]->ui.flags.s_pend) 
+	if (!(ioinfo[irq]->ui.flags.s_pend || ioinfo[irq]->ui.flags.repnone))
 		ioinfo[irq]->irq_desc.handler (irq, udp, NULL);
 	
 	return 1;
@@ -1478,7 +1417,6 @@
 	int irb_cc;		/* cond code from irb */
 
 	int issense = 0;
-	int ending_status = 0;
 	devstat_t *dp;
 	devstat_t *udp;
 	scsw_t *scsw;
@@ -1490,7 +1428,8 @@
 		s390_irq_count[cpu]++;
 	}
 
-	sprintf (dbf_txt, "procIRQ%x", irq);
+	CIO_TRACE_EVENT (3, "procIRQ");
+	sprintf (dbf_txt, "%x", irq);
 	CIO_TRACE_EVENT (3, dbf_txt);
 
 	if (ioinfo[irq] == INVALID_STORAGE_AREA) {
@@ -1502,14 +1441,8 @@
 			  "for non-initialized subchannel!\n", irq);
 
 		tsch (irq, &p_init_irb);
-		return (1);
-
-	}
-
-	if (ioinfo[irq]->st) {
-		/* can't be */
-		BUG();
 		return 1;
+
 	}
 
 	dp = &ioinfo[irq]->devstat;
@@ -1676,25 +1609,14 @@
 
 	switch (irb_cc) {
 	case 1:		/* status pending */
-
 		dp->flag |= DEVSTAT_STATUS_PENDING;
 
 	case 0:		/* normal i/o interruption */
+		return s390_process_IRQ_normal(irq);
 
-		ending_status = s390_process_IRQ_normal(irq, ending_status);
-
-
-		break;
-
-	case 3:		/* device/path not operational */
-
-		ending_status = s390_process_IRQ_notoper(irq, ending_status); 
-
-		break;
-
+	default:	/* device/path not operational */
+		return s390_process_IRQ_notoper(irq);
 	}
-
-	return (ending_status);
 }
 
 /*
@@ -1720,8 +1642,6 @@
 	int rc = 0;
 	char dbf_txt[15];
 
-	SANITY_CHECK (irq);
-
 	if (cons_dev != -1)
 		return -EBUSY;
 
@@ -1755,7 +1675,7 @@
 		}
 	}
 
-	return (rc);
+	return rc;
 }
 
 int
@@ -1783,7 +1703,7 @@
 		 */
 		__ctl_store (cr6, 6, 6);
 		save_cr6 = cr6;
-		cr6 &= 0x01FFFFFF;
+		cr6 = 0x01000000;
 		__ctl_load (cr6, 6, 6);
 
 		do {
@@ -1806,7 +1726,7 @@
 
 	}
 
-	return (rc);
+	return rc;
 }
 
 /*
@@ -1923,47 +1843,39 @@
 
 	/* This one spins until it can get the sync_isc lock for irq# irq */
 
-	if ((irq <= highest_subchannel) && 
-	    (ioinfo[irq] != INVALID_STORAGE_AREA) &&
-	    (!ioinfo[irq]->st)) {
-		if (atomic_read (&sync_isc) != irq)
-			atomic_compare_and_swap_spin (-1, irq, &sync_isc);
-
-		sync_isc_cnt++;
-
-		if (sync_isc_cnt > 255) {	/* fixme : magic number */
-			panic ("Too many recursive calls to enable_sync_isc");
-
-		}
-		/*
-		 * we only run the STSCH/MSCH path for the first enablement
-		 */
-		else if (sync_isc_cnt == 1) {
-			ioinfo[irq]->ui.flags.syncio = 1;
-
-			ccode = stsch (irq, &(ioinfo[irq]->schib));
-
-			if (!ccode) {
-				ioinfo[irq]->schib.pmcw.isc = 5;
-				rc = s390_set_isc5(irq, 0);
-
-			} else {
-				rc = -ENODEV;	/* device is not-operational */
-
-			}
-		}
-
-		if (rc) {	/* can only happen if stsch/msch fails */
-			sync_isc_cnt = 0;
-			atomic_set (&sync_isc, -1);
+	if (atomic_read (&sync_isc) != irq)
+		atomic_compare_and_swap_spin (-1, irq, &sync_isc);
+	
+	sync_isc_cnt++;
+	
+	if (sync_isc_cnt > 255) {	/* fixme : magic number */
+		panic ("Too many recursive calls to enable_sync_isc");
+		
+	}
+	/*
+	 * we only run the STSCH/MSCH path for the first enablement
+	 */
+	else if (sync_isc_cnt == 1) {
+		ioinfo[irq]->ui.flags.syncio = 1;
+		
+		ccode = stsch (irq, &(ioinfo[irq]->schib));
+		
+		if (!ccode) {
+			ioinfo[irq]->schib.pmcw.isc = 5;
+			rc = s390_set_isc5(irq, 0);
+			
+		} else {
+			rc = -ENODEV;	/* device is not-operational */
+			
 		}
-	} else {
-
-		rc = -EINVAL;
-
+	}
+	
+	if (rc) {	/* can only happen if stsch/msch fails */
+		sync_isc_cnt = 0;
+		atomic_set (&sync_isc, -1);
 	}
 
-	return (rc);
+	return rc;
 }
 
 
@@ -1978,44 +1890,36 @@
 	sprintf (dbf_txt, "disisc%x", irq);
 	CIO_TRACE_EVENT (4, dbf_txt);
 
-	if ((irq <= highest_subchannel) && 
-	    (ioinfo[irq] != INVALID_STORAGE_AREA) && 
-	    (!ioinfo[irq]->st)) {
-		/*
-		 * We disable if we're the top user only, as we may
-		 *  run recursively ... 
-		 * We must not decrease the count immediately; during
-		 *  msch() processing we may face another pending
-		 *  status we have to process recursively (sync).
-		 */
-
-		if (sync_isc_cnt == 1) {
-			ccode = stsch (irq, &(ioinfo[irq]->schib));
-
-			if (!ccode) {
-
-				ioinfo[irq]->schib.pmcw.isc = 3;
-				rc = s390_set_isc5(irq, 1);
-			} else {
-				rc = -ENODEV;
-			}
-				
-			ioinfo[irq]->ui.flags.syncio = 0;
-
-			sync_isc_cnt = 0;
-			atomic_set (&sync_isc, -1);
-
+	/*
+	 * We disable if we're the top user only, as we may
+	 *  run recursively ... 
+	 * We must not decrease the count immediately; during
+	 *  msch() processing we may face another pending
+	 *  status we have to process recursively (sync).
+	 */
+	
+	if (sync_isc_cnt == 1) {
+		ccode = stsch (irq, &(ioinfo[irq]->schib));
+		
+		if (!ccode) {
+			
+			ioinfo[irq]->schib.pmcw.isc = 3;
+			rc = s390_set_isc5(irq, 1);
 		} else {
-			sync_isc_cnt--;
-
+			rc = -ENODEV;
 		}
+		
+		ioinfo[irq]->ui.flags.syncio = 0;
+		
+		sync_isc_cnt = 0;
+		atomic_set (&sync_isc, -1);
+		
 	} else {
-
-		rc = -EINVAL;
-
+		sync_isc_cnt--;
+		
 	}
 
-	return (rc);
+	return rc;
 }
 
 EXPORT_SYMBOL (halt_IO);
diff -Nru a/drivers/s390/cio/cio_debug.c b/drivers/s390/cio/cio_debug.c
--- a/drivers/s390/cio/cio_debug.c	Mon Oct  7 11:25:31 2002
+++ b/drivers/s390/cio/cio_debug.c	Mon Oct  7 11:25:31 2002
@@ -1,7 +1,7 @@
 /*
  *  drivers/s390/cio/cio_debug.c
  *   S/390 common I/O routines -- message ids for debugging
- *   $Revision: 1.4 $
+ *   $Revision: 1.5 $
  *
  *    Copyright (C) 1999-2002 IBM Deutschland Entwicklung GmbH,
  *                            IBM Corporation
@@ -69,4 +69,4 @@
 	return ret;
 }
 
-__initcall (cio_debug_init);
+arch_initcall (cio_debug_init);
diff -Nru a/drivers/s390/cio/cio_debug.h b/drivers/s390/cio/cio_debug.h
--- a/drivers/s390/cio/cio_debug.h	Mon Oct  7 11:25:36 2002
+++ b/drivers/s390/cio/cio_debug.h	Mon Oct  7 11:25:36 2002
@@ -3,9 +3,9 @@
 
 #define SANITY_CHECK(irq) do { \
 if (irq > highest_subchannel || irq < 0) \
-		return (-ENODEV); \
+		return -ENODEV; \
 	if (ioinfo[irq] == INVALID_STORAGE_AREA) \
-		return (-ENODEV); \
+		return -ENODEV; \
         if (ioinfo[irq]->st) \
                 return -ENODEV; \
 	} while(0)
@@ -20,14 +20,14 @@
 #define CIO_MSG_EVENT(imp, args...) do { \
         if (cio_debug_initialized) \
                 debug_sprintf_event(cio_debug_msg_id, \
-                                    imp, \
+                                    imp , \
                                     ##args); \
         } while (0)
 
 #define CIO_CRW_EVENT(imp, args...) do { \
         if (cio_debug_initialized) \
                 debug_sprintf_event(cio_debug_crw_id, \
-                                    imp, \
+                                    imp , \
                                     ##args); \
         } while (0)
 
diff -Nru a/drivers/s390/cio/ioinfo.c b/drivers/s390/cio/ioinfo.c
--- a/drivers/s390/cio/ioinfo.c	Mon Oct  7 11:25:25 2002
+++ b/drivers/s390/cio/ioinfo.c	Mon Oct  7 11:25:25 2002
@@ -1,7 +1,7 @@
 /*
  *  drivers/s390/cio/ioinfo.c
  *   S/390 common I/O routines -- the ioinfo structure
- *   $Revision: 1.3 $
+ *   $Revision: 1.4 $
  *
  *    Copyright (C) 1999-2002 IBM Deutschland Entwicklung GmbH,
  *                            IBM Corporation
@@ -102,12 +102,12 @@
 int
 get_dev_info_by_irq (int irq, s390_dev_info_t * pdi)
 {
-	if (irq > highest_subchannel || irq < 0) \
-		return (-ENODEV); \
-	if (ioinfo[irq] == INVALID_STORAGE_AREA) \
-		return (-ENODEV); \
-        if (ioinfo[irq]->st) \
-                return -ENODEV; \
+	if (irq > highest_subchannel || irq < 0) 
+		return -ENODEV; 
+	if (ioinfo[irq] == INVALID_STORAGE_AREA) 
+		return -ENODEV; 
+        if (ioinfo[irq]->st) 
+                return -ENODEV; 
 
 	if (pdi == NULL)
 		return -EINVAL;
@@ -189,7 +189,7 @@
 		}
 	}
 
-	return (rc);
+	return rc;
 
 }
 
@@ -211,7 +211,7 @@
 		}
 	}
 
-	return (rc);
+	return rc;
 }
 
 unsigned int
@@ -235,7 +235,7 @@
 	 *  defined who's device number isn't valid ...
 	 */
 	if (ioinfo[irq]->schib.pmcw.dnv)
-		return (ioinfo[irq]->schib.pmcw.dev);
+		return ioinfo[irq]->schib.pmcw.dev;
 	else
 		return -1;
 }
@@ -258,9 +258,9 @@
 s390_set_private_data(int irq, void *data)
 {
 	if (irq > highest_subchannel || irq < 0)
-		return (-ENODEV);
+		return -ENODEV;
 	if (ioinfo[irq] == INVALID_STORAGE_AREA)
-		return (-ENODEV);
+		return -ENODEV;
         if (ioinfo[irq]->st)
                 return -ENODEV;
 	ioinfo[irq]->private_data = data;
diff -Nru a/drivers/s390/cio/misc.c b/drivers/s390/cio/misc.c
--- a/drivers/s390/cio/misc.c	Mon Oct  7 11:25:33 2002
+++ b/drivers/s390/cio/misc.c	Mon Oct  7 11:25:33 2002
@@ -1,7 +1,7 @@
 /*
  *  drivers/s390/s390io.c
  *   S/390 common I/O routines
- *   $Revision: 1.4 $
+ *   $Revision: 1.5 $
  *
  *    Copyright (C) 1999-2002 IBM Deutschland Entwicklung GmbH,
  *                            IBM Corporation
@@ -212,9 +212,7 @@
 
 			CRW_DEBUG(KERN_NOTICE, 2, 
 				  "source is channel subsystem\n");
-#ifdef CONFIG_CHSC
 			s390_process_css();
-#endif
 			break;
 
 		default:
diff -Nru a/drivers/s390/cio/proc.c b/drivers/s390/cio/proc.c
--- a/drivers/s390/cio/proc.c	Mon Oct  7 11:25:26 2002
+++ b/drivers/s390/cio/proc.c	Mon Oct  7 11:25:26 2002
@@ -1,7 +1,7 @@
 /*
  *  drivers/s390/cio/proc.c
  *   S/390 common I/O routines -- proc file system entries
- *   $Revision: 1.4 $
+ *   $Revision: 1.5 $
  *
  *    Copyright (C) 1999-2002 IBM Deutschland Entwicklung GmbH,
  *                            IBM Corporation
@@ -267,3 +267,9 @@
 }
 
 __initcall (cio_irq_proc_init);
+
+void
+init_irq_proc(void)
+{
+	/* For now, nothing... */
+}
diff -Nru a/drivers/s390/cio/requestirq.c b/drivers/s390/cio/requestirq.c
--- a/drivers/s390/cio/requestirq.c	Mon Oct  7 11:25:36 2002
+++ b/drivers/s390/cio/requestirq.c	Mon Oct  7 11:25:36 2002
@@ -1,7 +1,7 @@
 /*
  *  drivers/s390/cio/requestirq.c
  *   S/390 common I/O routines -- 
- *   $Revision: 1.7 $
+ *   $Revision: 1.12 $
  *
  *    Copyright (C) 1999-2002 IBM Deutschland Entwicklung GmbH,
  *                            IBM Corporation
@@ -122,6 +122,44 @@
 					 NULL, irqflags, devname, dev_id);
 }
 
+int
+s390_request_console_irq (int irq,
+			  void (*handler) (int, void *, struct pt_regs *),
+			  unsigned long irqflags,
+			  const char *devname,
+			  void *dev_id)
+{
+	int ret;
+	unsigned long flags;
+
+	s390_device_recognition_irq (irq);
+
+	ret = s390_request_irq_special (irq, (io_handler_func_t) handler,
+					NULL, irqflags, devname, dev_id);
+
+	if (ret)
+		goto out;
+
+	s390irq_spin_lock_irqsave(irq, flags);
+	ret = set_cons_dev(irq);
+	s390irq_spin_unlock_irqrestore(irq, flags);
+
+	if (ret)
+		free_irq(irq, dev_id);
+out:
+	return ret;
+}
+
+/*
+ * request_irq wrapper
+ */
+int
+request_irq(unsigned int irq, void (*handler)(int, void *, struct pt_regs *),
+	    unsigned long irqflags, const char *devname, void *dev_id)
+{
+	return s390_request_irq(irq, handler, irqflags, devname, dev_id);
+}
+
 void
 s390_free_irq (unsigned int irq, void *dev_id)
 {
@@ -141,7 +179,7 @@
 
 	s390irq_spin_lock_irqsave (irq, flags);
 
-	CIO_DEBUG_NOCONS(irq,KERN_DEBUG, printk, 2, 
+	CIO_DEBUG_NOCONS(irq,KERN_DEBUG, DBG, 2, 
 			 "Trying to free IRQ %d\n", 
 			 irq);
 
@@ -224,6 +262,15 @@
 }
 
 /*
+ * free_irq wrapper.
+ */
+void
+free_irq(unsigned int irq, void *dev_id)
+{
+	s390_free_irq(irq, dev_id);
+}
+
+/*
  * Enable IRQ by modifying the subchannel
  */
 static int
@@ -234,8 +281,6 @@
 	int retry = 5;
 	char dbf_txt[15];
 
-	SANITY_CHECK (irq);
-
 	sprintf (dbf_txt, "ensch%x", irq);
 	CIO_TRACE_EVENT (2, dbf_txt);
 
@@ -305,7 +350,7 @@
 	sprintf (dbf_txt, "ret:%d", ret);
 	CIO_TRACE_EVENT (2, dbf_txt);
 
-	return (ret);
+	return ret;
 }
 
 /*
@@ -319,8 +364,6 @@
 	int retry = 5;
 	char dbf_txt[15];
 
-	SANITY_CHECK (irq);
-
 	sprintf (dbf_txt, "dissch%x", irq);
 	CIO_TRACE_EVENT (2, dbf_txt);
 
@@ -408,7 +451,7 @@
 	sprintf (dbf_txt, "ret:%d", ret);
 	CIO_TRACE_EVENT (2, dbf_txt);
 
-	return (ret);
+	return ret;
 }
 
 /* FIXME: there must be a cleaner way to express what happens */
diff -Nru a/drivers/s390/cio/s390io.c b/drivers/s390/cio/s390io.c
--- a/drivers/s390/cio/s390io.c	Mon Oct  7 11:25:37 2002
+++ b/drivers/s390/cio/s390io.c	Mon Oct  7 11:25:37 2002
@@ -1,7 +1,7 @@
 /*
  *  drivers/s390/cio/s390io.c
  *   S/390 common I/O routines
- *   $Revision: 1.11 $
+ *   $Revision: 1.33 $
  *
  *  S390 version
  *    Copyright (C) 1999, 2000 IBM Deutschland Entwicklung GmbH,
@@ -75,9 +75,8 @@
 static __u64 irq_IPL_TOD; // FIXME: can be on stack
 
 static void s390_process_subchannels (void);
-static void s390_device_recognition_all (void);
 static int s390_SenseID (int irq, senseid_t * sid, __u8 lpm);
-static int s390_SetPGID (int irq, __u8 lpm, pgid_t * pgid);
+static int s390_SetPGID (int irq, __u8 lpm);
 static int s390_SensePGID (int irq, __u8 lpm, pgid_t * pgid);
 
 /* FIXME: intermixed with proc.c */
@@ -86,7 +85,6 @@
 
 int cio_show_msg;
 static int cio_notoper_msg = 1;
-static int cio_sid_with_pgid;     /* if we need a PGID for SenseID, switch this on */
 
 static int __init
 cio_setup (char *parm)
@@ -125,25 +123,6 @@
 
 __setup ("cio_notoper_msg=", cio_notoper_setup);
 
-static int __init
-cio_pgid_setup (char *parm)
-{
-	if (!strcmp (parm, "yes")) {
-		cio_sid_with_pgid = 1;
-	} else if (!strcmp (parm, "no")) {
-		cio_sid_with_pgid = 0;
-	} else {
-		printk (KERN_ERR 
-			"cio_pgid_setup : invalid cio_msg parameter '%s'",
-			parm);
-
-	}
-
-	return 1;
-}
-
-__setup ("cio_sid_with_pgid=", cio_pgid_setup);
-
 /* This function is replacing the init_IRQ function in
  * arch/s390(x)/kernel/irq.c and is called early during
  * bootup. Anything called from here must be careful 
@@ -199,8 +178,6 @@
 	cr6 = 0x10000000;
 	__ctl_load (cr6, 6, 6);
 
-	s390_device_recognition_all ();
-
 	init_IRQ_complete = 1;
 
 	local_irq_restore (flags);
@@ -209,6 +186,15 @@
 }
 
 /*
+ * init_IRQ wrapper
+ */
+void __init
+init_IRQ(void)
+{
+	s390_init_IRQ();
+}
+
+/*
  * dummy handler, used during init_IRQ() processing for compatibility only
  */
 static void
@@ -217,6 +203,51 @@
 	/* this is a dummy handler only ... */
 }
 
+/* 
+ * diag210 is used under VM to get information about a virtual device
+ */
+#ifdef CONFIG_ARCH_S390X
+int diag210(diag210_t * addr)
+{
+	/*
+	 * diag 210 needs its data below the 2GB border, so we
+	 * use a static data area to be sure
+	 */
+	static diag210_t diag210_tmp;
+	static spinlock_t diag210_lock = SPIN_LOCK_UNLOCKED;
+	unsigned long flags;
+	int ccode;
+
+	spin_lock_irqsave(&diag210_lock, flags);
+	diag210_tmp = *addr;
+
+	asm volatile (
+		"   sam31\n"
+		"   diag  %1,0,0x210\n"
+		"   sam64\n"
+		"   ipm   %0\n"
+		"   srl   %0,28"
+		: "=d" (ccode) : "a" (__pa(&diag210_tmp)) : "cc", "memory" );
+	
+	*addr = diag210_tmp;
+	spin_unlock_irqrestore(&diag210_lock, flags);
+
+	return ccode;
+}
+#else
+int diag210(diag210_t * addr)
+{
+	int ccode;
+
+	asm volatile (
+		"   diag  %1,0,0x210\n"
+		"   ipm   %0\n"
+		"   srl   %0,28"
+		: "=d" (ccode) : "a" (__pa(addr)) : "cc", "memory" );
+
+	return ccode;
+}
+#endif
 
 /*
  * Input :
@@ -225,31 +256,28 @@
  * Output : none
  */
 static inline void
-_VM_virtual_device_info (__u16 devno, senseid_t * ps)
+VM_virtual_device_info (__u16 devno, senseid_t * ps)
 {
-	diag210_t *p_diag_data;
+	diag210_t diag_data;
 	int ccode;
 
 	int error = 0;
 
 	CIO_TRACE_EVENT (4, "VMvdinf");
 
-	if (init_IRQ_complete) {
-		p_diag_data = kmalloc (sizeof (diag210_t), GFP_DMA);
-	} else {
-		p_diag_data = alloc_bootmem_low (sizeof (diag210_t));
-
-	}
+	diag_data = (diag210_t) {
+		.vrdcdvno = devno,
+		.vrdclen = sizeof (diag_data),
+	};
 
-	p_diag_data->vrdcdvno = devno;
-	p_diag_data->vrdclen = sizeof (diag210_t);
-	ccode = diag210 ((diag210_t *) virt_to_phys (p_diag_data));
+	ccode = diag210 (&diag_data);
 	ps->reserved = 0xff;
 
-	switch (p_diag_data->vrdcvcla) {
+	/* FIXME: this would be much nicer if it were table driven */
+	switch (diag_data.vrdcvcla) {
 	case 0x80:
 
-		switch (p_diag_data->vrdcvtyp) {
+		switch (diag_data.vrdcvtyp) {
 		case 00:
 
 			ps->cu_type = 0x3215;
@@ -268,7 +296,7 @@
 
 	case 0x40:
 
-		switch (p_diag_data->vrdcvtyp) {
+		switch (diag_data.vrdcvtyp) {
 		case 0xC0:
 
 			ps->cu_type = 0x5080;
@@ -305,7 +333,7 @@
 
 	case 0x20:
 
-		switch (p_diag_data->vrdcvtyp) {
+		switch (diag_data.vrdcvtyp) {
 		case 0x84:
 
 			ps->cu_type = 0x3505;
@@ -336,7 +364,7 @@
 
 	case 0x10:
 
-		switch (p_diag_data->vrdcvtyp) {
+		switch (diag_data.vrdcvtyp) {
 		case 0x84:
 
 			ps->cu_type = 0x3525;
@@ -413,7 +441,7 @@
 
 	case 0x08:
 
-		switch (p_diag_data->vrdcvtyp) {
+		switch (diag_data.vrdcvtyp) {
 		case 0x82:
 
 			ps->cu_type = 0x3422;
@@ -468,7 +496,7 @@
 
 	case 02:		/* special device class ... */
 
-		switch (p_diag_data->vrdcvtyp) {
+		switch (diag_data.vrdcvtyp) {
 		case 0x20:	/* OSA */
 
 			ps->cu_type = 0x3088;
@@ -493,13 +521,6 @@
 
 	}
 
-	if (init_IRQ_complete) {
-		kfree (p_diag_data);
-	} else {
-		free_bootmem ((unsigned long) p_diag_data, sizeof (diag210_t));
-
-	}
-
 	if (error) 
 		CIO_DEBUG_ALWAYS(KERN_ERR, 0,
 				 "DIAG X'210' for "
@@ -510,12 +531,11 @@
 				 "rdev model: %02X\n",
 				 devno,
 				 ccode,
-				 p_diag_data->vrdcvcla,
-				 p_diag_data->vrdcvtyp,
-				 p_diag_data->vrdcrccl,
-				 p_diag_data->vrdccrty,
-				 p_diag_data->vrdccrmd);
-		
+				 diag_data.vrdcvcla,
+				 diag_data.vrdcvtyp,
+				 diag_data.vrdcrccl,
+				 diag_data.vrdccrty,
+				 diag_data.vrdccrmd);
 }
 
 /*
@@ -544,7 +564,7 @@
 int
 read_dev_chars (int irq, void **buffer, int length)
 {
-	unsigned int flags;
+	unsigned long flags;
 	ccw1_t *rdc_ccw;
 	devstat_t devstat;
 	char *rdc_buf;
@@ -556,17 +576,13 @@
 
 	char dbf_txt[15];
 
-	if (!buffer || !length) {
-		return (-EINVAL);
-
-	}
+	if (!buffer || !length) 
+		return -EINVAL;
 
 	SANITY_CHECK (irq);
 
-	if (ioinfo[irq]->ui.flags.oper == 0) {
-		return (-ENODEV);
-
-	}
+	if (ioinfo[irq]->ui.flags.oper == 0) 
+		return -ENODEV;
 
 	sprintf (dbf_txt, "rddevch%x", irq);
 	CIO_TRACE_EVENT (4, dbf_txt);
@@ -656,9 +672,11 @@
 
 		}
 
+	} else {
+		local_irq_restore(flags);
 	}
 
-	return (ret);
+	return ret;
 }
 
 /*
@@ -678,11 +696,11 @@
 	SANITY_CHECK (irq);
 
 	if (!buffer || !length) {
-		return (-EINVAL);
+		return -EINVAL;
 	} else if (ioinfo[irq]->ui.flags.oper == 0) {
-		return (-ENODEV);
+		return -ENODEV;
 	} else if (ioinfo[irq]->ui.flags.esid == 0) {
-		return (-EOPNOTSUPP);
+		return -EOPNOTSUPP;
 
 	}
 
@@ -693,7 +711,7 @@
 	 * scan for RCD command in extended SenseID data
 	 */
 
-	for (ciw_cnt = 0; (found == 0) && (ciw_cnt < 62); ciw_cnt++) {
+	for (ciw_cnt = 0; (found == 0) && (ciw_cnt < MAX_CIWS); ciw_cnt++) {
 		if (ioinfo[irq]->senseid.ciw[ciw_cnt].ct == CIW_TYPE_RCD) {
 			/*
 			 * paranoia check ...
@@ -812,11 +830,10 @@
 				} while (retry);
 
 			}
-
-			local_irq_restore (flags);
-
 		}
 
+		local_irq_restore(flags);
+
 		/*
 		 * on success we update the user input parms
 		 */
@@ -848,7 +865,7 @@
 
 	}
 
-	return (ret);
+	return ret;
 
 }
 
@@ -862,8 +879,8 @@
 void
 s390_device_recognition_irq (int irq)
 {
-	int ret;
 	char dbf_txt[15];
+	devstat_t devstat;
 
 	sprintf (dbf_txt, "devrec%x", irq);
 	CIO_TRACE_EVENT (4, dbf_txt);
@@ -872,52 +889,78 @@
 	 * We issue the SenseID command on I/O subchannels we think are
 	 *  operational only.
 	 */
-	if ((ioinfo[irq] != INVALID_STORAGE_AREA)
-	    && (!ioinfo[irq]->st)
-	    && (ioinfo[irq]->schib.pmcw.st == 0)
-	    && (ioinfo[irq]->ui.flags.oper == 1)) {
-		int irq_ret;
-		devstat_t devstat;
+	if ((ioinfo[irq]->st) || (!ioinfo[irq]->ui.flags.oper))
+		goto out;
 
-		irq_ret = request_irq (irq,
-				       init_IRQ_handler,
-				       SA_PROBE, "INIT", &devstat);
+	if (request_irq (irq, init_IRQ_handler, SA_PROBE, "INIT", &devstat))
+		goto out;
 
-		if (!irq_ret) {
-			ret = enable_cpu_sync_isc (irq);
+	if (enable_cpu_sync_isc(irq))
+		goto out_freeirq;
 
-			if (!ret) {
-				ioinfo[irq]->ui.flags.unknown = 0;
+	ioinfo[irq]->ui.flags.unknown = 0;
 
-				memset (&ioinfo[irq]->senseid, '\0',
-					sizeof (senseid_t));
+	memset (&ioinfo[irq]->senseid, '\0', sizeof (senseid_t));
 
-				if (cio_sid_with_pgid) {
-					
-					ret = s390_DevicePathVerification(irq,0);
-					
-					if (ret == -EOPNOTSUPP) 
-						/* 
-						 * Doesn't prevent us from proceeding
-						 */
-						ret = 0;
-				}
+	s390_SenseID (irq, &ioinfo[irq]->senseid, 0xff);
 
-				/*
-				 * we'll fallthrough here if we don't want
-				 * to do SPID before SID
-				 */
-				if (!ret) {
-					s390_SenseID (irq, &ioinfo[irq]->senseid, 0xff);
-				}
-				disable_cpu_sync_isc (irq);
+	disable_cpu_sync_isc(irq);
+out_freeirq:
+	free_irq (irq, &devstat);
+out:
+	return;
+}
 
-			}
+static int
+css_bus_match (struct device *dev, struct device_driver *drv)
+{
+	struct subchannel *ioinfo;
+	struct subchannel_driver *sdrv;
 
-			free_irq (irq, &devstat);
+	ioinfo = list_entry (dev, struct subchannel, dev);
+	sdrv   = list_entry (drv, struct subchannel_driver, drv);
 
-		}
-	}
+	return (ioinfo->st == sdrv->st);
+}
+
+struct bus_type css_bus_type = {
+	.name  = "css",
+	.match = &css_bus_match,
+};
+
+static struct device css_bus_device = {
+	.name   = "Channel Subsystem",
+	.bus_id = "css0",
+};
+
+/*
+ * s390_register_subchannel
+ *
+ * initializes the struct device member of a subchannel and gives it to
+ * the device driver core
+ */
+static __devinit void
+s390_register_subchannel (struct subchannel *ioinfo)
+{
+	static const char *subchannel_types[] = {
+		"I/O Subchannel",
+		"CHSC Subchannel",
+		"Message Subchannel",
+		"ADM Subchannel",
+		"undefined subchannel type 4",
+		"undefined subchannel type 5",
+		"undefined subchannel type 6",
+		"undefined subchannel type 7",
+	};
+
+	strncpy (ioinfo->dev.name, subchannel_types[ioinfo->st], DEVICE_NAME_SIZE);
+	snprintf (ioinfo->dev.bus_id, DEVICE_ID_SIZE, "0:%04x", ioinfo->irq);
+
+	ioinfo->dev.bus    = &css_bus_type;
+	ioinfo->dev.parent = &css_bus_device;
+
+	if (device_register(&ioinfo->dev))
+		printk(KERN_WARNING "failed to register subchannel %04x\n", ioinfo->irq);
 }
 
 /*
@@ -926,19 +969,43 @@
  * Used for system wide device recognition.
  *
  */
-static void __init
-s390_device_recognition_all (void)
+static int __init
+s390_probe_css (void)
 {
-	int irq = 0;		/* let's start with subchannel 0 ... */
+	int ret;
+	int irq;
 
-	do {
-		s390_device_recognition_irq (irq);
+	printk (KERN_INFO "probing %d subchannels...\n", highest_subchannel+1);
 
-		irq++;
+	if ((ret = bus_register(&css_bus_type)))
+		goto out;
 
-	} while (irq <= highest_subchannel);
+	if ((ret = device_register(&css_bus_device)))
+		goto out_unregister;
+
+	for (irq = 0; irq <= highest_subchannel; irq++) {
+		if (ioinfo[irq] == INVALID_STORAGE_AREA)
+			continue;
+
+		printk(KERN_INFO "subchannel %04x: devno %04x, ",
+				irq, ioinfo[irq]->devno);
+
+		if (irq != cons_dev) /* console has already been probed */
+			s390_device_recognition_irq (irq);
+
+		s390_register_subchannel(ioinfo[irq]);
+		printk("control unit type %04x\n", ioinfo[irq]->senseid.cu_type);
+	}
+
+out_unregister:
+	if (ret)
+		put_bus(&css_bus_type);
+out:
+	return ret;
 }
 
+subsys_initcall(s390_probe_css);
+
 /*
  * s390_process_subchannels
  *
@@ -986,11 +1053,6 @@
 	int ccode2;		/* condition code for other I/O routines */
 	schib_t *p_schib;
 	int ret;
-#ifdef CONFIG_CHSC
-	int      chp = 0;
-	int      mask;
-#endif /* CONFIG_CHSC */
-
 	char dbf_txt[15];
 
 	sprintf (dbf_txt, "valsch%x", irq);
@@ -1112,19 +1174,7 @@
 	ioinfo[irq]->opm = ioinfo[irq]->schib.pmcw.pim
 	    & ioinfo[irq]->schib.pmcw.pam & ioinfo[irq]->schib.pmcw.pom;
 
-#ifdef CONFIG_CHSC
-	if (ioinfo[irq]->opm) {
-		for (chp=0;chp<=7;chp++) {
-			mask = 0x80 >> chp;
-			if (ioinfo[irq]->opm & mask) {
-				if (!chsc_chpid_logical (irq, chp)) {
-					/* disable using this path */
-					ioinfo[irq]->opm &= ~mask;
-				}
-			}
-		}
-	}
-#endif /* CONFIG_CHSC */
+	chsc_validate_chpids(irq);
 
 	CIO_DEBUG_IFMSG(KERN_INFO, 0,
 			"Detected device %04X "
@@ -1294,7 +1344,7 @@
 
 	}
 
-	return (ret);
+	return ret;
 }
 
 /*
@@ -1313,7 +1363,13 @@
 static int
 s390_SenseID (int irq, senseid_t * sid, __u8 lpm)
 {
-	ccw1_t *sense_ccw;	/* ccw area for SenseID command */
+	/* SenseID may be called during console initialization,
+	 * before we have a working kmalloc, using a static
+	 * ccw area is the least evil workaround */
+	static ccw1_t sense_ccw[2];	/* ccw area for SenseID command */
+	static spinlock_t sid_lock	/* lock to protect sense_ccw */
+		= SPIN_LOCK_UNLOCKED;
+
 	senseid_t isid;		/* internal sid */
 	devstat_t devstat;	/* required by request_irq() */
 	__u8 pathmask;		/* calulate path mask */
@@ -1330,55 +1386,37 @@
 	char dbf_txt[15];
 	int i;
 	int failure = 0;	/* nothing went wrong yet */
-
-	SANITY_CHECK (irq);
+	unsigned long flags;
 
 	if (ioinfo[irq]->ui.flags.oper == 0) {
-		return (-ENODEV);
+		return -ENODEV;
 
 	}
 
 	sprintf (dbf_txt, "snsID%x", irq);
 	CIO_TRACE_EVENT (4, dbf_txt);
 
-	inlreq = 0;		/* to make the compiler quiet... */
-
 	if (!ioinfo[irq]->ui.flags.ready) {
-
-		pdevstat = &devstat;
-
+		inlreq = 1;
 		/*
 		 * Perform SENSE ID command processing. We have to request device
 		 *  ownership and provide a dummy I/O handler. We issue sync. I/O
 		 *  requests and evaluate the devstat area on return therefore
 		 *  we don't need a real I/O handler in place.
 		 */
-		irq_ret =
-		    request_irq (irq, init_IRQ_handler, SA_PROBE, "SID",
-				 &devstat);
+		if ((irq_ret = request_irq (irq, init_IRQ_handler,
+						SA_PROBE, "SID", &devstat)))
+				return irq_ret;
 
-		if (irq_ret == 0)
-			inlreq = 1;
 	} else {
 		inlreq = 0;
-		irq_ret = 0;
-		pdevstat = ioinfo[irq]->irq_desc.dev_id;
-
 	}
 
-	if (irq_ret) {
-		return irq_ret;
-	}
+	pdevstat = ioinfo[irq]->irq_desc.dev_id;
 
+	spin_lock_irqsave(&sid_lock, flags);
 	s390irq_spin_lock (irq);
 
-	if (init_IRQ_complete) {
-		sense_ccw = kmalloc (2 * sizeof (ccw1_t), GFP_DMA);
-	} else {
-		sense_ccw = alloc_bootmem_low (2 * sizeof (ccw1_t));
-
-	}
-
 	/* more than one path installed ? */
 	if (ioinfo[irq]->schib.pmcw.pim != 0x80) {
 		sense_ccw[0].cmd_code = CCW_CMD_SUSPEND_RECONN;
@@ -1598,14 +1636,8 @@
 
 	}
 
-	if (init_IRQ_complete) {
-		kfree (sense_ccw);
-	} else {
-		free_bootmem ((unsigned long) sense_ccw, 2 * sizeof (ccw1_t));
-
-	}
-
 	s390irq_spin_unlock (irq);
+	spin_unlock_irqrestore(&sid_lock, flags);
 
 	/*
 	 * If we installed the irq action handler we have to
@@ -1620,7 +1652,7 @@
 	 */
 	if ((sid->cu_type == 0xFFFF)
 	    && (MACHINE_IS_VM)) {
-		_VM_virtual_device_info (ioinfo[irq]->schib.pmcw.dev, sid);
+		VM_virtual_device_info (ioinfo[irq]->schib.pmcw.dev, sid);
 	}
 
 	if (sid->cu_type == 0xFFFF) {
@@ -1636,43 +1668,18 @@
 			  ioinfo[irq]->schib.pmcw.dev, irq);
 
 		ioinfo[irq]->ui.flags.unknown = 1;
-
+		return -ENODEV;
 	}
 
 	/*
 	 * Issue device info message if unit was operational .
 	 */
-	if (!ioinfo[irq]->ui.flags.unknown) {
-		if (sid->dev_type != 0) {
+	CIO_DEBUG_IFMSG(KERN_INFO, 2, "SenseID : device %04X reports: "
+		"CU  Type/Mod = %04X/%02X, Dev Type/Mod = %04X/%02X\n",
+		ioinfo[irq]->schib.pmcw.dev, sid->cu_type, sid->cu_model,
+		sid->dev_type, sid->dev_model);
 
-			CIO_DEBUG_IFMSG(KERN_INFO, 2,
-					"SenseID : device %04X reports: "
-					"CU  Type/Mod = %04X/%02X,"
-					" Dev Type/Mod = %04X/%02X\n",
-					ioinfo[irq]->schib.pmcw.dev,
-					sid->cu_type,
-					sid->cu_model,
-					sid->dev_type,
-					sid->dev_model);
-
-		} else {
-
-			CIO_DEBUG_IFMSG(KERN_INFO, 2,
-					"SenseID : device %04X reports:"
-					" Dev Type/Mod = %04X/%02X\n",
-					ioinfo[irq]->schib.pmcw.dev,
-					sid->cu_type,
-					sid->cu_model);
-		}
-
-	}
-
-	if (!ioinfo[irq]->ui.flags.unknown)
-		irq_ret = 0;
-	else
-		irq_ret = -ENODEV;
-
-	return (irq_ret);
+	return 0;
 }
 
 /*
@@ -1691,17 +1698,12 @@
 	int ccode;
 	__u8 pathmask;
 	__u8 domask;
-#ifdef CONFIG_CHSC
-	int chp;
-	int mask;
 	int old_opm = 0;
-#endif /* CONFIG_CHSC */
 
 	int ret = 0;
 	int i;
 	pgid_t pgid;
 	__u8 dev_path;
-	int first = 1;
 
 	char dbf_txt[15];
 
@@ -1711,9 +1713,7 @@
 	if (ioinfo[irq]->st) 
 		return -ENODEV;
 
-#ifdef CONFIG_CHSC
 	old_opm = ioinfo[irq]->opm;
-#endif /* CONFIG_CHSC */
 	ccode = stsch (irq, &(ioinfo[irq]->schib));
 
 	if (ccode) {
@@ -1726,7 +1726,6 @@
 		ioinfo[irq]->ui.flags.pgid_supp = 0;
 		ret = 0;
 
-#ifdef CONFIG_CHSC
 		/*
 		 * disable if chpid is logically offline
 		 */
@@ -1772,25 +1771,13 @@
 		} else {
 			ret = 0;
 		}
-#endif /* CONFIG_CHSC */
 		return ret;
 	}
 
 	ioinfo[irq]->opm = ioinfo[irq]->schib.pmcw.pim
 	    & ioinfo[irq]->schib.pmcw.pam & ioinfo[irq]->schib.pmcw.pom;
 
-#ifdef CONFIG_CHSC
-	if (ioinfo[irq]->opm) {
-		for (chp=0;chp<=7;chp++) {
-			mask = 0x80 >> chp;
-			if (ioinfo[irq]->opm & mask) {
-				if (!chsc_chpid_logical (irq, chp)) {
-					/* disable using this path */
-					ioinfo[irq]->opm &= ~mask;
-				}
-			}
-		}
-	}
+	chsc_validate_chpids(irq);
 	
 	if ((ioinfo[irq]->opm == 0) && (old_opm)) {
 		not_oper_handler_func_t nopfunc=ioinfo[irq]->nopfunc;
@@ -1821,10 +1808,9 @@
 				pdevreg->oper_func( irq, pdevreg);
 
 	}
-#endif /* CONFIG_CHSC */
 
 	if ( ioinfo[irq]->ui.flags.pgid_supp == 0 )
-		return( 0);	/* just exit ... */
+		return 0;	/* just exit ... */
 
 	if (usermask) {
 		dev_path = usermask;
@@ -1837,7 +1823,6 @@
 		memcpy (&ioinfo[irq]->pgid, &global_pgid, sizeof (pgid_t));
 		ioinfo[irq]->ui.flags.pgid = 1;
 	}
-	memcpy (&pgid, &ioinfo[irq]->pgid, sizeof (pgid_t));
 
 	for (i = 0; i < 8 && !ret; i++) {
 		pathmask = 0x80 >> i;
@@ -1845,46 +1830,38 @@
 		domask = dev_path & pathmask;
 
 		if (domask) {
-			ret = s390_SetPGID (irq, domask, &pgid);
-
-			/*
-			 * For the *first* path we are prepared
-			 *  for recovery
-			 *
-			 *  - If we fail setting the PGID we assume its
-			 *     using  a different PGID already (VM) we
-			 *     try to sense.
-			 */
-			if (ret == -EOPNOTSUPP && first) {
+			if (MACHINE_IS_VM) {
+				/*
+				 * If we are running under VM, try to obtain
+				 * VM's PGID by SensePGID
+				 */
 				*(int *) &pgid = 0;
-
-				ret = s390_SensePGID (irq, domask, &pgid);
-				first = 0;
-
-				if (ret == 0) {
+				
+				if (!s390_SensePGID (irq, domask, &pgid)) 
 					/*
 					 * Check whether we retrieved
 					 *  a reasonable PGID ...
 					 */
 					if (pgid.inf.ps.state1 ==
-					    SNID_STATE1_GROUPED) {
+					    SNID_STATE1_GROUPED) 
 						memcpy (&ioinfo[irq]->pgid,
 							&pgid, sizeof (pgid_t));
-					} else {	/* ungrouped or garbage ... */
-						ret = -EOPNOTSUPP;
 
-					}
-				} else {
-					ioinfo[irq]->ui.flags.pgid_supp = 0;
+			}
+			
+			ret = s390_SetPGID (irq, domask);
 
-					CIO_DEBUG(KERN_WARNING, 2,
-						  "PathVerification(%04X) "
-						  "- Device %04X doesn't "
-						  " support path grouping\n",
-						  irq,
-						  ioinfo[irq]->schib.pmcw.dev);
-					
-				}
+			if (ret == -EOPNOTSUPP) {
+
+				ioinfo[irq]->ui.flags.pgid_supp = 0;
+				
+				CIO_DEBUG(KERN_WARNING, 2,
+					  "PathVerification(%04X) "
+					  "- Device %04X doesn't "
+					  " support path grouping\n",
+					  irq,
+					  ioinfo[irq]->schib.pmcw.dev);
+				
 			} else if (ret == -EIO) {
 
 				CIO_DEBUG(KERN_ERR, 2,
@@ -1907,12 +1884,19 @@
 
 				ret = 0;
 
-			} else {
+			} else if (ret == -ENODEV) {
 
 				CIO_DEBUG(KERN_ERR, 2,
-					  "PathVerification(%04X) - "
-					  "Unexpected error on device %04X\n",
+					  "PathVerification(%04X) "
+					  "- Device %04X is no longer there?!?\n",
 					  irq, ioinfo[irq]->schib.pmcw.dev);
+
+			} else if (ret) {
+
+				CIO_DEBUG(KERN_ERR, 2,
+					  "PathVerification(%04X) - "
+					  "Unexpected error %d on device %04X\n",
+					  irq, ret, ioinfo[irq]->schib.pmcw.dev);
 				
 				ioinfo[irq]->ui.flags.pgid_supp = 0;
 			}
@@ -1929,7 +1913,7 @@
  *
  */
 static int
-s390_SetPGID (int irq, __u8 lpm, pgid_t * pgid)
+s390_SetPGID (int irq, __u8 lpm)
 {
 	ccw1_t *spid_ccw;	/* ccw area for SPID command */
 	devstat_t devstat;	/* required by request_irq() */
@@ -1942,10 +1926,8 @@
 	int inlreq = 0;		/* inline request_irq() */
 	int mpath = 1;		/* try multi-path first */
 
-	SANITY_CHECK (irq);
-
 	if (ioinfo[irq]->ui.flags.oper == 0) {
-		return (-ENODEV);
+		return -ENODEV;
 
 	}
 
@@ -1989,11 +1971,11 @@
 	spid_ccw[0].flags = CCW_FLAG_SLI | CCW_FLAG_CC;
 
 	spid_ccw[1].cmd_code = CCW_CMD_SET_PGID;
-	spid_ccw[1].cda = (__u32) virt_to_phys (pgid);
+	spid_ccw[1].cda = (__u32) virt_to_phys (&ioinfo[irq]->pgid);
 	spid_ccw[1].count = sizeof (pgid_t);
 	spid_ccw[1].flags = CCW_FLAG_SLI;
 
-	pgid->inf.fc = SPID_FUNC_MULTI_PATH | SPID_FUNC_ESTABLISH;
+	ioinfo[irq]->pgid.inf.fc = SPID_FUNC_MULTI_PATH | SPID_FUNC_ESTABLISH;
 
 	/*
 	 * We now issue a SetPGID request. In case of BUSY
@@ -2047,13 +2029,13 @@
 						spid_ccw[0].cmd_code =
 						    CCW_CMD_SET_PGID;
 						spid_ccw[0].cda = (__u32)
-						    virt_to_phys (pgid);
+						    virt_to_phys (&ioinfo[irq]->pgid);
 						spid_ccw[0].count =
 						    sizeof (pgid_t);
 						spid_ccw[0].flags =
 						    CCW_FLAG_SLI;
 
-						pgid->inf.fc =
+						ioinfo[irq]->pgid.inf.fc =
 						    SPID_FUNC_SINGLE_PATH
 						    | SPID_FUNC_ESTABLISH;
 						mpath = 0;
@@ -2107,6 +2089,7 @@
 
 				retry = 0;
 				ioinfo[irq]->opm &= ~lpm;
+				switch_off_chpids(irq, lpm);
 				irq_ret = -EAGAIN;
 
 			}
@@ -2153,7 +2136,7 @@
 	if (inlreq)
 		free_irq (irq, pdevstat);
 
-	return (irq_ret);
+	return irq_ret;
 }
 
 /*
@@ -2169,16 +2152,15 @@
 	devstat_t devstat;	/* required by request_irq() */
 	devstat_t *pdevstat = &devstat;
 	char dbf_txt[15];
+	pgid_t * tmp_pgid;
 
 	int irq_ret = 0;	/* return code */
 	int retry = 5;		/* retry count */
 	int inlreq = 0;		/* inline request_irq() */
 	unsigned long flags;
 
-	SANITY_CHECK (irq);
-
 	if (ioinfo[irq]->ui.flags.oper == 0) {
-		return (-ENODEV);
+		return -ENODEV;
 
 	}
 
@@ -2212,13 +2194,14 @@
 
 	if (init_IRQ_complete) {
 		snid_ccw = kmalloc (sizeof (ccw1_t), GFP_DMA);
+		tmp_pgid = kmalloc (sizeof (pgid_t), GFP_DMA);
 	} else {
 		snid_ccw = alloc_bootmem_low (sizeof (ccw1_t));
-
+		tmp_pgid = alloc_bootmem_low (sizeof (pgid_t));
 	}
 
 	snid_ccw->cmd_code = CCW_CMD_SENSE_PGID;
-	snid_ccw->cda = (__u32) virt_to_phys (pgid);
+	snid_ccw->cda = (__u32) virt_to_phys (tmp_pgid);
 	snid_ccw->count = sizeof (pgid_t);
 	snid_ccw->flags = CCW_FLAG_SLI;
 
@@ -2242,8 +2225,8 @@
 				 *  Sense Path Group ID command
 				 *  further retries wouldn't help ...
 				 */
-				if (pdevstat->ii.sense.
-				    data[0] & SNS0_CMD_REJECT) {
+				if (pdevstat->ii.sense.data[0] & 
+				    (SNS0_CMD_REJECT | SNS0_INTERVENTION_REQ)) {
 					retry = 0;
 					irq_ret = -EOPNOTSUPP;
 				} else {
@@ -2294,6 +2277,7 @@
 			} else {
 				retry = 0;	/* success ... */
 				irq_ret = 0;
+				memcpy(pgid, tmp_pgid, sizeof(pgid_t));
 
 			}
 		} else if (irq_ret != -ENODEV) {	/* -EIO, or -EBUSY */
@@ -2328,9 +2312,10 @@
 
 	if (init_IRQ_complete) {
 		kfree (snid_ccw);
+		kfree (tmp_pgid);
 	} else {
 		free_bootmem ((unsigned long) snid_ccw, sizeof (ccw1_t));
-
+		free_bootmem ((unsigned long) tmp_pgid, sizeof (tmp_pgid));
 	}
 
 	s390irq_spin_unlock_irqrestore (irq, flags);
@@ -2342,20 +2327,18 @@
 	if (inlreq)
 		free_irq (irq, pdevstat);
 
-	return (irq_ret);
+	return irq_ret;
 }
 
 /*
  * Function: s390_send_nop
  * 
  * sends a nop CCW to the specified subchannel down the given path(s)
- * FIXME: why not put nop_ccw on the stack, it's only 64 bits?
  */
 int
 s390_send_nop(int irq, __u8 lpm)
 {
  	char dbf_txt[15];
- 	ccw1_t *nop_ccw;
  	devstat_t devstat;
  	devstat_t *pdevstat = &devstat;
  	unsigned long flags;
@@ -2363,8 +2346,14 @@
  	int irq_ret = 0;
  	int inlreq = 0;
 	
- 	SANITY_CHECK(irq);
-	
+	/* static allocation is necessary because nop_ccw has to be below
+	 * the 2GB border. locking is not required since it nop_ccw is
+	 * never modified */
+	static ccw1_t nop_ccw = {
+		.cmd_code  = CCW_CMD_NOOP,
+		.flags	   = CCW_FLAG_SLI,
+	};
+
  	if (!ioinfo[irq]->ui.flags.oper)
  		/* no sense in trying */
  		return -ENODEV;
@@ -2392,19 +2381,9 @@
  
  	s390irq_spin_lock_irqsave (irq, flags);
  
- 	if (init_IRQ_complete)
- 		nop_ccw = kmalloc (sizeof (ccw1_t), GFP_DMA);
- 	else
- 		nop_ccw = alloc_bootmem_low (sizeof (ccw1_t));
- 
- 	nop_ccw->cmd_code = CCW_CMD_NOOP;
- 	nop_ccw->cda = 0;
- 	nop_ccw->count = 0;
- 	nop_ccw->flags = CCW_FLAG_SLI;
- 
  	memset (pdevstat, '\0', sizeof (devstat_t));
  	
- 	irq_ret = s390_start_IO (irq, nop_ccw, 0xE2D5D6D7, lpm,
+ 	irq_ret = s390_start_IO (irq, &nop_ccw, 0xE2D5D6D7, lpm,
  				 DOIO_WAIT_FOR_INTERRUPT
  				 | DOIO_TIMEOUT
  				 | DOIO_DONT_CALL_INTHDLR
@@ -2415,11 +2394,6 @@
  		/* better cancel... */
  		cancel_IO(irq);
  	}
- 
- 	if (init_IRQ_complete) 
- 		kfree (nop_ccw);
- 	else
- 		free_bootmem ((unsigned long) nop_ccw, sizeof (ccw1_t));
  
  	s390irq_spin_unlock_irqrestore (irq, flags);
  
diff -Nru a/drivers/s390/misc/chandev.c b/drivers/s390/misc/chandev.c
--- a/drivers/s390/misc/chandev.c	Mon Oct  7 11:25:32 2002
+++ b/drivers/s390/misc/chandev.c	Mon Oct  7 11:25:32 2002
@@ -24,6 +24,7 @@
 #include <asm/s390dyn.h>
 #include <asm/queue.h>
 #include <linux/kmod.h>
+#include <linux/tqueue.h>
 #ifndef MIN
 #define MIN(a,b) ((a<b)?a:b)
 #endif
@@ -2825,6 +2826,7 @@
 	struct stat statbuf;
 	char        *buff;
 	int         curr,left,len,fd;
+	mm_segment_t oldfs;
 
 	/* if called from chandev_register_and_probe & 
 	   the driver is compiled into the kernel the
@@ -2835,6 +2837,7 @@
 	if(in_interrupt()||current->fs->root==NULL)
 		return;
 	atomic_set(&chandev_conf_read,TRUE);
+	oldfs = get_fs();
 	set_fs(KERNEL_DS);
 	if(stat(CHANDEV_FILE,&statbuf)==0)
 	{
@@ -2859,7 +2862,7 @@
 			vfree(buff);
 		}
 	}
-	set_fs(USER_DS);
+	set_fs(oldfs);
 }
 
 static void chandev_read_conf_if_necessary(void)
diff -Nru a/drivers/s390/net/ctcmain.c b/drivers/s390/net/ctcmain.c
--- a/drivers/s390/net/ctcmain.c	Mon Oct  7 11:25:32 2002
+++ b/drivers/s390/net/ctcmain.c	Mon Oct  7 11:25:32 2002
@@ -49,6 +49,7 @@
 #include <linux/interrupt.h>
 #include <linux/timer.h>
 #include <linux/sched.h>
+#include <linux/tqueue.h>
 
 #include <linux/signal.h>
 #include <linux/string.h>
@@ -195,7 +196,7 @@
 	unsigned long doios_multi;
 	unsigned long txlen;
 	unsigned long tx_time;
-	struct timeval send_stamp;
+	struct timespec send_stamp;
 } ctc_profile;
 
 /**
@@ -976,10 +977,10 @@
 	int            first = 1;
 	int            i;
 
-	struct timeval done_stamp = xtime;
+	struct timespec done_stamp = xtime;
 	unsigned long duration = 
 		(done_stamp.tv_sec - ch->prof.send_stamp.tv_sec) * 1000000 +
-		done_stamp.tv_usec - ch->prof.send_stamp.tv_usec;
+		(done_stamp.tv_nsec - ch->prof.send_stamp.tv_nsec) / 1000;
 	if (duration > ch->prof.tx_time)
 		ch->prof.tx_time = duration;
 
diff -Nru a/drivers/s390/net/ctctty.c b/drivers/s390/net/ctctty.c
--- a/drivers/s390/net/ctctty.c	Mon Oct  7 11:25:35 2002
+++ b/drivers/s390/net/ctctty.c	Mon Oct  7 11:25:35 2002
@@ -86,7 +86,7 @@
   wait_queue_head_t	open_wait;
   wait_queue_head_t	close_wait;
   struct semaphore      write_sem;
-  struct tq_struct      tq;
+  struct tasklet_struct tasklet;
   struct timer_list     stoptimer;
 } ctc_tty_info;
 
@@ -272,8 +272,7 @@
 	 */
 	skb_queue_tail(&info->rx_queue, skb);
 	/* Schedule dequeuing */
-	queue_task(&info->tq, &tq_immediate);
-	mark_bh(IMMEDIATE_BH);
+	tasklet_schedule(&info->tasklet);
 }
 
 static int
@@ -390,8 +389,7 @@
 	skb_reserve(skb, skb_res);
 	*(skb_put(skb, 1)) = c;
 	skb_queue_head(&info->tx_queue, skb);
-	queue_task(&info->tq, &tq_immediate);
-	mark_bh(IMMEDIATE_BH);
+	tasklet_schedule(&info->tasklet);
 }
 
 static void
@@ -400,8 +398,7 @@
 	if (ctc_tty_shuttingdown)
 		return;
 	info->flags |= CTC_ASYNC_TX_LINESTAT;
-	queue_task(&info->tq, &tq_immediate);
-	mark_bh(IMMEDIATE_BH);
+	tasklet_schedule(&info->tasklet);
 }
 
 static void
@@ -562,8 +559,7 @@
 	}
 	if (skb_queue_len(&info->tx_queue)) {
 		info->lsr &= ~UART_LSR_TEMT;
-		queue_task(&info->tq, &tq_immediate);
-		mark_bh(IMMEDIATE_BH);
+		tasklet_schedule(&info->tasklet);
 	}
 	if (from_user)
 		up(&info->write_sem);
@@ -596,21 +592,17 @@
 	ctc_tty_info *info;
 	unsigned long flags;
 
-#warning FIXME [kj] Consider using spinlocks.
-	save_flags(flags);
-	cli();
-	if (!tty) {
-		restore_flags(flags);
+	if (!tty)
 		return;
-	}
+	spin_lock_irqsave(&ctc_tty_lock, flags);
 	info = (ctc_tty_info *) tty->driver_data;
 	if (ctc_tty_paranoia_check(info, tty->device, "ctc_tty_flush_buffer")) {
-		restore_flags(flags);
+		spin_unlock_irqrestore(&ctc_tty_lock, flags);
 		return;
 	}
 	skb_queue_purge(&info->tx_queue);
 	info->lsr |= UART_LSR_TEMT;
-	restore_flags(flags);
+	spin_unlock_irqrestore(&ctc_tty_lock, flags);
 	wake_up_interruptible(&tty->write_wait);
 	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
 	    tty->ldisc.write_wakeup)
@@ -628,8 +620,7 @@
 		return;
 	if (tty->stopped || tty->hw_stopped || (!skb_queue_len(&info->tx_queue)))
 		return;
-	queue_task(&info->tq, &tq_immediate);
-	mark_bh(IMMEDIATE_BH);
+	tasklet_schedule(&info->tasklet);
 }
 
 /*
@@ -689,10 +680,9 @@
 	uint result;
 	ulong flags;
 
-	save_flags(flags);
-	cli();
+	spin_lock_irqsave(&ctc_tty_lock, flags);
 	status = info->lsr;
-	restore_flags(flags);
+	spin_unlock_irqrestore(&ctc_tty_lock, flags);
 	result = ((status & UART_LSR_TEMT) ? TIOCSER_TEMT : 0);
 	put_user(result, (uint *) value);
 	return 0;
@@ -708,10 +698,9 @@
 	ulong flags;
 
 	control = info->mcr;
-	save_flags(flags);
-	cli();
+	spin_lock_irqsave(&ctc_tty_lock, flags);
 	status = info->msr;
-	restore_flags(flags);
+	spin_unlock_irqrestore(&ctc_tty_lock, flags);
 	result = ((control & UART_MCR_RTS) ? TIOCM_RTS : 0)
 	    | ((control & UART_MCR_DTR) ? TIOCM_DTR : 0)
 	    | ((status & UART_MSR_DCD) ? TIOCM_CAR : 0)
@@ -942,11 +931,10 @@
 	printk(KERN_DEBUG "ctc_tty_block_til_ready before block: %s%d, count = %d\n",
 	       CTC_TTY_NAME, info->line, info->count);
 #endif
-	save_flags(flags);
-	cli();
+	spin_lock_irqsave(&ctc_tty_lock, flags);
 	if (!(tty_hung_up_p(filp)))
 		info->count--;
-	restore_flags(flags);
+	spin_unlock_irqrestore(&ctc_tty_lock, flags);
 	info->blocked_open++;
 	while (1) {
 		set_current_state(TASK_INTERRUPTIBLE);
@@ -1053,16 +1041,14 @@
 ctc_tty_close(struct tty_struct *tty, struct file *filp)
 {
 	ctc_tty_info *info = (ctc_tty_info *) tty->driver_data;
-	unsigned long saveflags;
 	ulong flags;
 	ulong timeout;
 
 	if (!info || ctc_tty_paranoia_check(info, tty->device, "ctc_tty_close"))
 		return;
-	save_flags(flags);
-	cli();
+	spin_lock_irqsave(&ctc_tty_lock, flags);
 	if (tty_hung_up_p(filp)) {
-		restore_flags(flags);
+		spin_unlock_irqrestore(&ctc_tty_lock, flags);
 #ifdef CTC_DEBUG_MODEM_OPEN
 		printk(KERN_DEBUG "ctc_tty_close return after tty_hung_up_p\n");
 #endif
@@ -1086,7 +1072,7 @@
 		info->count = 0;
 	}
 	if (info->count) {
-		restore_flags(flags);
+		local_irq_restore(flags);
 #ifdef CTC_DEBUG_MODEM_OPEN
 		printk(KERN_DEBUG "ctc_tty_close after info->count != 0\n");
 #endif
@@ -1117,7 +1103,9 @@
 		timeout = jiffies + HZ;
 		while (!(info->lsr & UART_LSR_TEMT)) {
 			set_current_state(TASK_INTERRUPTIBLE);
+			spin_unlock_irqrestore(&ctc_tty_lock, flags);
 			schedule_timeout(20);
+			spin_lock_irqsave(&ctc_tty_lock, flags);
 			if (time_after(jiffies,timeout))
 				break;
 		}
@@ -1127,9 +1115,7 @@
 		tty->driver.flush_buffer(tty);
 	if (tty->ldisc.flush_buffer)
 		tty->ldisc.flush_buffer(tty);
-	spin_lock_irqsave(&ctc_tty_lock, saveflags);
 	info->tty = 0;
-	spin_unlock_irqrestore(&ctc_tty_lock, saveflags);
 	tty->closing = 0;
 	if (info->blocked_open) {
 		set_current_state(TASK_INTERRUPTIBLE);
@@ -1138,7 +1124,7 @@
 	}
 	info->flags &= ~(CTC_ASYNC_NORMAL_ACTIVE | CTC_ASYNC_CLOSING);
 	wake_up_interruptible(&info->close_wait);
-	restore_flags(flags);
+	spin_unlock_irqrestore(&ctc_tty_lock, flags);
 #ifdef CTC_DEBUG_MODEM_OPEN
 	printk(KERN_DEBUG "ctc_tty_close normal exit\n");
 #endif
@@ -1170,8 +1156,9 @@
  * the lower levels.
  */
 static void
-ctc_tty_task(ctc_tty_info *info)
+ctc_tty_task(unsigned long arg)
 {
+	ctc_tty_info *info = (void *)arg;
 	unsigned long saveflags;
 	int again;
 
@@ -1182,8 +1169,7 @@
 			info->lsr |= UART_LSR_TEMT;
 		again |= ctc_tty_readmodem(info);
 		if (again) {
-			queue_task(&info->tq, &tq_immediate);
-			mark_bh(IMMEDIATE_BH);
+			tasklet_schedule(&info->tasklet);
 		}
 	}
 	spin_unlock_irqrestore(&ctc_tty_lock, saveflags);
@@ -1243,14 +1229,8 @@
 	for (i = 0; i < CTC_TTY_MAX_DEVICES; i++) {
 		info = &driver->info[i];
 		init_MUTEX(&info->write_sem);
-#if LINUX_VERSION_CODE >= 0x020400
-		INIT_LIST_HEAD(&info->tq.list);
-#else
-		info->tq.next    = NULL;
-#endif
-		info->tq.sync    = 0;
-		info->tq.routine = (void *)(void *)ctc_tty_task;
-		info->tq.data    = info;
+		tasklet_init(&info->tasklet, ctc_tty_task,
+				(unsigned long) info);
 		info->magic = CTC_ASYNC_MAGIC;
 		info->line = i;
 		info->tty = 0;
@@ -1331,10 +1311,6 @@
 		kfree(driver);
 		driver = NULL;
 	} else {
-		int i;
-
-		for (i = 0; i < CTC_TTY_MAX_DEVICES; i++)
-			driver->info[i].tq.routine = NULL;
 		tty_unregister_driver(&driver->ctc_tty_device);
 	}
 	spin_unlock_irqrestore(&ctc_tty_lock, saveflags);
diff -Nru a/drivers/s390/net/iucv.c b/drivers/s390/net/iucv.c
--- a/drivers/s390/net/iucv.c	Mon Oct  7 11:25:25 2002
+++ b/drivers/s390/net/iucv.c	Mon Oct  7 11:25:25 2002
@@ -41,9 +41,9 @@
 #include <linux/kernel.h>
 #include <linux/slab.h>
 #include <linux/init.h>
-#include <linux/tqueue.h>
 #include <linux/interrupt.h>
 #include <linux/list.h>
+#include <linux/errno.h>
 #include <asm/atomic.h>
 #include "iucv.h"
 #include <asm/io.h>
@@ -99,16 +99,14 @@
 static struct list_head  iucv_irq_queue;
 static spinlock_t iucv_irq_queue_lock = SPIN_LOCK_UNLOCKED;
 
-static struct tq_struct  iucv_tq;
-
-static atomic_t   iucv_bh_scheduled = ATOMIC_INIT (0);
-
 /*
  *Internal function prototypes
  */
-static void iucv_bh_handler(void);
+static void iucv_tasklet_handler(unsigned long);
 static void iucv_irq_handler(struct pt_regs *, __u16);
 
+static DECLARE_TASKLET(iucv_tasklet,iucv_tasklet_handler,0);
+
 /************ FUNCTION ID'S ****************************/
 
 #define ACCEPT          10
@@ -302,7 +300,7 @@
 	if (debuglevel < 3)
 		return;
 
-	printk(KERN_DEBUG __FUNCTION__ ": %s\n", title);
+	printk(KERN_DEBUG "%s\n", title);
 	printk("  ");
 	for (i = 0; i < len; i++) {
 		if (!(i % 16) && i != 0)
@@ -318,7 +316,7 @@
 #define iucv_debug(lvl, fmt, args...) \
 do { \
 	if (debuglevel >= lvl) \
-		printk(KERN_DEBUG __FUNCTION__ ": " fmt "\n" , ## args); \
+		printk(KERN_DEBUG "%s: " fmt "\n", __FUNCTION__ , ## args); \
 } while (0)
 
 #else
@@ -385,11 +383,6 @@
 	}
 	memset(iucv_param_pool, 0, sizeof(iucv_param) * PARAM_POOL_SIZE);
 
-	/* Initialize task queue */
-	INIT_LIST_HEAD(&iucv_tq.list);
-	iucv_tq.sync = 0;
-	iucv_tq.routine = (void *)iucv_bh_handler;
-
 	/* Initialize irq queue */
 	INIT_LIST_HEAD(&iucv_irq_queue);
 
@@ -2177,20 +2170,19 @@
  * @code: irq code
  *
  * Handles external interrupts coming in from CP.
- * Places the interrupt buffer on a queue and schedules iucv_bh_handler().
+ * Places the interrupt buffer on a queue and schedules iucv_tasklet_handler().
  */
 static void
 iucv_irq_handler(struct pt_regs *regs, __u16 code)
 {
 	iucv_irqdata *irqdata;
-	int          cpu = smp_processor_id();
 
-	irq_enter(cpu, 0x4000);
+	irq_enter();
 
 	irqdata = kmalloc(sizeof(iucv_irqdata), GFP_ATOMIC);
 	if (!irqdata) {
 		printk(KERN_WARNING "%s: out of memory\n", __FUNCTION__);
-		irq_exit(cpu, 0x4000);
+		irq_exit();
 		return;
 	}
 
@@ -2201,12 +2193,9 @@
 	list_add_tail(&irqdata->queue, &iucv_irq_queue);
 	spin_unlock(&iucv_irq_queue_lock);
 
-	if (atomic_compare_and_swap (0, 1, &iucv_bh_scheduled) == 0) {
-		queue_task (&iucv_tq, &tq_immediate);
-		mark_bh(IMMEDIATE_BH);
-	}
+	tasklet_schedule(&iucv_tasklet);
 
-	irq_exit(cpu, 0x4000);
+	irq_exit();
 	return;
 }
 
@@ -2215,7 +2204,7 @@
  * @int_buf: Pointer to copy of external interrupt buffer
  *
  * The workhorse for handling interrupts queued by iucv_irq_handler().
- * This function is called from the bottom half iucv_bh_handler().
+ * This function is called from the bottom half iucv_tasklet_handler().
  */
 static void
 iucv_do_int(iucv_GeneralInterrupt * int_buf)
@@ -2385,19 +2374,17 @@
 }
 
 /**
- * iucv_bh_handler:
+ * iucv_tasklet_handler:
  *
  * This function loops over the queue of irq buffers and runs iucv_do_int()
  * on every queue element.
  */
 static void
-iucv_bh_handler(void)
+iucv_tasklet_handler(unsigned long ignored)
 {
 	struct list_head head;
 	struct list_head *next;
 	ulong  flags;
-
-	atomic_set(&iucv_bh_scheduled, 0);
 
 	spin_lock_irqsave(&iucv_irq_queue_lock, flags);
 	list_add(&head, &iucv_irq_queue);
diff -Nru a/drivers/s390/net/lcs.c b/drivers/s390/net/lcs.c
--- a/drivers/s390/net/lcs.c	Mon Oct  7 11:25:26 2002
+++ b/drivers/s390/net/lcs.c	Mon Oct  7 11:25:26 2002
@@ -126,6 +126,7 @@
 #include <linux/sched.h>
 #include <linux/errno.h>
 #include <linux/init.h>
+#include <linux/tqueue.h>
 #include <asm/system.h>
 #include <asm/io.h>
 #include <asm/irq.h>
diff -Nru a/drivers/s390/net/netiucv.c b/drivers/s390/net/netiucv.c
--- a/drivers/s390/net/netiucv.c	Mon Oct  7 11:25:35 2002
+++ b/drivers/s390/net/netiucv.c	Mon Oct  7 11:25:35 2002
@@ -88,7 +88,7 @@
 	unsigned long doios_multi;
 	unsigned long txlen;
 	unsigned long tx_time;
-	struct timeval send_stamp;
+	struct timespec send_stamp;
 } connection_profile;
 
 /**
diff -Nru a/drivers/s390/qdio.c b/drivers/s390/qdio.c
--- a/drivers/s390/qdio.c	Mon Oct  7 11:25:26 2002
+++ b/drivers/s390/qdio.c	Mon Oct  7 11:25:26 2002
@@ -245,26 +245,21 @@
 static void qdio_wait_nonbusy(unsigned int timeout)
 {
         unsigned int start;
-        unsigned long flags;
         char dbf_text[15];
 
 	sprintf(dbf_text,"wtnb%4x",timeout);
 	QDIO_DBF_TEXT3(0,trace,dbf_text);
 
 	start=qdio_get_millis();
-	save_flags(flags); cli();
 	for (;;) {
 		set_task_state(current,TASK_INTERRUPTIBLE);
 		if (qdio_get_millis()-start>timeout) {
 			goto out;
 		}
-		restore_flags(flags);
 		schedule_timeout(((start+timeout-qdio_get_millis())>>10)*HZ);
-		save_flags(flags); cli();
 	}
 out:
 	set_task_state(current,TASK_RUNNING);
-	restore_flags(flags);
 }
 
 static int qdio_wait_for_no_use_count(atomic_t *use_count)
diff -Nru a/drivers/s390/s390mach.c b/drivers/s390/s390mach.c
--- a/drivers/s390/s390mach.c	Mon Oct  7 11:25:39 2002
+++ b/drivers/s390/s390mach.c	Mon Oct  7 11:25:39 2002
@@ -11,6 +11,7 @@
 #include <linux/spinlock.h>
 #include <linux/init.h>
 #include <linux/slab.h>
+#include <linux/completion.h>
 #ifdef CONFIG_SMP
 #include <linux/smp.h>
 #endif
@@ -51,6 +52,7 @@
 static spinlock_t crw_queue_lock = SPIN_LOCK_UNLOCKED;
 
 static struct semaphore s_sem;
+static DECLARE_COMPLETION(mchchk_thread_active);
 
 #ifdef CONFIG_MACHCHK_WARNING
 static int mchchk_wng_posted = 0;
@@ -103,14 +105,15 @@
 	kernel_thread(s390_machine_check_handler, &s_sem,
 		      CLONE_FS | CLONE_FILES);
 
+	wait_for_completion(&mchchk_thread_active);
+
 	ctl_clear_bit(14, 25);	/* disable damage MCH */
 
 	ctl_set_bit(14, 26);	/* enable degradation MCH */
 	ctl_set_bit(14, 27);	/* enable system recovery MCH */
 
-	ctl_set_bit(14, 28);	/* enable channel report MCH */
 
-#ifdef CONFIG_MACHCK_WARNING
+#ifdef CONFIG_MACHCHK_WARNING
 	ctl_set_bit(14, 24);	/* enable warning MCH */
 #endif
 
@@ -127,6 +130,30 @@
 	return;
 }
 
+/*
+ * initialize the machine check handler really early to be able to
+ * catch all machine checks that happen during boot
+ */
+static int __init
+machine_check_init (void)
+{
+	s390_init_machine_check();
+	return 0;
+}
+arch_initcall(machine_check_init);
+
+/*
+ * Machine checks for the channel subsystem must be enabled
+ * after the channel subsystem is initialized
+ */
+static int __init
+machine_check_crw_init (void)
+{
+	ctl_set_bit(14, 28);	/* enable channel report MCH */
+	return 0;
+}
+device_initcall (machine_check_crw_init);
+
 static void
 s390_handle_damage(char *msg)
 {
@@ -244,6 +271,8 @@
 
 	DBG(KERN_NOTICE "mach_handler : ready\n");
 
+	complete(&mchchk_thread_active);
+
 	do {
 
 		DBG(KERN_NOTICE "mach_handler : waiting for wakeup\n");
@@ -314,7 +343,7 @@
 
 	} while (1);
 
-	return (0);
+	return 0;
 }
 
 /*
@@ -557,7 +586,7 @@
 
 	} while (ccode == 0);
 
-	return (count);
+	return count;
 }
 
 #ifdef CONFIG_MACHCHK_WARNING
@@ -587,6 +616,6 @@
 
 	DBG(KERN_DEBUG "post_warning : 1 warning machine check posted\n");
 
-	return (1);
+	return 1;
 }
 #endif
diff -Nru a/drivers/sbus/Makefile b/drivers/sbus/Makefile
--- a/drivers/sbus/Makefile	Mon Oct  7 11:25:39 2002
+++ b/drivers/sbus/Makefile	Mon Oct  7 11:25:39 2002
@@ -7,6 +7,5 @@
 endif
 
 obj-$(CONFIG_SBUSCHAR) += char/
-obj-$(CONFIG_SPARCAUDIO) += audio/
 
 include $(TOPDIR)/Rules.make
diff -Nru a/drivers/sbus/char/jsflash.c b/drivers/sbus/char/jsflash.c
--- a/drivers/sbus/char/jsflash.c	Mon Oct  7 11:25:35 2002
+++ b/drivers/sbus/char/jsflash.c	Mon Oct  7 11:25:35 2002
@@ -127,7 +127,7 @@
 
 /*
  */
-static struct gendisk jsfd_disk[JSF_MAX];
+static struct gendisk *jsfd_disk[JSF_MAX];
 
 /*
  * Let's pretend we may have several of these...
@@ -614,20 +614,30 @@
 	static spinlock_t lock = SPIN_LOCK_UNLOCKED;
 	struct jsflash *jsf;
 	struct jsfd_part *jdp;
+	int err;
 	int i;
 
 	if (jsf0.base == 0)
 		return -ENXIO;
 
+	err = -ENOMEM;
+	for (i = 0; i < JSF_MAX; i++) {
+		struct gendisk *disk = alloc_disk();
+		if (!disk)
+			goto out;
+		jsfd_disk[i] = disk;
+	}
+
 	if (register_blkdev(JSFD_MAJOR, "jsfd", &jsfd_fops)) {
 		printk("jsfd_init: unable to get major number %d\n",
 		    JSFD_MAJOR);
-		return -EIO;
+		err = -EIO;
+		goto out;
 	}
 
 	blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), jsfd_do_request, &lock);
 	for (i = 0; i < JSF_MAX; i++) {
-		struct gendisk *disk = jsfd_disk + i;
+		struct gendisk *disk = jsfd_disk[i];
 		if ((i & JSF_PART_MASK) >= JSF_NPART) continue;
 		jsf = &jsf0;	/* actually, &jsfv[i >> JSF_PART_BITS] */
 		jdp = &jsf->dv[i&JSF_PART_MASK];
@@ -644,6 +654,10 @@
 		set_device_ro(MKDEV(JSFD_MAJOR, i), 1);
 	}
 	return 0;
+out:
+	while (i--)
+		put_disk(jsfd_disk[i]);
+	return err;
 }
 
 MODULE_LICENSE("GPL");
@@ -664,7 +678,8 @@
 
 	for (i = 0; i < JSF_MAX; i++) {
 		if ((i & JSF_PART_MASK) >= JSF_NPART) continue;
-		del_gendisk(jsfd_disk + i);
+		del_gendisk(jsfd_disk[i]);
+		put_disk(jsfd_disk[i]);
 	}
 	if (jsf0.busy)
 		printk("jsf0: cleaning busy unit\n");
diff -Nru a/drivers/sbus/sbus.c b/drivers/sbus/sbus.c
--- a/drivers/sbus/sbus.c	Mon Oct  7 11:25:33 2002
+++ b/drivers/sbus/sbus.c	Mon Oct  7 11:25:33 2002
@@ -312,7 +312,7 @@
 		nd = prom_searchsiblings(topnd, "sbus");
 		if(nd == 0) {
 #ifdef CONFIG_PCI
-			if (!pcibios_present()) {	
+			if (!pci_present()) {
 				prom_printf("Neither SBUS nor PCI found.\n");
 				prom_halt();
 			} else {
diff -Nru a/drivers/scsi/53c7,8xx.c b/drivers/scsi/53c7,8xx.c
--- a/drivers/scsi/53c7,8xx.c	Mon Oct  7 11:25:25 2002
+++ b/drivers/scsi/53c7,8xx.c	Mon Oct  7 11:25:25 2002
@@ -1533,8 +1533,7 @@
     int i;
     int current_override;
     int count;			/* Number of boards detected */
-    unsigned char pci_bus, pci_device_fn;
-    static short pci_index=0;	/* Device index to PCI BIOS calls */
+    struct pci_dev *pdev = NULL;
 
     tpnt->proc_name = "ncr53c7xx";
 
@@ -1563,13 +1562,11 @@
 
     if (pci_present()) {
 	for (i = 0; i < NPCI_CHIP_IDS; ++i) 
-	    for (pci_index = 0;
-		!pcibios_find_device (PCI_VENDOR_ID_NCR, 
-		    pci_chip_ids[i].pci_device_id, pci_index, &pci_bus, 
-		    &pci_device_fn); 
-    		++pci_index)
+	    while ((pdev = pci_find_device (PCI_VENDOR_ID_NCR,
+					    pci_chip_ids[i].pci_device_id,
+					    pdev)))
 		if (!ncr_pci_init (tpnt, BOARD_GENERIC, pci_chip_ids[i].chip, 
-		    pci_bus, pci_device_fn, /* no options */ 0))
+		    pdev->bus->number, pdev->devfn, /* no options */ 0))
 		    ++count;
     }
     return count;
diff -Nru a/drivers/scsi/Config.help b/drivers/scsi/Config.help
--- a/drivers/scsi/Config.help	Mon Oct  7 11:25:35 2002
+++ b/drivers/scsi/Config.help	Mon Oct  7 11:25:35 2002
@@ -788,6 +788,16 @@
 
   The common answer here is N, but answering Y is safe.
 
+CONFIG_SCSI_NSP32
+  This is support for the Workbit NinjaSCSI-32Bi/UDE PCI/Cardbus
+  SCSI host adapter. Please read the SCSI-HOWTO, available from
+  <http://www.linuxdoc.org/docs.html#howto>.
+
+  If you want to compile this as a module ( = code which can be
+  inserted in and removed from the running kernel whenever you want),
+  say M here and read <file:Documentation/modules.txt>.  The module
+  will be called nsp32.o.
+
 CONFIG_SCSI_IBMMCA
   This is support for the IBM SCSI adapter found in many of the PS/2
   series computers.  These machines have an MCA bus, so you need to
diff -Nru a/drivers/scsi/Config.in b/drivers/scsi/Config.in
--- a/drivers/scsi/Config.in	Mon Oct  7 11:25:33 2002
+++ b/drivers/scsi/Config.in	Mon Oct  7 11:25:33 2002
@@ -51,6 +51,9 @@
 if [ "$CONFIG_EISA" = "y" ]; then
   dep_tristate 'Adaptec AHA1740 support' CONFIG_SCSI_AHA1740 $CONFIG_SCSI
 fi
+if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+   dep_tristate 'Adaptec AACRAID support (EXPERIMENTAL)' CONFIG_SCSI_AACRAID $CONFIG_SCSI $CONFIG_PCI
+fi
 source drivers/scsi/aic7xxx/Config.in
 if [ "$CONFIG_SCSI_AIC7XXX" != "y" ]; then
    dep_tristate 'Old Adaptec AIC7xxx support' CONFIG_SCSI_AIC7XXX_OLD $CONFIG_SCSI
@@ -210,6 +213,7 @@
 if [ "$CONFIG_X86" = "y" -a "$CONFIG_ISA" = "y" ]; then
    dep_tristate 'UltraStor SCSI support' CONFIG_SCSI_ULTRASTOR $CONFIG_SCSI
 fi
+dep_tristate 'Workbit NinjaSCSI-32Bi/UDE support' CONFIG_SCSI_NSP32 $CONFIG_SCSI
 #
 # Note - this is a very special 'host' adapter that simulates the presence of some disks.
 # It can come in very handy for troubleshooting.  Anyone else is welcome to use it - all
diff -Nru a/drivers/scsi/Makefile b/drivers/scsi/Makefile
--- a/drivers/scsi/Makefile	Mon Oct  7 11:25:28 2002
+++ b/drivers/scsi/Makefile	Mon Oct  7 11:25:28 2002
@@ -58,6 +58,7 @@
 obj-$(CONFIG_SCSI_AHA1542)	+= aha1542.o
 obj-$(CONFIG_SCSI_AHA1740)	+= aha1740.o
 obj-$(CONFIG_SCSI_AIC7XXX)	+= aic7xxx/
+obj-$(CONFIG_SCSI_AACRAID)	+= aacraid/
 obj-$(CONFIG_SCSI_AIC7XXX_OLD)	+= aic7xxx_old.o
 obj-$(CONFIG_SCSI_IPS)		+= ips.o
 obj-$(CONFIG_SCSI_FD_MCS)	+= fd_mcs.o
@@ -110,6 +111,7 @@
 obj-$(CONFIG_SCSI_FCAL)		+= fcal.o
 obj-$(CONFIG_SCSI_CPQFCTS)	+= cpqfc.o
 obj-$(CONFIG_SCSI_LASI700)	+= lasi700.o 53c700.o
+obj-$(CONFIG_SCSI_NSP32)	+= nsp32.o
 
 obj-$(CONFIG_ARCH_ACORN)	+= ../acorn/scsi/
 
diff -Nru a/drivers/scsi/NCR5380.c b/drivers/scsi/NCR5380.c
--- a/drivers/scsi/NCR5380.c	Mon Oct  7 11:25:27 2002
+++ b/drivers/scsi/NCR5380.c	Mon Oct  7 11:25:27 2002
@@ -396,24 +396,21 @@
  *
  *	Print the SCSI bus signals for debugging purposes
  *
- *	Locks: none
+ *	Locks: caller holds hostdata lock (not essential)
  */
 
 static void NCR5380_print(struct Scsi_Host *instance)
 {
 	NCR5380_local_declare();
-	unsigned long flags;
 	unsigned char status, data, basr, mr, icr, i;
 	NCR5380_setup(instance);
-	/* FIXME - this needs proper locking */
-	save_flags(flags);
-	cli();
+
 	data = NCR5380_read(CURRENT_SCSI_DATA_REG);
 	status = NCR5380_read(STATUS_REG);
 	mr = NCR5380_read(MODE_REG);
 	icr = NCR5380_read(INITIATOR_COMMAND_REG);
 	basr = NCR5380_read(BUS_AND_STATUS_REG);
-	restore_flags(flags);
+
 	printk("STATUS_REG: %02x ", status);
 	for (i = 0; signals[i].mask; ++i)
 		if (status & signals[i].mask)
@@ -500,7 +497,8 @@
  *      request.  main_running is checked/set here (in an inline function)
  *      rather than in NCR5380_main itself to reduce the chances of stack
  *      overflow.
- *
+ * FIXME: NCR5380_main should probably be run with schedule_task or be a
+ * thread.
  */
 
 static __inline__ void run_main(void)
@@ -533,6 +531,7 @@
 #endif
 
 static struct Scsi_Host *expires_first = NULL;
+static spinlock_t timer_lock;	/* Guards expires list */
 
 /* 
  * Function : int should_disconnect (unsigned char cmd)
@@ -578,17 +577,21 @@
 
 /*
  * Assumes instance->time_expires has been set in higher level code.
+ * We should move to a timer per host
  *
- * Locks: Caller must hold io_request_lock
+ * Locks: Takes the timer queue lock
  */
 
 static int NCR5380_set_timer(struct Scsi_Host *instance)
 {
 	struct Scsi_Host *tmp, **prev;
+	unsigned long flags;
 
 	if (((struct NCR5380_hostdata *) (instance->hostdata))->next_timer) {
 		return -1;
 	}
+	
+	spin_lock_irqsave(&timer_lock, flags);
 	for (prev = &expires_first, tmp = expires_first; tmp; prev = &(((struct NCR5380_hostdata *) tmp->hostdata)->next_timer), tmp = ((struct NCR5380_hostdata *) tmp->hostdata)->next_timer)
 		if (((struct NCR5380_hostdata *) instance->hostdata)->time_expires < ((struct NCR5380_hostdata *) tmp->hostdata)->time_expires)
 			break;
@@ -597,6 +600,8 @@
 	*prev = instance;
 
 	mod_timer(&usleep_timer, ((struct NCR5380_hostdata *) expires_first->hostdata)->time_expires);
+	
+	spin_unlock_irqrestore(&timer_lock, flags);
 	return 0;
 }
 
@@ -610,15 +615,15 @@
  *
  *	Doing something about unwanted reentrancy here might be useful 
  *
- *	Locks: disables irqs, takes and frees io_request_lock
+ *	Locks: disables irqs, takes and frees the timer lock
  */
  
 static void NCR5380_timer_fn(unsigned long unused)
 {
 	struct Scsi_Host *instance;
+	unsigned long flags;
 
-	spin_lock_irq(&io_request_lock);
-	
+	spin_lock_irqsave(&timer_lock, flags);
 	for (; expires_first && time_before_eq(((struct NCR5380_hostdata *) expires_first->hostdata)->time_expires, jiffies);) {
 		instance = ((struct NCR5380_hostdata *) expires_first->hostdata)->next_timer;
 		((struct NCR5380_hostdata *) expires_first->hostdata)->next_timer = NULL;
@@ -631,8 +636,10 @@
 		usleep_timer.expires = ((struct NCR5380_hostdata *) expires_first->hostdata)->time_expires;
 		add_timer(&usleep_timer);
 	}
+
+	spin_unlock_irqrestore(&timer_lock, flags);
+	
 	run_main();
-	spin_unlock_irq(&io_request_lock);
 }
 
 /**
@@ -648,6 +655,7 @@
 		dprintk(NDEBUG_INIT, ("scsi : NCR5380_all_init()\n"));
 		done = 1;
 		init_timer(&usleep_timer);
+		spin_lock_init(&timer_lock);
 		usleep_timer.function = NCR5380_timer_fn;
 	}
 }
@@ -777,7 +785,7 @@
  *	Print commands in the various queues, called from NCR5380_abort 
  *	and NCR5380_debug to aid debugging.
  *
- *	Locks: called functions disable irqs, missing queue lock in proc call
+ *	Locks: called functions disable irqs
  */
 
 static void NCR5380_print_status(struct Scsi_Host *instance)
@@ -872,7 +880,7 @@
 #ifdef PAS16_PUBLIC_RELEASE
 	SPRINTF("Highwater I/O busy_spin_counts -- write: %d  read: %d\n", pas_wmaxi, pas_maxi);
 #endif
-	spin_lock_irq(&io_request_lock);
+	spin_lock_irq(instance->host_lock);
 	SPRINTF("NCR5380 : coroutine is%s running.\n", main_running ? "" : "n't");
 	if (!hostdata->connected)
 		SPRINTF("scsi%d: no currently connected command\n", instance->host_no);
@@ -886,7 +894,7 @@
 	for (ptr = (Scsi_Cmnd *) hostdata->disconnected_queue; ptr; ptr = (Scsi_Cmnd *) ptr->host_scribble)
 		pos = lprint_Scsi_Cmnd(ptr, pos, buffer, length);
 
-	spin_unlock_irq(&io_request_lock);
+	spin_unlock_irq(instance->host_lock);
 	
 	*start = buffer;
 	if (pos - buffer < offset)
@@ -1131,6 +1139,8 @@
 	dprintk(NDEBUG_QUEUES, ("scsi%d : command added to %s of queue\n", instance->host_no, (cmd->cmnd[0] == REQUEST_SENSE) ? "head" : "tail"));
 
 	/* Run the coroutine if it isn't already running. */
+	
+	/* FIMXE: drop any locks here */
 	run_main();
 	return 0;
 }
@@ -1276,20 +1286,22 @@
 }
 
 #ifndef DONT_USE_INTR
-#include <linux/blk.h>
-#include <linux/spinlock.h>
 
 /**
  * 	NCR5380_intr	-	generic NCR5380 irq handler
+ *	@irq: interrupt number
+ *	@dev_id: device info
+ *	@regs: registers (unused)
  *
  *	Handle interrupts, reestablishing I_T_L or I_T_L_Q nexuses
  *      from the disconnected queue, and restarting NCR5380_main() 
  *      as required.
  *
- *	Locks: caller must hold the io_request lock.
+ *	Locks: takes the needed instance locks
  */
 
-static void NCR5380_intr(int irq, void *dev_id, struct pt_regs *regs) {
+static void NCR5380_intr(int irq, void *dev_id, struct pt_regs *regs) 
+{
 	NCR5380_local_declare();
 	struct Scsi_Host *instance;
 	int done;
@@ -1299,9 +1311,12 @@
 
 	do {
 		done = 1;
+		/* The instance list is constant while the driver is
+		   loaded */
 		for (instance = first_instance; instance && (instance->hostt == the_template); instance = instance->next)
+		{
 			if (instance->irq == irq) {
-
+				spin_lock_irq(instance->host_lock);
 				/* Look for pending interrupts */
 				NCR5380_setup(instance);
 				basr = NCR5380_read(BUS_AND_STATUS_REG);
@@ -1343,15 +1358,19 @@
 							{
 								unsigned long timeout = jiffies + NCR_TIMEOUT;
 
-								spin_unlock_irq(&io_request_lock);
-								while (NCR5380_read(BUS_AND_STATUS_REG) & BASR_ACK && time_before(jiffies, timeout));
-								spin_lock_irq(&io_request_lock);
+								spin_unlock_irq(instance->host_lock);
+								/* FIXME: prove timer is always running here! */
+								while (NCR5380_read(BUS_AND_STATUS_REG) & BASR_ACK && time_before(jiffies, timeout))
+									cpu_relax();
+								spin_lock_irq(instance->host_lock);
 
 								if (time_after_eq(jiffies, timeout))
 									printk("scsi%d: timeout at NCR5380.c:%d\n", host->host_no, __LINE__);
 							}
+							
 #else /* NCR_TIMEOUT */
-							while (NCR5380_read(BUS_AND_STATUS_REG) & BASR_ACK);
+							while (NCR5380_read(BUS_AND_STATUS_REG) & BASR_ACK)
+								cpu_relax();
 #endif
 
 							NCR5380_write(MODE_REG, MR_BASE);
@@ -1363,33 +1382,15 @@
 #endif
 					}
 				}	/* if BASR_IRQ */
+				spin_unlock_irq(instance->host_lock);
 				if (!done)
 					run_main();
 			}	/* if (instance->irq == irq) */
+		}
 	} while (!done);
 }
 
-/**
- *	do_NCR5380_intr
- *	@irq: interrupt number
- *	@dev_id: device info
- *	@regs: registers (unused)
- *
- *	Takes the io_request_lock and invokes the generic NCR5380 interrupt
- *	handler code
- *
- *	Locks: takes and releases the io_request lock
- */
-
-static void do_NCR5380_intr(int irq, void *dev_id, struct pt_regs *regs) {
-	unsigned long flags;
-
-	 spin_lock_irqsave(&io_request_lock, flags);
-	 NCR5380_intr(irq, dev_id, regs);
-	 spin_unlock_irqrestore(&io_request_lock, flags);
-}
-#endif
-
+#endif 
 
 /**
  *	collect_stats		-	collect stats on a scsi command
@@ -1450,7 +1451,7 @@
  *      If failed (no target) : cmd->scsi_done() will be called, and the 
  *              cmd->result host byte set to DID_BAD_TARGET.
  *
- *	Locks: caller holds io_request_lock
+ *	Locks: caller holds hostdata lock
  */
  
 static int NCR5380_select(struct Scsi_Host *instance, Scsi_Cmnd * cmd, int tag) 
@@ -1493,12 +1494,13 @@
 	{
 		unsigned long timeout = jiffies + 2 * NCR_TIMEOUT;
 
-		spin_unlock_irq(&io_request_lock);
+		spin_unlock_irq(instance->host_lock);
 
 		while (!(NCR5380_read(INITIATOR_COMMAND_REG) & ICR_ARBITRATION_PROGRESS)
-		       && time_before(jiffies, timeout));
+		       && time_before(jiffies, timeout))
+		       	cpu_relax();
 
-		spin_lock_irq(&io_request_lock);
+		spin_lock_irq(instance->host_lock);
 
 		if (time_after_eq(jiffies, timeout)) {
 			printk("scsi: arbitration timeout at %d\n", __LINE__);
@@ -1624,6 +1626,7 @@
 	   we poll only once ech clock tick */
 	value = NCR5380_read(STATUS_REG) & (SR_BSY | SR_IO);
 
+	/* FIXME HZ=100 assumption ? */
 	if (!value && (hostdata->select_time < 25)) {
 		/* RvC: we still must wait for a device response */
 		hostdata->select_time++;	/* after 25 ticks the device has failed */
@@ -1692,9 +1695,10 @@
 	{
 		unsigned long timeout = jiffies + NCR_TIMEOUT;
 
-		spin_unlock_irq(&io_request_lock);
-		while (!(NCR5380_read(STATUS_REG) & SR_REQ) && time_before(jiffies, timeout));
-		spin_lock_irq(&io_request_lock);
+		spin_unlock_irq(instance->host_lock);
+		while (!(NCR5380_read(STATUS_REG) & SR_REQ) && time_before(jiffies, timeout))
+			cpu_relax();
+		spin_lock_irq(instance->host_lock);
 
 		if (time_after_eq(jiffies, timeout)) {
 			printk("scsi%d: timeout at NCR5380.c:%d\n", instance->host_no, __LINE__);
@@ -1703,7 +1707,8 @@
 		}
 	}
 #else				/* NCR_TIMEOUT */
-	while (!(NCR5380_read(STATUS_REG) & SR_REQ));
+	while (!(NCR5380_read(STATUS_REG) & SR_REQ))
+		cpu_relax();
 #endif				/* def NCR_TIMEOUT */
 
 	dprintk(NDEBUG_SELECTION, ("scsi%d : target %d selected, going into MESSAGE OUT phase.\n", instance->host_no, cmd->target));
@@ -1886,7 +1891,8 @@
  *	Issue a reset sequence to the NCR5380 and try and get the bus
  *	back into sane shape.
  *
- *	Locks: caller holds io_request lock
+ *	Locks: caller holds queue lock
+ *	FIXME: sort this out and get new_eh running
  */
  
 static void do_reset(struct Scsi_Host *host) {
@@ -1907,7 +1913,8 @@
  * 
  * Returns : 0 on success, -1 on failure.
  *
- * Locks: io_request lock held by caller
+ * Locks: queue lock held by caller
+ *	FIXME: sort this out and get new_eh running
  */
 
 static int do_abort(struct Scsi_Host *host) {
@@ -1930,7 +1937,8 @@
 	 * the target sees, so we just handshake.
 	 */
 
-	while (!(tmp = NCR5380_read(STATUS_REG)) & SR_REQ);
+	while (!(tmp = NCR5380_read(STATUS_REG)) & SR_REQ)
+		cpu_relax();
 
 	NCR5380_write(TARGET_COMMAND_REG, PHASE_SR_TO_TCR(tmp));
 
@@ -2019,7 +2027,7 @@
 	 */
 
 #if defined(PSEUDO_DMA) && defined(UNSAFE)
-	spin_unlock_irq(&io_request_lock);
+	spin_unlock_irq(instance->host_lock);
 #endif
 	/* KLL May need eop and parity in 53c400 */
 	if (hostdata->flags & FLAG_NCR53C400)
@@ -2228,7 +2236,7 @@
 	*count = 0;
 	*phase = NCR5380_read(STATUS_REG) & PHASE_MASK;
 #if defined(PSEUDO_DMA) && defined(UNSAFE)
-	spin_lock_irq(&io_request_lock);
+	spin_lock_irq(instance->host_lock);
 #endif				/* defined(REAL_DMA_POLL) */
 	return foo;
 #endif				/* def REAL_DMA */
@@ -2707,9 +2715,11 @@
 
 	/*
 	 * Wait for target to go into MSGIN.
+	 * FIXME: timeout needed
 	 */
 
-	while (!(NCR5380_read(STATUS_REG) & SR_REQ));
+	while (!(NCR5380_read(STATUS_REG) & SR_REQ))
+		cpu_relax();
 
 	len = 1;
 	data = msg;
@@ -2843,12 +2853,12 @@
 	struct NCR5380_hostdata *hostdata = (struct NCR5380_hostdata *) instance->hostdata;
 	Scsi_Cmnd *tmp, **prev;
 
-	printk("scsi%d : aborting command\n", instance->host_no);
+	printk(KERN_WARNING "scsi%d : aborting command\n", instance->host_no);
 	print_Scsi_Cmnd(cmd);
 
 	NCR5380_print_status(instance);
 
-	printk("scsi%d : aborting command\n", instance->host_no);
+	printk(KERN_WARNING "scsi%d : aborting command\n", instance->host_no);
 	print_Scsi_Cmnd(cmd);
 
 	NCR5380_print_status(instance);
@@ -2893,6 +2903,8 @@
  * Case 2 : If the command hasn't been issued yet, we simply remove it 
  *          from the issue queue.
  */
+ 
+ 	/* FIXME: check - I think we need the hostdata lock here */
 	/* KLL */
 	dprintk(NDEBUG_ABORT, ("scsi%d : abort going into loop.\n", instance->host_no));
 	for (prev = (Scsi_Cmnd **) & (hostdata->issue_queue), tmp = (Scsi_Cmnd *) hostdata->issue_queue; tmp; prev = (Scsi_Cmnd **) & (tmp->host_scribble), tmp = (Scsi_Cmnd *) tmp->host_scribble)
@@ -2903,12 +2915,12 @@
 			tmp->result = DID_ABORT << 16;
 			dprintk(NDEBUG_ABORT, ("scsi%d : abort removed command from issue queue.\n", instance->host_no));
 			tmp->done(tmp);
-			return SCSI_ABORT_SUCCESS;
+			return SUCCESS;
 		}
 #if (NDEBUG  & NDEBUG_ABORT)
 	/* KLL */
 		else if (prev == tmp)
-			printk("scsi%d : LOOP\n", instance->host_no);
+			printk(KERN_ERR "scsi%d : LOOP\n", instance->host_no);
 #endif
 
 /* 
@@ -2924,7 +2936,7 @@
 
 	if (hostdata->connected) {
 		dprintk(NDEBUG_ABORT, ("scsi%d : abort failed, command connected.\n", instance->host_no));
-		return SCSI_ABORT_NOT_RUNNING;
+		return FAILED;
 	}
 /*
  * Case 4: If the command is currently disconnected from the bus, and 
@@ -2956,7 +2968,7 @@
 			dprintk(NDEBUG_ABORT, ("scsi%d : aborting disconnected command.\n", instance->host_no));
 
 			if (NCR5380_select(instance, cmd, (int) cmd->tag))
-				return SCSI_ABORT_BUSY;
+				return FAILED;
 			dprintk(NDEBUG_ABORT, ("scsi%d : nexus reestablished.\n", instance->host_no));
 
 			do_abort(instance);
@@ -2968,7 +2980,7 @@
 					tmp->host_scribble = NULL;
 					tmp->result = DID_ABORT << 16;
 					tmp->done(tmp);
-					return SCSI_ABORT_SUCCESS;
+					return SUCCESS;
 				}
 		}
 /*
@@ -2980,30 +2992,68 @@
  * so we won't panic, but we will notify the user in case something really
  * broke.
  */
-	printk("scsi%d : warning : SCSI command probably completed successfully\n" "         before abortion\n", instance->host_no);
-	return SCSI_ABORT_NOT_RUNNING;
+	printk(KERN_WARNING "scsi%d : warning : SCSI command probably completed successfully\n" "         before abortion\n", instance->host_no);
+	return FAILED;
 }
 
 
 /* 
- * Function : int NCR5380_reset (Scsi_Cmnd *cmd, unsigned int reset_flags)
+ * Function : int NCR5380_bus_reset (Scsi_Cmnd *cmd)
  * 
  * Purpose : reset the SCSI bus.
  *
- * Returns : SCSI_RESET_WAKEUP
+ * Returns : SUCCESS
  *
  * Locks: io_request_lock held by caller
  */
 
-#ifndef NCR5380_reset
+#ifndef NCR5380_bus_reset
 static
 #endif
-int NCR5380_reset(Scsi_Cmnd * cmd, unsigned int dummy) {
+int NCR5380_bus_reset(Scsi_Cmnd * cmd) {
 	NCR5380_local_declare();
 	NCR5380_setup(cmd->host);
 
 	NCR5380_print_status(cmd->host);
 	do_reset(cmd->host);
 
-	return SCSI_RESET_WAKEUP;
+	return SUCCESS;
+}
+
+/* 
+ * Function : int NCR5380_device_reset (Scsi_Cmnd *cmd)
+ * 
+ * Purpose : reset a SCSI device
+ *
+ * Returns : FAILED
+ *
+ * Locks: io_request_lock held by caller
+ */
+
+#ifndef NCR5380_device_reset
+static
+#endif
+int NCR5380_device_reset(Scsi_Cmnd * cmd) {
+	NCR5380_local_declare();
+	NCR5380_setup(cmd->host);
+	return FAILED;
+}
+
+/* 
+ * Function : int NCR5380_host_reset (Scsi_Cmnd *cmd)
+ * 
+ * Purpose : reset a SCSI device
+ *
+ * Returns : FAILED
+ *
+ * Locks: io_request_lock held by caller
+ */
+
+#ifndef NCR5380_host_reset
+static
+#endif
+int NCR5380_host_reset(Scsi_Cmnd * cmd) {
+	NCR5380_local_declare();
+	NCR5380_setup(cmd->host);
+	return FAILED;
 }
diff -Nru a/drivers/scsi/NCR5380.h b/drivers/scsi/NCR5380.h
--- a/drivers/scsi/NCR5380.h	Mon Oct  7 11:25:35 2002
+++ b/drivers/scsi/NCR5380.h	Mon Oct  7 11:25:35 2002
@@ -304,10 +304,18 @@
 static
 #endif
 int NCR5380_abort(Scsi_Cmnd * cmd);
-#ifndef NCR5380_reset
+#ifndef NCR5380_bus_reset
 static
 #endif
-int NCR5380_reset(Scsi_Cmnd * cmd, unsigned int reset_flags);
+int NCR5380_bus_reset(Scsi_Cmnd * cmd);
+#ifndef NCR5380_host_reset
+static
+#endif
+int NCR5380_host_reset(Scsi_Cmnd * cmd);
+#ifndef NCR5380_device_reset
+static
+#endif
+int NCR5380_device_reset(Scsi_Cmnd * cmd);
 #ifndef NCR5380_queue_command
 static
 #endif
diff -Nru a/drivers/scsi/NCR53c406a.c b/drivers/scsi/NCR53c406a.c
--- a/drivers/scsi/NCR53c406a.c	Mon Oct  7 11:25:35 2002
+++ b/drivers/scsi/NCR53c406a.c	Mon Oct  7 11:25:35 2002
@@ -459,7 +459,7 @@
 
 int  __init 
 NCR53c406a_detect(Scsi_Host_Template * tpnt){
-    struct Scsi_Host *shpnt;
+    struct Scsi_Host *shpnt = NULL;
 #ifndef PORT_BASE
     int i;
 #endif
@@ -535,11 +535,20 @@
 #endif
     
     DEB(printk("NCR53c406a: using port_base %x\n", port_base));
+
+    tpnt->present = 1;
+    tpnt->proc_name = "NCR53c406a";
+    
+    shpnt = scsi_register(tpnt, 0);
+    if (!shpnt) {
+            printk("NCR53c406a: Unable to register host, giving up.\n");
+            goto err_release;
+    }
     
     if(irq_level > 0) {
         if(request_irq(irq_level, do_NCR53c406a_intr, 0, "NCR53c406a", shpnt)){
             printk("NCR53c406a: unable to allocate IRQ %d\n", irq_level);
-            goto err_release;
+            goto err_free_scsi;
         }
         tpnt->can_queue = 1;
         DEB(printk("NCR53c406a: allocated IRQ %d\n", irq_level));
@@ -549,32 +558,24 @@
         DEB(printk("NCR53c406a: No interrupts detected\n"));
 #if USE_DMA
         printk("NCR53c406a: No interrupts found and DMA mode defined. Giving up.\n");
-        goto err_release;
+        goto err_free_scsi;
 #endif /* USE_DMA */
     }
     else {
         DEB(printk("NCR53c406a: Shouldn't get here!\n"));
-        goto err_free_irq;
+        goto err_free_scsi;
     }
     
 #if USE_DMA
     dma_chan = DMA_CHAN;
     if(request_dma(dma_chan, "NCR53c406a") != 0){
         printk("NCR53c406a: unable to allocate DMA channel %d\n", dma_chan);
-        goto err_release;
+        goto err_free_irq;
     }
     
     DEB(printk("Allocated DMA channel %d\n", dma_chan));
 #endif /* USE_DMA */
     
-    tpnt->present = 1;
-    tpnt->proc_name = "NCR53c406a";
-    
-    shpnt = scsi_register(tpnt, 0);
-    if (!shpnt) {
-            printk("NCR53c406a: Unable to register host, giving up.\n");
-            goto err_free_dma;
-    }
     shpnt->irq = irq_level;
     shpnt->io_port = port_base;
     shpnt->n_io_port = 0x10;
@@ -592,13 +593,13 @@
     
     return (tpnt->present);
 
-
- err_free_dma:
 #if USE_DMA
-    free_dma(dma_chan);
-#endif
  err_free_irq:
-    free_irq(irq_level, do_NCR53c406a_intr);
+    if(irq_level)
+    	free_irq(irq_level, shpnt);
+#endif    	
+ err_free_scsi:
+    scsi_unregister(shpnt);
  err_release:
     release_region(port_base, 0x10);
     return 0;
@@ -896,7 +897,7 @@
                 sgcount = current_SC->use_sg;
                 sglist = current_SC->request_buffer;
                 while( sgcount-- ) {
-                    NCR53c406a_pio_write(sglist->address, sglist->length);
+                    NCR53c406a_pio_write(page_address(sglist->page) + sglist->offset, sglist->length);
                     sglist++;
                 }
             }
@@ -925,7 +926,7 @@
                 sgcount = current_SC->use_sg;
                 sglist = current_SC->request_buffer;
                 while( sgcount-- ) {
-                    NCR53c406a_pio_read(sglist->address, sglist->length);
+                    NCR53c406a_pio_read(page_address(sglist->page) + sglist->offset, sglist->length);
                     sglist++;
                 }
             }
diff -Nru a/drivers/scsi/aacraid/Makefile b/drivers/scsi/aacraid/Makefile
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/scsi/aacraid/Makefile	Mon Oct  7 11:26:06 2002
@@ -0,0 +1,10 @@
+
+EXTRA_CFLAGS	+= -I$(TOPDIR)/drivers/scsi
+
+O_TARGET	:= aacraid.o
+obj-m		:= $(O_TARGET)
+
+obj-y		:= linit.o aachba.o commctrl.o comminit.o commsup.o \
+		   dpcsup.o rx.o sa.o
+
+include $(TOPDIR)/Rules.make
diff -Nru a/drivers/scsi/aacraid/README b/drivers/scsi/aacraid/README
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/scsi/aacraid/README	Mon Oct  7 11:25:40 2002
@@ -0,0 +1,42 @@
+AACRAID Driver for Linux (take two)
+
+Introduction
+-------------------------
+The aacraid driver adds support for Adaptec (http://www.adaptec.com)
+RAID controllers. This is a major rewrite from the original 
+Adaptec supplied driver. It has signficantly cleaned up both the code
+and the running binary size (the module is less than half the size of
+the original).
+
+Supported Cards/Chipsets
+-------------------------
+	Dell Computer Corporation PERC 2 Quad Channel
+	Dell Computer Corporation PERC 2/Si
+	Dell Computer Corporation PERC 3/Si
+	Dell Computer Corporation PERC 3/Di
+	HP NetRAID-4M
+	ADAPTEC 2120S
+	ADAPTEC 2200S
+	ADAPTEC 5400S
+
+People
+-------------------------
+Alan Cox <alan@redhat.com>
+Christoph Hellwig <hch@infradead.org>	(small cleanups/fixes)
+Matt Domsch <matt_domsch@dell.com>	(revision ioctl, adapter messages)
+Deanna Bonds <deanna_bonds@adaptec.com> (non-DASD support, PAE fibs and 64 bit, added new adaptec controllers
+					 added new ioctls, changed scsi interface to use new error handler,
+					 increased the number of fibs and outstanding commands to a container)
+
+Original Driver
+-------------------------
+Adaptec Unix OEM Product Group
+
+Mailing List
+-------------------------
+None currently. Also note this is very different to Brian's original driver
+so don't expect him to support it.
+Adaptec does support this driver.  Contact either tech support or deanna bonds.
+
+Original by Brian Boerner February 2001
+Rewritten by Alan Cox, November 2001
diff -Nru a/drivers/scsi/aacraid/TODO b/drivers/scsi/aacraid/TODO
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/scsi/aacraid/TODO	Mon Oct  7 11:25:40 2002
@@ -0,0 +1,4 @@
+o	Testing
+o	More testing
+o	Feature request: display the firmware/bios/etc revisions in the
+	/proc info
diff -Nru a/drivers/scsi/aacraid/aachba.c b/drivers/scsi/aacraid/aachba.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/scsi/aacraid/aachba.c	Mon Oct  7 11:26:06 2002
@@ -0,0 +1,1661 @@
+/*
+ *	Adaptec AAC series RAID controller driver
+ *	(c) Copyright 2001 Red Hat Inc.	<alan@redhat.com>
+ *
+ * based on the old aacraid driver that is..
+ * Adaptec aacraid device driver for Linux.
+ * Copyright (c) 2000 Adaptec, Inc. (aacraid@adaptec.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, 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; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/pci.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/completion.h>
+#include <asm/semaphore.h>
+#include <asm/uaccess.h>
+#define MAJOR_NR SCSI_DISK0_MAJOR	/* For DEVICE_NR() */
+#include <linux/blk.h>
+#include "scsi.h"
+#include "hosts.h"
+#include "sd.h"
+
+#include "aacraid.h"
+
+/* FIXME: We share this with sd.c - wants putting in one spot only */
+#define DEVICE_NR(device) (((major(device) & SD_MAJOR_MASK) << (8 - 4)) + (minor(device) >> 4))
+
+/*	SCSI Commands */
+/*	TODO:  dmb - use the ones defined in include/scsi/scsi.h */
+
+#define	SS_TEST			0x00	/* Test unit ready */
+#define SS_REZERO		0x01	/* Rezero unit */
+#define	SS_REQSEN		0x03	/* Request Sense */
+#define SS_REASGN		0x07	/* Reassign blocks */
+#define	SS_READ			0x08	/* Read 6   */
+#define	SS_WRITE		0x0A	/* Write 6  */
+#define	SS_INQUIR		0x12	/* inquiry */
+#define	SS_ST_SP		0x1B	/* Start/Stop unit */
+#define	SS_LOCK			0x1E	/* prevent/allow medium removal */
+#define SS_RESERV		0x16	/* Reserve */
+#define SS_RELES		0x17	/* Release */
+#define SS_MODESEN		0x1A	/* Mode Sense 6 */
+#define	SS_RDCAP		0x25	/* Read Capacity */
+#define	SM_READ			0x28	/* Read 10  */
+#define	SM_WRITE		0x2A	/* Write 10 */
+#define SS_SEEK			0x2B	/* Seek */
+
+/* values for inqd_pdt: Peripheral device type in plain English */
+#define	INQD_PDT_DA	0x00		/* Direct-access (DISK) device */
+#define	INQD_PDT_PROC	0x03		/* Processor device */
+#define	INQD_PDT_CHNGR	0x08		/* Changer (jukebox, scsi2) */
+#define	INQD_PDT_COMM	0x09		/* Communication device (scsi2) */
+#define	INQD_PDT_NOLUN2 0x1f		/* Unknown Device (scsi2) */
+#define	INQD_PDT_NOLUN	0x7f		/* Logical Unit Not Present */
+
+#define	INQD_PDT_DMASK	0x1F		/* Peripheral Device Type Mask */
+#define	INQD_PDT_QMASK	0xE0		/* Peripheral Device Qualifer Mask */
+
+#define	TARGET_LUN_TO_CONTAINER(target, lun)	(target)
+#define CONTAINER_TO_TARGET(cont)		((cont))
+#define CONTAINER_TO_LUN(cont)			(0)
+
+#define MAX_FIB_DATA (sizeof(struct hw_fib) - sizeof(FIB_HEADER))
+
+#define MAX_DRIVER_SG_SEGMENT_COUNT 17
+
+/*
+ *	Sense keys
+ */
+#define SENKEY_NO_SENSE      			0x00	
+#define SENKEY_UNDEFINED     			0x01	
+#define SENKEY_NOT_READY     			0x02	
+#define SENKEY_MEDIUM_ERR    			0x03	
+#define SENKEY_HW_ERR        			0x04	
+#define SENKEY_ILLEGAL       			0x05	
+#define SENKEY_ATTENTION     			0x06	
+#define SENKEY_PROTECTED     			0x07	
+#define SENKEY_BLANK         			0x08	
+#define SENKEY_V_UNIQUE      			0x09	
+#define SENKEY_CPY_ABORT     			0x0A	
+#define SENKEY_ABORT         			0x0B	
+#define SENKEY_EQUAL         			0x0C	
+#define SENKEY_VOL_OVERFLOW  			0x0D	
+#define SENKEY_MISCOMP       			0x0E	
+#define SENKEY_RESERVED      			0x0F	
+
+/*
+ *	Sense codes
+ */
+ 
+#define SENCODE_NO_SENSE                        0x00
+#define SENCODE_END_OF_DATA                     0x00
+#define SENCODE_BECOMING_READY                  0x04
+#define SENCODE_INIT_CMD_REQUIRED               0x04
+#define SENCODE_PARAM_LIST_LENGTH_ERROR         0x1A
+#define SENCODE_INVALID_COMMAND                 0x20
+#define SENCODE_LBA_OUT_OF_RANGE                0x21
+#define SENCODE_INVALID_CDB_FIELD               0x24
+#define SENCODE_LUN_NOT_SUPPORTED               0x25
+#define SENCODE_INVALID_PARAM_FIELD             0x26
+#define SENCODE_PARAM_NOT_SUPPORTED             0x26
+#define SENCODE_PARAM_VALUE_INVALID             0x26
+#define SENCODE_RESET_OCCURRED                  0x29
+#define SENCODE_LUN_NOT_SELF_CONFIGURED_YET     0x3E
+#define SENCODE_INQUIRY_DATA_CHANGED            0x3F
+#define SENCODE_SAVING_PARAMS_NOT_SUPPORTED     0x39
+#define SENCODE_DIAGNOSTIC_FAILURE              0x40
+#define SENCODE_INTERNAL_TARGET_FAILURE         0x44
+#define SENCODE_INVALID_MESSAGE_ERROR           0x49
+#define SENCODE_LUN_FAILED_SELF_CONFIG          0x4c
+#define SENCODE_OVERLAPPED_COMMAND              0x4E
+
+/*
+ *	Additional sense codes
+ */
+ 
+#define ASENCODE_NO_SENSE                       0x00
+#define ASENCODE_END_OF_DATA                    0x05
+#define ASENCODE_BECOMING_READY                 0x01
+#define ASENCODE_INIT_CMD_REQUIRED              0x02
+#define ASENCODE_PARAM_LIST_LENGTH_ERROR        0x00
+#define ASENCODE_INVALID_COMMAND                0x00
+#define ASENCODE_LBA_OUT_OF_RANGE               0x00
+#define ASENCODE_INVALID_CDB_FIELD              0x00
+#define ASENCODE_LUN_NOT_SUPPORTED              0x00
+#define ASENCODE_INVALID_PARAM_FIELD            0x00
+#define ASENCODE_PARAM_NOT_SUPPORTED            0x01
+#define ASENCODE_PARAM_VALUE_INVALID            0x02
+#define ASENCODE_RESET_OCCURRED                 0x00
+#define ASENCODE_LUN_NOT_SELF_CONFIGURED_YET    0x00
+#define ASENCODE_INQUIRY_DATA_CHANGED           0x03
+#define ASENCODE_SAVING_PARAMS_NOT_SUPPORTED    0x00
+#define ASENCODE_DIAGNOSTIC_FAILURE             0x80
+#define ASENCODE_INTERNAL_TARGET_FAILURE        0x00
+#define ASENCODE_INVALID_MESSAGE_ERROR          0x00
+#define ASENCODE_LUN_FAILED_SELF_CONFIG         0x00
+#define ASENCODE_OVERLAPPED_COMMAND             0x00
+
+#define BYTE0(x) (unsigned char)(x)
+#define BYTE1(x) (unsigned char)((x) >> 8)
+#define BYTE2(x) (unsigned char)((x) >> 16)
+#define BYTE3(x) (unsigned char)((x) >> 24)
+
+/*------------------------------------------------------------------------------
+ *              S T R U C T S / T Y P E D E F S
+ *----------------------------------------------------------------------------*/
+/* SCSI inquiry data */
+struct inquiry_data {
+	u8 inqd_pdt;		/* Peripheral qualifier | Peripheral Device Type  */
+	u8 inqd_dtq;		/* RMB | Device Type Qualifier  */
+	u8 inqd_ver;		/* ISO version | ECMA version | ANSI-approved version */
+	u8 inqd_rdf;		/* AENC | TrmIOP | Response data format */
+	u8 inqd_len;		/* Additional length (n-4) */
+	u8 inqd_pad1[2];	/* Reserved - must be zero */
+	u8 inqd_pad2;		/* RelAdr | WBus32 | WBus16 |  Sync  | Linked |Reserved| CmdQue | SftRe */
+	u8 inqd_vid[8];		/* Vendor ID */
+	u8 inqd_pid[16];	/* Product ID */
+	u8 inqd_prl[4];		/* Product Revision Level */
+};
+
+struct sense_data {
+	u8 error_code;		/* 70h (current errors), 71h(deferred errors) */
+	u8 valid:1;		/* A valid bit of one indicates that the information  */
+	/* field contains valid information as defined in the
+	 * SCSI-2 Standard.
+	 */
+	u8 segment_number;	/* Only used for COPY, COMPARE, or COPY AND VERIFY Commands */
+	u8 sense_key:4;		/* Sense Key */
+	u8 reserved:1;
+	u8 ILI:1;		/* Incorrect Length Indicator */
+	u8 EOM:1;		/* End Of Medium - reserved for random access devices */
+	u8 filemark:1;		/* Filemark - reserved for random access devices */
+
+	u8 information[4];	/* for direct-access devices, contains the unsigned 
+				 * logical block address or residue associated with 
+				 * the sense key 
+				 */
+	u8 add_sense_len;	/* number of additional sense bytes to follow this field */
+	u8 cmnd_info[4];	/* not used */
+	u8 ASC;			/* Additional Sense Code */
+	u8 ASCQ;		/* Additional Sense Code Qualifier */
+	u8 FRUC;		/* Field Replaceable Unit Code - not used */
+	u8 bit_ptr:3;		/* indicates which byte of the CDB or parameter data
+				 * was in error
+				 */
+	u8 BPV:1;		/* bit pointer valid (BPV): 1- indicates that 
+				 * the bit_ptr field has valid value
+				 */
+	u8 reserved2:2;
+	u8 CD:1;		/* command data bit: 1- illegal parameter in CDB.
+				 * 0- illegal parameter in data.
+				 */
+	u8 SKSV:1;
+	u8 field_ptr[2];	/* byte of the CDB or parameter data in error */
+};
+
+/*
+ *              M O D U L E   G L O B A L S
+ */
+ 
+static struct fsa_scsi_hba *fsa_dev[MAXIMUM_NUM_ADAPTERS];	/*  SCSI Device Instance Pointers */
+static struct sense_data sense_data[MAXIMUM_NUM_CONTAINERS];
+static void get_sd_devname(int disknum, char *buffer);
+static unsigned long aac_build_sg(Scsi_Cmnd* scsicmd, struct sgmap* sgmap);
+static unsigned long aac_build_sg64(Scsi_Cmnd* scsicmd, struct sgmap64* psg);
+static int aac_send_srb_fib(Scsi_Cmnd* scsicmd);
+#ifdef AAC_DETAILED_STATUS_INFO
+static char *aac_get_status_string(u32 status);
+#endif
+
+/**
+ *	aac_get_containers	-	list containers
+ *	@common: adapter to probe
+ *
+ *	Make a list of all containers on this controller
+ */
+int aac_get_containers(struct aac_dev *dev)
+{
+	struct fsa_scsi_hba *fsa_dev_ptr;
+	u32 index, status = 0;
+	struct aac_query_mount *dinfo;
+	struct aac_mount *dresp;
+	struct fib * fibptr;
+	unsigned instance;
+
+	fsa_dev_ptr = &(dev->fsa_dev);
+	instance = dev->scsi_host_ptr->unique_id;
+
+	if (!(fibptr = fib_alloc(dev)))
+		return -ENOMEM;
+
+	for (index = 0; index < MAXIMUM_NUM_CONTAINERS; index++) {
+		fib_init(fibptr);
+		dinfo = (struct aac_query_mount *) fib_data(fibptr);
+
+		dinfo->command = cpu_to_le32(VM_NameServe);
+		dinfo->count = cpu_to_le32(index);
+		dinfo->type = cpu_to_le32(FT_FILESYS);
+
+		status = fib_send(ContainerCommand,
+				    fibptr,
+				    sizeof (struct aac_query_mount),
+				    FsaNormal,
+				    1, 1,
+				    NULL, NULL);
+		if (status < 0 ) {
+			printk(KERN_WARNING "ProbeContainers: SendFIB failed.\n");
+			break;
+		}
+		dresp = (struct aac_mount *)fib_data(fibptr);
+
+		if ((le32_to_cpu(dresp->status) == ST_OK) &&
+		    (le32_to_cpu(dresp->mnt[0].vol) != CT_NONE)) {
+			fsa_dev_ptr->valid[index] = 1;
+			fsa_dev_ptr->type[index] = le32_to_cpu(dresp->mnt[0].vol);
+			fsa_dev_ptr->size[index] = le32_to_cpu(dresp->mnt[0].capacity);
+			if (le32_to_cpu(dresp->mnt[0].state) & FSCS_READONLY)
+				    fsa_dev_ptr->ro[index] = 1;
+		}
+		fib_complete(fibptr);
+		/*
+		 *	If there are no more containers, then stop asking.
+		 */
+		if ((index + 1) >= le32_to_cpu(dresp->count))
+			break;
+	}
+	fib_free(fibptr);
+	fsa_dev[instance] = fsa_dev_ptr;
+	return status;
+}
+
+/**
+ *	probe_container		-	query a logical volume
+ *	@dev: device to query
+ *	@cid: container identifier
+ *
+ *	Queries the controller about the given volume. The volume information
+ *	is updated in the struct fsa_scsi_hba structure rather than returned.
+ */
+ 
+static int probe_container(struct aac_dev *dev, int cid)
+{
+	struct fsa_scsi_hba *fsa_dev_ptr;
+	int status;
+	struct aac_query_mount *dinfo;
+	struct aac_mount *dresp;
+	struct fib * fibptr;
+	unsigned instance;
+
+	fsa_dev_ptr = &(dev->fsa_dev);
+	instance = dev->scsi_host_ptr->unique_id;
+
+	if (!(fibptr = fib_alloc(dev)))
+		return -ENOMEM;
+
+	fib_init(fibptr);
+
+	dinfo = (struct aac_query_mount *)fib_data(fibptr);
+
+	dinfo->command = cpu_to_le32(VM_NameServe);
+	dinfo->count = cpu_to_le32(cid);
+	dinfo->type = cpu_to_le32(FT_FILESYS);
+
+	status = fib_send(ContainerCommand,
+			    fibptr,
+			    sizeof(struct aac_query_mount),
+			    FsaNormal,
+			    1, 1,
+			    NULL, NULL);
+	if (status < 0) {
+		printk(KERN_WARNING "aacraid: probe_containers query failed.\n");
+		goto error;
+	}
+
+	dresp = (struct aac_mount *) fib_data(fibptr);
+
+	if ((le32_to_cpu(dresp->status) == ST_OK) &&
+	    (le32_to_cpu(dresp->mnt[0].vol) != CT_NONE)) {
+		fsa_dev_ptr->valid[cid] = 1;
+		fsa_dev_ptr->type[cid] = le32_to_cpu(dresp->mnt[0].vol);
+		fsa_dev_ptr->size[cid] = le32_to_cpu(dresp->mnt[0].capacity);
+		if (le32_to_cpu(dresp->mnt[0].state) & FSCS_READONLY)
+			fsa_dev_ptr->ro[cid] = 1;
+	}
+
+error:
+	fib_complete(fibptr);
+	fib_free(fibptr);
+
+	return status;
+}
+
+/* Local Structure to set SCSI inquiry data strings */
+struct scsi_inq {
+	char vid[8];         /* Vendor ID */
+	char pid[16];        /* Product ID */
+	char prl[4];         /* Product Revision Level */
+};
+
+/**
+ *	InqStrCopy	-	string merge
+ *	@a:	string to copy from
+ *	@b:	string to copy to
+ *
+ * 	Copy a String from one location to another
+ *	without copying \0
+ */
+
+static void inqstrcpy(char *a, char *b)
+{
+
+	while(*a != (char)0) 
+		*b++ = *a++;
+}
+
+static char *container_types[] = {
+        "None",
+        "Volume",
+        "Mirror",
+        "Stripe",
+        "RAID5",
+        "SSRW",
+        "SSRO",
+        "Morph",
+        "Legacy",
+        "RAID4",
+        "RAID10",             
+        "RAID00",             
+        "V-MIRRORS",          
+        "PSEUDO R4",          
+	"RAID50",
+        "Unknown"
+};
+
+
+
+/* Function: setinqstr
+ *
+ * Arguments: [1] pointer to void [1] int
+ *
+ * Purpose: Sets SCSI inquiry data strings for vendor, product
+ * and revision level. Allows strings to be set in platform dependant
+ * files instead of in OS dependant driver source.
+ */
+
+static void setinqstr(int devtype, void *data, int tindex)
+{
+	struct scsi_inq *str;
+	char *findit;
+	struct aac_driver_ident *mp;
+
+	mp = aac_get_driver_ident(devtype);
+   
+	str = (struct scsi_inq *)(data); /* cast data to scsi inq block */
+
+	inqstrcpy (mp->vname, str->vid); 
+	inqstrcpy (mp->model, str->pid); /* last six chars reserved for vol type */
+
+	findit = str->pid;
+
+	for ( ; *findit != ' '; findit++); /* walk till we find a space then incr by 1 */
+		findit++;
+	
+	if (tindex < (sizeof(container_types)/sizeof(char *))){
+		inqstrcpy (container_types[tindex], findit);
+	}
+	inqstrcpy ("V1.0", str->prl);
+}
+
+void set_sense(u8 *sense_buf, u8 sense_key, u8 sense_code,
+		    u8 a_sense_code, u8 incorrect_length,
+		    u8 bit_pointer, u16 field_pointer,
+		    u32 residue)
+{
+	sense_buf[0] = 0xF0;	/* Sense data valid, err code 70h (current error) */
+	sense_buf[1] = 0;	/* Segment number, always zero */
+
+	if (incorrect_length) {
+		sense_buf[2] = sense_key | 0x20;	/* Set ILI bit | sense key */
+		sense_buf[3] = BYTE3(residue);
+		sense_buf[4] = BYTE2(residue);
+		sense_buf[5] = BYTE1(residue);
+		sense_buf[6] = BYTE0(residue);
+	} else
+		sense_buf[2] = sense_key;	/* Sense key */
+
+	if (sense_key == SENKEY_ILLEGAL)
+		sense_buf[7] = 10;	/* Additional sense length */
+	else
+		sense_buf[7] = 6;	/* Additional sense length */
+
+	sense_buf[12] = sense_code;	/* Additional sense code */
+	sense_buf[13] = a_sense_code;	/* Additional sense code qualifier */
+	if (sense_key == SENKEY_ILLEGAL) {
+		sense_buf[15] = 0;
+
+		if (sense_code == SENCODE_INVALID_PARAM_FIELD)
+			sense_buf[15] = 0x80;	/* Std sense key specific field */
+		/* Illegal parameter is in the parameter block */
+
+		if (sense_code == SENCODE_INVALID_CDB_FIELD)
+			sense_buf[15] = 0xc0;	/* Std sense key specific field */
+		/* Illegal parameter is in the CDB block */
+		sense_buf[15] |= bit_pointer;
+		sense_buf[16] = field_pointer >> 8;	/* MSB */
+		sense_buf[17] = field_pointer;		/* LSB */
+	}
+}
+
+static void aac_io_done(Scsi_Cmnd * scsicmd)
+{
+	unsigned long cpu_flags;
+	spin_lock_irqsave(scsicmd->host->host_lock, cpu_flags);
+	scsicmd->scsi_done(scsicmd);
+	spin_unlock_irqrestore(scsicmd->host->host_lock, cpu_flags);
+}
+
+static void __aac_io_done(Scsi_Cmnd * scsicmd)
+{
+	scsicmd->scsi_done(scsicmd);
+}
+
+int aac_get_adapter_info(struct aac_dev* dev)
+{
+	struct fib* fibptr;
+	struct aac_adapter_info* info;
+	int rcode;
+	u32 tmp;
+	if (!(fibptr = fib_alloc(dev)))
+		return -ENOMEM;
+
+	fib_init(fibptr);
+	info = (struct aac_adapter_info*) fib_data(fibptr);
+
+	memset(info,0,sizeof(struct aac_adapter_info));
+
+	rcode = fib_send(RequestAdapterInfo,
+			fibptr, 
+			sizeof(struct aac_adapter_info),
+			FsaNormal, 
+			1, 1, 
+			NULL, 
+			NULL);
+
+	memcpy(&dev->adapter_info, info, sizeof(struct aac_adapter_info));
+
+	tmp = dev->adapter_info.kernelrev;
+	printk(KERN_INFO "%s%d: kernel %d.%d.%d build %d\n", 
+			dev->name, dev->id,
+			tmp>>24,(tmp>>16)&0xff,(tmp>>8)&0xff,
+			dev->adapter_info.kernelbuild);
+	tmp = dev->adapter_info.monitorrev;
+	printk(KERN_INFO "%s%d: monitor %d.%d.%d build %d\n", 
+			dev->name, dev->id,
+			tmp>>24,(tmp>>16)&0xff,(tmp>>8)&0xff,
+			dev->adapter_info.monitorbuild);
+	tmp = dev->adapter_info.biosrev;
+	printk(KERN_INFO "%s%d: bios %d.%d.%d build %d\n", 
+			dev->name, dev->id,
+			tmp>>24,(tmp>>16)&0xff,(tmp>>8)&0xff,
+			dev->adapter_info.biosbuild);
+	printk(KERN_INFO "%s%d: serial %x%x\n",
+			dev->name, dev->id,
+			dev->adapter_info.serial[0],
+			dev->adapter_info.serial[1]);
+	dev->pae_support = 0;
+	dev->nondasd_support = 0;
+	if( BITS_PER_LONG >= 64 && 
+	  (dev->adapter_info.options & AAC_OPT_SGMAP_HOST64)){
+		printk(KERN_INFO "%s%d: 64 Bit PAE enabled\n", dev->name, dev->id);
+		dev->pae_support = 1;
+	}
+	/* TODO - dmb temporary until fw can set this bit  */
+	dev->pae_support = (BITS_PER_LONG >= 64);
+	if(dev->pae_support != 0) {
+		printk(KERN_INFO "%s%d: 64 Bit PAE enabled\n", dev->name, dev->id);
+	}
+
+	if(dev->adapter_info.options & AAC_OPT_NONDASD){
+		dev->nondasd_support = 1;
+	}
+	return rcode;
+}
+
+
+static void read_callback(void *context, struct fib * fibptr)
+{
+	struct aac_dev *dev;
+	struct aac_read_reply *readreply;
+	Scsi_Cmnd *scsicmd;
+	u32 lba;
+	u32 cid;
+
+	scsicmd = (Scsi_Cmnd *) context;
+
+	dev = (struct aac_dev *)scsicmd->host->hostdata;
+	cid =TARGET_LUN_TO_CONTAINER(scsicmd->target, scsicmd->lun);
+
+	lba = ((scsicmd->cmnd[1] & 0x1F) << 16) | (scsicmd->cmnd[2] << 8) | scsicmd->cmnd[3];
+	dprintk((KERN_DEBUG "read_callback[cpu %d]: lba = %d, t = %ld.\n", smp_processor_id(), lba, jiffies));
+
+	if (fibptr == NULL)
+		BUG();
+		
+	if(scsicmd->use_sg)
+		pci_unmap_sg(dev->pdev, 
+			(struct scatterlist *)scsicmd->buffer,
+			scsicmd->use_sg,
+			scsi_to_pci_dma_dir(scsicmd->sc_data_direction));
+	else if(scsicmd->request_bufflen)
+		pci_unmap_single(dev->pdev, (dma_addr_t)(unsigned long)scsicmd->SCp.ptr,
+				 scsicmd->request_bufflen,
+				 scsi_to_pci_dma_dir(scsicmd->sc_data_direction));
+	readreply = (struct aac_read_reply *)fib_data(fibptr);
+	if (le32_to_cpu(readreply->status) == ST_OK)
+		scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8 | GOOD;
+	else {
+		printk(KERN_WARNING "read_callback: read failed, status = %d\n", readreply->status);
+		scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8 | CHECK_CONDITION;
+		set_sense((u8 *) &sense_data[cid],
+				    SENKEY_HW_ERR,
+				    SENCODE_INTERNAL_TARGET_FAILURE,
+				    ASENCODE_INTERNAL_TARGET_FAILURE, 0, 0,
+				    0, 0);
+	}
+	fib_complete(fibptr);
+	fib_free(fibptr);
+
+	aac_io_done(scsicmd);
+}
+
+static void write_callback(void *context, struct fib * fibptr)
+{
+	struct aac_dev *dev;
+	struct aac_write_reply *writereply;
+	Scsi_Cmnd *scsicmd;
+	u32 lba;
+	u32 cid;
+
+	scsicmd = (Scsi_Cmnd *) context;
+	dev = (struct aac_dev *)scsicmd->host->hostdata;
+	cid = TARGET_LUN_TO_CONTAINER(scsicmd->target, scsicmd->lun);
+
+	lba = ((scsicmd->cmnd[1] & 0x1F) << 16) | (scsicmd->cmnd[2] << 8) | scsicmd->cmnd[3];
+	dprintk((KERN_DEBUG "write_callback[cpu %d]: lba = %d, t = %ld.\n", smp_processor_id(), lba, jiffies));
+	if (fibptr == NULL)
+		BUG();
+
+	if(scsicmd->use_sg)
+		pci_unmap_sg(dev->pdev, 
+			(struct scatterlist *)scsicmd->buffer,
+			scsicmd->use_sg,
+			scsi_to_pci_dma_dir(scsicmd->sc_data_direction));
+	else if(scsicmd->request_bufflen)
+		pci_unmap_single(dev->pdev, (dma_addr_t)(unsigned long)scsicmd->SCp.ptr,
+				 scsicmd->request_bufflen,
+				 scsi_to_pci_dma_dir(scsicmd->sc_data_direction));
+
+	writereply = (struct aac_write_reply *) fib_data(fibptr);
+	if (le32_to_cpu(writereply->status) == ST_OK)
+		scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8 | GOOD;
+	else {
+		printk(KERN_WARNING "write_callback: write failed, status = %d\n", writereply->status);
+		scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8 | CHECK_CONDITION;
+		set_sense((u8 *) &sense_data[cid],
+				    SENKEY_HW_ERR,
+				    SENCODE_INTERNAL_TARGET_FAILURE,
+				    ASENCODE_INTERNAL_TARGET_FAILURE, 0, 0,
+				    0, 0);
+	}
+
+	fib_complete(fibptr);
+	fib_free(fibptr);
+	aac_io_done(scsicmd);
+}
+
+int aac_read(Scsi_Cmnd * scsicmd, int cid)
+{
+	u32 lba;
+	u32 count;
+	int status;
+
+	u16 fibsize;
+	struct aac_dev *dev;
+	struct fib * cmd_fibcontext;
+
+	dev = (struct aac_dev *)scsicmd->host->hostdata;
+	/*
+	 *	Get block address and transfer length
+	 */
+	if (scsicmd->cmnd[0] == SS_READ)	/* 6 byte command */
+	{
+		dprintk((KERN_DEBUG "aachba: received a read(6) command on target %d.\n", cid));
+
+		lba = ((scsicmd->cmnd[1] & 0x1F) << 16) | (scsicmd->cmnd[2] << 8) | scsicmd->cmnd[3];
+		count = scsicmd->cmnd[4];
+
+		if (count == 0)
+			count = 256;
+	} else {
+		dprintk((KERN_DEBUG "aachba: received a read(10) command on target %d.\n", cid));
+
+		lba = (scsicmd->cmnd[2] << 24) | (scsicmd->cmnd[3] << 16) | (scsicmd->cmnd[4] << 8) | scsicmd->cmnd[5];
+		count = (scsicmd->cmnd[7] << 8) | scsicmd->cmnd[8];
+	}
+	dprintk((KERN_DEBUG "aac_read[cpu %d]: lba = %u, t = %ld.\n", smp_processor_id(), lba, jiffies));
+	/*
+	 *	Alocate and initialize a Fib
+	 */
+	if (!(cmd_fibcontext = fib_alloc(dev))) {
+		scsicmd->result = DID_ERROR << 16;
+		aac_io_done(scsicmd);
+		return (-1);
+	}
+
+	fib_init(cmd_fibcontext);
+
+	if(dev->pae_support == 1){
+		struct aac_read64 *readcmd;
+		readcmd = (struct aac_read64 *) fib_data(cmd_fibcontext);
+		readcmd->command = cpu_to_le32(VM_CtHostRead64);
+		readcmd->cid = cpu_to_le16(cid);
+		readcmd->sector_count = cpu_to_le16(count);
+		readcmd->block = cpu_to_le32(lba);
+		readcmd->pad   = cpu_to_le16(0);
+		readcmd->flags = cpu_to_le16(0); 
+		
+		aac_build_sg64(scsicmd, &readcmd->sg);
+		if(readcmd->sg.count > MAX_DRIVER_SG_SEGMENT_COUNT)
+			BUG();
+		fibsize = sizeof(struct aac_read64) + ((readcmd->sg.count - 1) * sizeof (struct sgentry64));
+		/*
+		 *	Now send the Fib to the adapter
+		 */
+		status = fib_send(ContainerCommand64, 
+			  cmd_fibcontext, 
+			  fibsize, 
+			  FsaNormal, 
+			  0, 1, 
+			  (fib_callback) read_callback, 
+			  (void *) scsicmd);
+	} else {
+		struct aac_read *readcmd;
+		readcmd = (struct aac_read *) fib_data(cmd_fibcontext);
+		readcmd->command = cpu_to_le32(VM_CtBlockRead);
+		readcmd->cid = cpu_to_le32(cid);
+		readcmd->block = cpu_to_le32(lba);
+		readcmd->count = cpu_to_le32(count * 512);
+
+		if (count * 512 > (64 * 1024))
+			BUG();
+
+		aac_build_sg(scsicmd, &readcmd->sg);
+		if(readcmd->sg.count > MAX_DRIVER_SG_SEGMENT_COUNT)
+			BUG();
+		fibsize = sizeof(struct aac_read) + ((readcmd->sg.count - 1) * sizeof (struct sgentry));
+		/*
+		 *	Now send the Fib to the adapter
+		 */
+		status = fib_send(ContainerCommand, 
+			  cmd_fibcontext, 
+			  fibsize, 
+			  FsaNormal, 
+			  0, 1, 
+			  (fib_callback) read_callback, 
+			  (void *) scsicmd);
+	}
+	
+	
+	/*
+	 *	Check that the command queued to the controller
+	 */
+	if (status == -EINPROGRESS) 
+		return 0;
+		
+	printk(KERN_WARNING "aac_read: fib_send failed with status: %d.\n", status);
+	/*
+	 *	For some reason, the Fib didn't queue, return QUEUE_FULL
+	 */
+	scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8 | QUEUE_FULL;
+	aac_io_done(scsicmd);
+	fib_complete(cmd_fibcontext);
+	fib_free(cmd_fibcontext);
+	return -1;
+}
+
+static int aac_write(Scsi_Cmnd * scsicmd, int cid)
+{
+	u32 lba;
+	u32 count;
+	int status;
+	u16 fibsize;
+	struct aac_dev *dev;
+	struct fib * cmd_fibcontext;
+
+	dev = (struct aac_dev *)scsicmd->host->hostdata;
+	/*
+	 *	Get block address and transfer length
+	 */
+	if (scsicmd->cmnd[0] == SS_WRITE)	/* 6 byte command */
+	{
+		lba = ((scsicmd->cmnd[1] & 0x1F) << 16) | (scsicmd->cmnd[2] << 8) | scsicmd->cmnd[3];
+		count = scsicmd->cmnd[4];
+		if (count == 0)
+			count = 256;
+	} else {
+		dprintk((KERN_DEBUG "aachba: received a write(10) command on target %d.\n", cid));
+		lba = (scsicmd->cmnd[2] << 24) | (scsicmd->cmnd[3] << 16) | (scsicmd->cmnd[4] << 8) | scsicmd->cmnd[5];
+		count = (scsicmd->cmnd[7] << 8) | scsicmd->cmnd[8];
+	}
+	dprintk((KERN_DEBUG "aac_write[cpu %d]: lba = %lu, t = %ld.\n", smp_processor_id(), lba, jiffies));
+	/*
+	 *	Allocate and initialize a Fib then setup a BlockWrite command
+	 */
+	if (!(cmd_fibcontext = fib_alloc(dev))) {
+		scsicmd->result = DID_ERROR << 16;
+		aac_io_done(scsicmd);
+		return -1;
+	}
+	fib_init(cmd_fibcontext);
+
+	if(dev->pae_support == 1)
+	{
+		struct aac_write64 *writecmd;
+		writecmd = (struct aac_write64 *) fib_data(cmd_fibcontext);
+		writecmd->command = cpu_to_le32(VM_CtHostWrite64);
+		writecmd->cid = cpu_to_le16(cid);
+		writecmd->sector_count = cpu_to_le16(count); 
+		writecmd->block = cpu_to_le32(lba);
+		writecmd->pad	= cpu_to_le16(0);
+		writecmd->flags	= cpu_to_le16(0);
+
+		aac_build_sg64(scsicmd, &writecmd->sg);
+		if(writecmd->sg.count > MAX_DRIVER_SG_SEGMENT_COUNT)
+			BUG();
+		fibsize = sizeof(struct aac_write64) + ((writecmd->sg.count - 1) * sizeof (struct sgentry64));
+		/*
+		 *	Now send the Fib to the adapter
+		 */
+		status = fib_send(ContainerCommand64, 
+			  cmd_fibcontext, 
+			  fibsize, 
+			  FsaNormal, 
+			  0, 1, 
+			  (fib_callback) write_callback, 
+			  (void *) scsicmd);
+	}
+	else 
+	{
+		struct aac_write *writecmd;
+		writecmd = (struct aac_write *) fib_data(cmd_fibcontext);
+		writecmd->command = cpu_to_le32(VM_CtBlockWrite);
+		writecmd->cid = cpu_to_le32(cid);
+		writecmd->block = cpu_to_le32(lba);
+		writecmd->count = cpu_to_le32(count * 512);
+		writecmd->sg.count = cpu_to_le32(1);
+		/* ->stable is not used - it did mean which type of write */
+
+		if (count * 512 > (64 * 1024))
+			BUG();
+		aac_build_sg(scsicmd, &writecmd->sg);
+		if(writecmd->sg.count > MAX_DRIVER_SG_SEGMENT_COUNT)
+			BUG();
+		fibsize = sizeof(struct aac_write) + ((writecmd->sg.count - 1) * sizeof (struct sgentry));
+		/*
+		 *	Now send the Fib to the adapter
+		 */
+		status = fib_send(ContainerCommand, 
+			  cmd_fibcontext, 
+			  fibsize, 
+			  FsaNormal, 
+			  0, 1, 
+			  (fib_callback) write_callback, 
+			  (void *) scsicmd);
+	}
+
+	/*
+	 *	Check that the command queued to the controller
+	 */
+	if (status == -EINPROGRESS)
+		return 0;
+
+	printk(KERN_WARNING "aac_write: fib_send failed with status: %d\n", status);
+	/*
+	 *	For some reason, the Fib didn't queue, return QUEUE_FULL
+	 */
+	scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8 | QUEUE_FULL;
+	aac_io_done(scsicmd);
+
+	fib_complete(cmd_fibcontext);
+	fib_free(cmd_fibcontext);
+	return -1;
+}
+
+
+/**
+ *	aac_scsi_cmd()		-	Process SCSI command
+ *	@scsicmd:		SCSI command block
+ *	@wait:			1 if the user wants to await completion
+ *
+ *	Emulate a SCSI command and queue the required request for the
+ *	aacraid firmware.
+ */
+ 
+int aac_scsi_cmd(Scsi_Cmnd * scsicmd)
+{
+	u32 cid = 0;
+	struct fsa_scsi_hba *fsa_dev_ptr;
+	int cardtype;
+	int ret;
+	struct aac_dev *dev = (struct aac_dev *)scsicmd->host->hostdata;
+	
+	cardtype = dev->cardtype;
+
+	fsa_dev_ptr = fsa_dev[scsicmd->host->unique_id];
+
+	/*
+	 *	If the bus, target or lun is out of range, return fail
+	 *	Test does not apply to ID 16, the pseudo id for the controller
+	 *	itself.
+	 */
+	if (scsicmd->target != scsicmd->host->this_id) {
+		if ((scsicmd->channel == 0) ){
+			if( (scsicmd->target >= AAC_MAX_TARGET) || (scsicmd->lun != 0)){ 
+				scsicmd->result = DID_NO_CONNECT << 16;
+				__aac_io_done(scsicmd);
+				return 0;
+			}
+			cid = TARGET_LUN_TO_CONTAINER(scsicmd->target, scsicmd->lun);
+
+			/*
+			 *	If the target container doesn't exist, it may have
+			 *	been newly created
+			 */
+			if (fsa_dev_ptr->valid[cid] == 0) {
+				switch (scsicmd->cmnd[0]) {
+				case SS_INQUIR:
+				case SS_RDCAP:
+				case SS_TEST:
+					spin_unlock_irq(scsicmd->host->host_lock);
+					probe_container(dev, cid);
+					spin_lock_irq(scsicmd->host->host_lock);
+					if (fsa_dev_ptr->valid[cid] == 0) {
+						scsicmd->result = DID_NO_CONNECT << 16;
+						__aac_io_done(scsicmd);
+						return 0;
+					}
+				default:
+					break;
+				}
+			}
+			/*
+			 *	If the target container still doesn't exist, 
+			 *	return failure
+			 */
+			if (fsa_dev_ptr->valid[cid] == 0) {
+				scsicmd->result = DID_BAD_TARGET << 16;
+				__aac_io_done(scsicmd);
+				return -1;
+			}
+		} else {  /* check for physical non-dasd devices */
+			if(dev->nondasd_support == 1){
+				return aac_send_srb_fib(scsicmd);
+			} else {
+				scsicmd->result = DID_NO_CONNECT << 16;
+				__aac_io_done(scsicmd);
+				return 0;
+			}
+		}
+	}
+	/*
+	 * else Command for the controller itself
+	 */
+	else if ((scsicmd->cmnd[0] != SS_INQUIR) &&	/* only INQUIRY & TUR cmnd supported for controller */
+		(scsicmd->cmnd[0] != SS_TEST)) 
+	{
+		dprintk((KERN_WARNING "Only INQUIRY & TUR command supported for controller, rcvd = 0x%x.\n", scsicmd->cmnd[0]));
+		scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8 | CHECK_CONDITION;
+		set_sense((u8 *) &sense_data[cid],
+			    SENKEY_ILLEGAL,
+			    SENCODE_INVALID_COMMAND,
+			    ASENCODE_INVALID_COMMAND, 0, 0, 0, 0);
+		__aac_io_done(scsicmd);
+		return -1;
+	}
+
+
+	/* Handle commands here that don't really require going out to the adapter */
+	switch (scsicmd->cmnd[0]) {
+	case SS_INQUIR:
+	{
+		struct inquiry_data *inq_data_ptr;
+
+		dprintk((KERN_DEBUG "INQUIRY command, ID: %d.\n", scsicmd->target));
+		inq_data_ptr = (struct inquiry_data *)scsicmd->request_buffer;
+		memset(inq_data_ptr, 0, sizeof (struct inquiry_data));
+
+		inq_data_ptr->inqd_ver = 2;	/* claim compliance to SCSI-2 */
+		inq_data_ptr->inqd_dtq = 0x80;	/* set RMB bit to one indicating that the medium is removable */
+		inq_data_ptr->inqd_rdf = 2;	/* A response data format value of two indicates that the data shall be in the format specified in SCSI-2 */
+		inq_data_ptr->inqd_len = 31;
+		/*Format for "pad2" is  RelAdr | WBus32 | WBus16 |  Sync  | Linked |Reserved| CmdQue | SftRe */
+		inq_data_ptr->inqd_pad2= 0x32 ;	 /*WBus16|Sync|CmdQue */
+		/*
+		 *	Set the Vendor, Product, and Revision Level
+		 *	see: <vendor>.c i.e. aac.c
+		 */
+		setinqstr(cardtype, (void *) (inq_data_ptr->inqd_vid), fsa_dev_ptr->type[cid]);
+		if (scsicmd->target == scsicmd->host->this_id)
+			inq_data_ptr->inqd_pdt = INQD_PDT_PROC;	/* Processor device */
+		else
+			inq_data_ptr->inqd_pdt = INQD_PDT_DA;	/* Direct/random access device */
+		scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8 | GOOD;
+		__aac_io_done(scsicmd);
+		return 0;
+	}
+	case SS_RDCAP:
+	{
+		int capacity;
+		char *cp;
+
+		dprintk((KERN_DEBUG "READ CAPACITY command.\n"));
+		capacity = fsa_dev_ptr->size[cid] - 1;
+		cp = scsicmd->request_buffer;
+		cp[0] = (capacity >> 24) & 0xff;
+		cp[1] = (capacity >> 16) & 0xff;
+		cp[2] = (capacity >> 8) & 0xff;
+		cp[3] = (capacity >> 0) & 0xff;
+		cp[4] = 0;
+		cp[5] = 0;
+		cp[6] = 2;
+		cp[7] = 0;
+
+		scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8 | GOOD;
+		__aac_io_done(scsicmd);
+
+		return 0;
+	}
+
+	case SS_MODESEN:
+	{
+		char *mode_buf;
+
+		dprintk((KERN_DEBUG "MODE SENSE command.\n"));
+		mode_buf = scsicmd->request_buffer;
+		mode_buf[0] = 0;	/* Mode data length (MSB) */
+		mode_buf[1] = 6;	/* Mode data length (LSB) */
+		mode_buf[2] = 0;	/* Medium type - default */
+		mode_buf[3] = 0;	/* Device-specific param, bit 8: 0/1 = write enabled/protected */
+		mode_buf[4] = 0;	/* reserved */
+		mode_buf[5] = 0;	/* reserved */
+		mode_buf[6] = 0;	/* Block descriptor length (MSB) */
+		mode_buf[7] = 0;	/* Block descriptor length (LSB) */
+
+		scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8 | GOOD;
+		__aac_io_done(scsicmd);
+
+		return 0;
+	}
+	case SS_REQSEN:
+		dprintk((KERN_DEBUG "REQUEST SENSE command.\n"));
+		memcpy(scsicmd->sense_buffer, &sense_data[cid], sizeof (struct sense_data));
+		memset(&sense_data[cid], 0, sizeof (struct sense_data));
+		scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8 | GOOD;
+		__aac_io_done(scsicmd);
+		return (0);
+
+	case SS_LOCK:
+		dprintk((KERN_DEBUG "LOCK command.\n"));
+		if (scsicmd->cmnd[4])
+			fsa_dev_ptr->locked[cid] = 1;
+		else
+			fsa_dev_ptr->locked[cid] = 0;
+
+		scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8 | GOOD;
+		__aac_io_done(scsicmd);
+		return 0;
+	/*
+	 *	These commands are all No-Ops
+	 */
+	case SS_TEST:
+	case SS_RESERV:
+	case SS_RELES:
+	case SS_REZERO:
+	case SS_REASGN:
+	case SS_SEEK:
+	case SS_ST_SP:
+		scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8 | GOOD;
+		__aac_io_done(scsicmd);
+		return (0);
+	}
+
+	switch (scsicmd->cmnd[0]) 
+	{
+		case SS_READ:
+		case SM_READ:
+			/*
+			 *	Hack to keep track of ordinal number of the device that
+			 *	corresponds to a container. Needed to convert
+			 *	containers to /dev/sd device names
+			 */
+			 
+			spin_unlock_irq(scsicmd->host->host_lock);
+			fsa_dev_ptr->devno[cid] = DEVICE_NR(scsicmd->sc_request->sr_request->rq_dev);
+			ret = aac_read(scsicmd, cid);
+			spin_lock_irq(scsicmd->host->host_lock);
+			return ret;
+
+		case SS_WRITE:
+		case SM_WRITE:
+			spin_unlock_irq(scsicmd->host->host_lock);
+			ret = aac_write(scsicmd, cid);
+			spin_lock_irq(scsicmd->host->host_lock);
+			return ret;
+		default:
+			/*
+			 *	Unhandled commands
+			 */
+			printk(KERN_WARNING "Unhandled SCSI Command: 0x%x.\n", scsicmd->cmnd[0]);
+			scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8 | CHECK_CONDITION;
+			set_sense((u8 *) &sense_data[cid],
+				SENKEY_ILLEGAL, SENCODE_INVALID_COMMAND,
+			ASENCODE_INVALID_COMMAND, 0, 0, 0, 0);
+			__aac_io_done(scsicmd);
+			return -1;
+	}
+}
+
+static int query_disk(struct aac_dev *dev, void *arg)
+{
+	struct aac_query_disk qd;
+	struct fsa_scsi_hba *fsa_dev_ptr;
+
+	fsa_dev_ptr = &(dev->fsa_dev);
+	if (copy_from_user(&qd, arg, sizeof (struct aac_query_disk)))
+		return -EFAULT;
+	if (qd.cnum == -1)
+		qd.cnum = TARGET_LUN_TO_CONTAINER(qd.target, qd.lun);
+	else if ((qd.bus == -1) && (qd.target == -1) && (qd.lun == -1)) 
+	{
+		if (qd.cnum < 0 || qd.cnum > MAXIMUM_NUM_CONTAINERS)
+			return -EINVAL;
+		qd.instance = dev->scsi_host_ptr->host_no;
+		qd.bus = 0;
+		qd.target = CONTAINER_TO_TARGET(qd.cnum);
+		qd.lun = CONTAINER_TO_LUN(qd.cnum);
+	}
+	else return -EINVAL;
+
+	qd.valid = fsa_dev_ptr->valid[qd.cnum];
+	qd.locked = fsa_dev_ptr->locked[qd.cnum];
+	qd.deleted = fsa_dev_ptr->deleted[qd.cnum];
+
+	if (fsa_dev_ptr->devno[qd.cnum] == -1)
+		qd.unmapped = 1;
+	else
+		qd.unmapped = 0;
+
+	get_sd_devname(fsa_dev_ptr->devno[qd.cnum], qd.name);
+
+	if (copy_to_user(arg, &qd, sizeof (struct aac_query_disk)))
+		return -EFAULT;
+	return 0;
+}
+
+static void get_sd_devname(int disknum, char *buffer)
+{
+	if (disknum < 0) {
+		sprintf(buffer, "%s", "");
+		return;
+	}
+
+	if (disknum < 26)
+		sprintf(buffer, "sd%c", 'a' + disknum);
+	else {
+		unsigned int min1;
+		unsigned int min2;
+		/*
+		 * For larger numbers of disks, we need to go to a new
+		 * naming scheme.
+		 */
+		min1 = disknum / 26;
+		min2 = disknum % 26;
+		sprintf(buffer, "sd%c%c", 'a' + min1 - 1, 'a' + min2);
+	}
+}
+
+static int force_delete_disk(struct aac_dev *dev, void *arg)
+{
+	struct aac_delete_disk dd;
+	struct fsa_scsi_hba *fsa_dev_ptr;
+
+	fsa_dev_ptr = &(dev->fsa_dev);
+
+	if (copy_from_user(&dd, arg, sizeof (struct aac_delete_disk)))
+		return -EFAULT;
+
+	if (dd.cnum > MAXIMUM_NUM_CONTAINERS)
+		return -EINVAL;
+	/*
+	 *	Mark this container as being deleted.
+	 */
+	fsa_dev_ptr->deleted[dd.cnum] = 1;
+	/*
+	 *	Mark the container as no longer valid
+	 */
+	fsa_dev_ptr->valid[dd.cnum] = 0;
+	return 0;
+}
+
+static int delete_disk(struct aac_dev *dev, void *arg)
+{
+	struct aac_delete_disk dd;
+	struct fsa_scsi_hba *fsa_dev_ptr;
+
+	fsa_dev_ptr = &(dev->fsa_dev);
+
+	if (copy_from_user(&dd, arg, sizeof (struct aac_delete_disk)))
+		return -EFAULT;
+
+	if (dd.cnum > MAXIMUM_NUM_CONTAINERS)
+		return -EINVAL;
+	/*
+	 *	If the container is locked, it can not be deleted by the API.
+	 */
+	if (fsa_dev_ptr->locked[dd.cnum])
+		return -EBUSY;
+	else {
+		/*
+		 *	Mark the container as no longer being valid.
+		 */
+		fsa_dev_ptr->valid[dd.cnum] = 0;
+		fsa_dev_ptr->devno[dd.cnum] = -1;
+		return 0;
+	}
+}
+
+int aac_dev_ioctl(struct aac_dev *dev, int cmd, void *arg)
+{
+	switch (cmd) {
+	case FSACTL_QUERY_DISK:
+		return query_disk(dev, arg);
+	case FSACTL_DELETE_DISK:
+		return delete_disk(dev, arg);
+	case FSACTL_FORCE_DELETE_DISK:
+		return force_delete_disk(dev, arg);
+	case 2131:
+		return aac_get_containers(dev);
+	default:
+		return -ENOTTY;
+	}
+}
+
+/**
+ *
+ * aac_srb_callback
+ * @context: the context set in the fib - here it is scsi cmd
+ * @fibptr: pointer to the fib
+ *
+ * Handles the completion of a scsi command to a non dasd device
+ *
+ */
+
+static void aac_srb_callback(void *context, struct fib * fibptr)
+{
+	struct aac_dev *dev;
+	struct aac_srb_reply *srbreply;
+	Scsi_Cmnd *scsicmd;
+
+	scsicmd = (Scsi_Cmnd *) context;
+	dev = (struct aac_dev *)scsicmd->host->hostdata;
+
+	if (fibptr == NULL)
+		BUG();
+
+	srbreply = (struct aac_srb_reply *) fib_data(fibptr);
+
+	scsicmd->sense_buffer[0] = '\0';  // initialize sense valid flag to false
+	// calculate resid for sg 
+	scsicmd->resid = scsicmd->request_bufflen - srbreply->data_xfer_length;
+
+	if(scsicmd->use_sg)
+		pci_unmap_sg(dev->pdev, 
+			(struct scatterlist *)scsicmd->buffer,
+			scsicmd->use_sg,
+			scsi_to_pci_dma_dir(scsicmd->sc_data_direction));
+	else if(scsicmd->request_bufflen)
+		pci_unmap_single(dev->pdev, (ulong)scsicmd->SCp.ptr, scsicmd->request_bufflen,
+			scsi_to_pci_dma_dir(scsicmd->sc_data_direction));
+
+	/*
+	 * First check the fib status
+	 */
+
+	if (le32_to_cpu(srbreply->status) != ST_OK){
+		int len;
+		printk(KERN_WARNING "aac_srb_callback: srb failed, status = %d\n", le32_to_cpu(srbreply->status));
+		len = (srbreply->sense_data_size > sizeof(scsicmd->sense_buffer))?
+				sizeof(scsicmd->sense_buffer):srbreply->sense_data_size;
+		scsicmd->result = DID_ERROR << 16 | COMMAND_COMPLETE << 8 | CHECK_CONDITION;
+		memcpy(scsicmd->sense_buffer, srbreply->sense_data, len);
+	}
+
+	/*
+	 * Next check the srb status
+	 */
+	switch(le32_to_cpu(srbreply->srb_status)){
+	case SRB_STATUS_ERROR_RECOVERY:
+	case SRB_STATUS_PENDING:
+	case SRB_STATUS_SUCCESS:
+		if(scsicmd->cmnd[0] == INQUIRY ){
+			u8 b;
+			/* We can't expose disk devices because we can't tell whether they
+			 * are the raw container drives or stand alone drives
+			 */
+			b = *(u8*)scsicmd->buffer;
+			if( (b & 0x0f) == TYPE_DISK ){
+				scsicmd->result = DID_NO_CONNECT << 16 | COMMAND_COMPLETE << 8;
+			}
+		} else {
+			scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8;
+		}
+		break;
+	case SRB_STATUS_DATA_OVERRUN:
+		switch(scsicmd->cmnd[0]){
+		case  READ_6:
+		case  WRITE_6:
+		case  READ_10:
+		case  WRITE_10:
+		case  READ_12:
+		case  WRITE_12:
+			if(le32_to_cpu(srbreply->data_xfer_length) < scsicmd->underflow ) {
+				printk(KERN_WARNING"aacraid: SCSI CMD underflow\n");
+			} else {
+				printk(KERN_WARNING"aacraid: SCSI CMD Data Overrun\n");
+			}
+			scsicmd->result = DID_ERROR << 16 | COMMAND_COMPLETE << 8;
+			break;
+		default:
+			scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8;
+			break;
+		}
+		break;
+	case SRB_STATUS_ABORTED:
+		scsicmd->result = DID_ABORT << 16 | ABORT << 8;
+		break;
+	case SRB_STATUS_ABORT_FAILED:
+		// Not sure about this one - but assuming the hba was trying to abort for some reason
+		scsicmd->result = DID_ERROR << 16 | ABORT << 8;
+		break;
+	case SRB_STATUS_PARITY_ERROR:
+		scsicmd->result = DID_PARITY << 16 | MSG_PARITY_ERROR << 8;
+		break;
+	case SRB_STATUS_NO_DEVICE:
+	case SRB_STATUS_INVALID_PATH_ID:
+	case SRB_STATUS_INVALID_TARGET_ID:
+	case SRB_STATUS_INVALID_LUN:
+	case SRB_STATUS_SELECTION_TIMEOUT:
+		scsicmd->result = DID_NO_CONNECT << 16 | COMMAND_COMPLETE << 8;
+		break;
+
+	case SRB_STATUS_COMMAND_TIMEOUT:
+	case SRB_STATUS_TIMEOUT:
+		scsicmd->result = DID_TIME_OUT << 16 | COMMAND_COMPLETE << 8;
+		break;
+
+	case SRB_STATUS_BUSY:
+		scsicmd->result = DID_NO_CONNECT << 16 | COMMAND_COMPLETE << 8;
+		break;
+
+	case SRB_STATUS_BUS_RESET:
+		scsicmd->result = DID_RESET << 16 | COMMAND_COMPLETE << 8;
+		break;
+
+	case SRB_STATUS_MESSAGE_REJECTED:
+		scsicmd->result = DID_ERROR << 16 | MESSAGE_REJECT << 8;
+		break;
+	case SRB_STATUS_REQUEST_FLUSHED:
+	case SRB_STATUS_ERROR:
+	case SRB_STATUS_INVALID_REQUEST:
+	case SRB_STATUS_REQUEST_SENSE_FAILED:
+	case SRB_STATUS_NO_HBA:
+	case SRB_STATUS_UNEXPECTED_BUS_FREE:
+	case SRB_STATUS_PHASE_SEQUENCE_FAILURE:
+	case SRB_STATUS_BAD_SRB_BLOCK_LENGTH:
+	case SRB_STATUS_DELAYED_RETRY:
+	case SRB_STATUS_BAD_FUNCTION:
+	case SRB_STATUS_NOT_STARTED:
+	case SRB_STATUS_NOT_IN_USE:
+	case SRB_STATUS_FORCE_ABORT:
+	case SRB_STATUS_DOMAIN_VALIDATION_FAIL:
+	default:
+#ifdef AAC_DETAILED_STATUS_INFO
+		printk("aacraid: SRB ERROR (%s)\n",aac_get_status_string(le32_to_cpu(srbreply->srb_status)));
+#endif
+		scsicmd->result = DID_ERROR << 16 | COMMAND_COMPLETE << 8;
+		break;
+	}
+	if (le32_to_cpu(srbreply->scsi_status) == 0x02 ){  // Check Condition
+		int len;
+		len = (srbreply->sense_data_size > sizeof(scsicmd->sense_buffer))?
+				sizeof(scsicmd->sense_buffer):srbreply->sense_data_size;
+		printk(KERN_WARNING "aac_srb_callback: check condition, status = %d len=%d\n", le32_to_cpu(srbreply->status), len);
+		memcpy(scsicmd->sense_buffer, srbreply->sense_data, len);
+	}
+	/*
+	 * OR in the scsi status (already shifted up a bit)
+	 */
+	scsicmd->result |= le32_to_cpu(srbreply->scsi_status);
+
+	fib_complete(fibptr);
+	fib_free(fibptr);
+	aac_io_done(scsicmd);
+}
+
+/**
+ *
+ * aac_send_scb_fib
+ * @scsicmd: the scsi command block
+ *
+ * This routine will form a FIB and fill in the aac_srb from the 
+ * scsicmd passed in.
+ */
+
+static int aac_send_srb_fib(Scsi_Cmnd* scsicmd)
+{
+	struct fib* cmd_fibcontext;
+	struct aac_dev* dev;
+	int status;
+	struct aac_srb *srbcmd;
+	u16 fibsize;
+	u32 flag;
+
+	if( scsicmd->target > 15 || scsicmd->lun > 7) {
+		scsicmd->result = DID_NO_CONNECT << 16;
+		__aac_io_done(scsicmd);
+		return 0;
+	}
+
+	dev = (struct aac_dev *)scsicmd->host->hostdata;
+	switch(scsicmd->sc_data_direction){
+	case SCSI_DATA_WRITE:
+		flag = SRB_DataOut;
+		break;
+	case SCSI_DATA_UNKNOWN:  
+		flag = SRB_DataIn | SRB_DataOut;
+		break;
+	case SCSI_DATA_READ:
+		flag = SRB_DataIn;
+		break;
+	case SCSI_DATA_NONE: 
+	default:
+		flag = SRB_NoDataXfer;
+		break;
+	}
+
+
+	/*
+	 *	Allocate and initialize a Fib then setup a BlockWrite command
+	 */
+	if (!(cmd_fibcontext = fib_alloc(dev))) {
+		scsicmd->result = DID_ERROR << 16;
+		__aac_io_done(scsicmd);
+		return -1;
+	}
+	fib_init(cmd_fibcontext);
+
+	srbcmd = (struct aac_srb*) fib_data(cmd_fibcontext);
+	srbcmd->function = cpu_to_le32(SRBF_ExecuteScsi);
+	srbcmd->channel  = cpu_to_le32(aac_logical_to_phys(scsicmd->channel));
+	srbcmd->target   = cpu_to_le32(scsicmd->target);
+	srbcmd->lun      = cpu_to_le32(scsicmd->lun);
+	srbcmd->flags    = cpu_to_le32(flag);
+	srbcmd->timeout  = cpu_to_le32(0);  // timeout not used
+	srbcmd->retry_limit =cpu_to_le32(0); // Obsolete parameter
+	srbcmd->cdb_size = cpu_to_le32(scsicmd->cmd_len);
+	
+	if( dev->pae_support ==1 ) {
+		aac_build_sg64(scsicmd, (struct sgmap64*) &srbcmd->sg);
+		srbcmd->count = cpu_to_le32(scsicmd->request_bufflen);
+
+		memset(srbcmd->cdb, 0, sizeof(srbcmd->cdb));
+		memcpy(srbcmd->cdb, scsicmd->cmnd, scsicmd->cmd_len);
+		/*
+		 *	Build Scatter/Gather list
+		 */
+		fibsize = sizeof (struct aac_srb) + (((srbcmd->sg.count & 0xff) - 1) * sizeof (struct sgentry64));
+
+		/*
+		 *	Now send the Fib to the adapter
+		 */
+		status = fib_send(ScsiPortCommand64, cmd_fibcontext, fibsize, FsaNormal, 0, 1,
+				  (fib_callback) aac_srb_callback, (void *) scsicmd);
+	} else {
+		aac_build_sg(scsicmd, (struct sgmap*)&srbcmd->sg);
+		srbcmd->count = cpu_to_le32(scsicmd->request_bufflen);
+
+		memset(srbcmd->cdb, 0, sizeof(srbcmd->cdb));
+		memcpy(srbcmd->cdb, scsicmd->cmnd, scsicmd->cmd_len);
+		/*
+		 *	Build Scatter/Gather list
+		 */
+		fibsize = sizeof (struct aac_srb) + (((srbcmd->sg.count & 0xff) - 1) * sizeof (struct sgentry));
+
+		/*
+		 *	Now send the Fib to the adapter
+		 */
+		status = fib_send(ScsiPortCommand, cmd_fibcontext, fibsize, FsaNormal, 0, 1,
+				  (fib_callback) aac_srb_callback, (void *) scsicmd);
+	}
+	/*
+	 *	Check that the command queued to the controller
+	 */
+	if (status == -EINPROGRESS){
+		return 0;
+	}
+
+	printk(KERN_WARNING "aac_srb: fib_send failed with status: %d\n", status);
+	/*
+	 *	For some reason, the Fib didn't queue, return QUEUE_FULL
+	 */
+	scsicmd->result = DID_OK << 16 | COMMAND_COMPLETE << 8 | QUEUE_FULL;
+	__aac_io_done(scsicmd);
+
+	fib_complete(cmd_fibcontext);
+	fib_free(cmd_fibcontext);
+
+	return -1;
+}
+
+static unsigned long aac_build_sg(Scsi_Cmnd* scsicmd, struct sgmap* psg)
+{
+	struct aac_dev *dev;
+	unsigned long byte_count = 0;
+
+	dev = (struct aac_dev *)scsicmd->host->hostdata;
+	// Get rid of old data
+	psg->count = cpu_to_le32(0);
+	psg->sg[0].addr = cpu_to_le32(NULL);
+	psg->sg[0].count = cpu_to_le32(0);  
+	if (scsicmd->use_sg) {
+		struct scatterlist *sg;
+		int i;
+		int sg_count;
+		sg = (struct scatterlist *) scsicmd->request_buffer;
+
+		sg_count = pci_map_sg(dev->pdev, sg, scsicmd->use_sg,
+			scsi_to_pci_dma_dir(scsicmd->sc_data_direction));
+		psg->count = cpu_to_le32(sg_count);
+
+		byte_count = 0;
+
+		for (i = 0; i < sg_count; i++) {
+			psg->sg[i].addr = cpu_to_le32(sg_dma_address(sg));
+			psg->sg[i].count = cpu_to_le32(sg_dma_len(sg));
+			byte_count += sg_dma_len(sg);
+			sg++;
+		}
+		/* hba wants the size to be exact */
+		if(byte_count > scsicmd->request_bufflen){
+			psg->sg[i-1].count -= (byte_count - scsicmd->request_bufflen);
+			byte_count = scsicmd->request_bufflen;
+		}
+		/* Check for command underflow */
+		if(scsicmd->underflow && (byte_count < scsicmd->underflow)){
+			printk(KERN_WARNING"aacraid: cmd len %08lX cmd underflow %08X\n",
+					byte_count, scsicmd->underflow);
+		}
+	}
+	else if(scsicmd->request_bufflen) {
+		dma_addr_t addr; 
+		addr = pci_map_single(dev->pdev,
+				scsicmd->request_buffer,
+				scsicmd->request_bufflen,
+				scsi_to_pci_dma_dir(scsicmd->sc_data_direction));
+		psg->count = cpu_to_le32(1);
+		psg->sg[0].addr = cpu_to_le32(addr);
+		psg->sg[0].count = cpu_to_le32(scsicmd->request_bufflen);  
+		scsicmd->SCp.ptr = (void *)addr;
+		byte_count = scsicmd->request_bufflen;
+	}
+	return byte_count;
+}
+
+
+static unsigned long aac_build_sg64(Scsi_Cmnd* scsicmd, struct sgmap64* psg)
+{
+	struct aac_dev *dev;
+	unsigned long byte_count = 0;
+	u64 le_addr;
+
+	dev = (struct aac_dev *)scsicmd->host->hostdata;
+	// Get rid of old data
+	psg->count = cpu_to_le32(0);
+	psg->sg[0].addr[0] = cpu_to_le32(NULL);
+	psg->sg[0].addr[1] = cpu_to_le32(NULL);
+	psg->sg[0].count = cpu_to_le32(0);  
+	if (scsicmd->use_sg) {
+		struct scatterlist *sg;
+		int i;
+		int sg_count;
+		sg = (struct scatterlist *) scsicmd->request_buffer;
+
+		sg_count = pci_map_sg(dev->pdev, sg, scsicmd->use_sg,
+			scsi_to_pci_dma_dir(scsicmd->sc_data_direction));
+		psg->count = cpu_to_le32(sg_count);
+
+		byte_count = 0;
+
+		for (i = 0; i < sg_count; i++) {
+			le_addr = cpu_to_le64(sg_dma_address(sg));
+			psg->sg[i].addr[1] = (u32)(le_addr>>32);
+			psg->sg[i].addr[0] = (u32)(le_addr & 0xffffffff);
+			psg->sg[i].count = cpu_to_le32(sg_dma_len(sg));
+			byte_count += sg_dma_len(sg);
+			sg++;
+		}
+		/* hba wants the size to be exact */
+		if(byte_count > scsicmd->request_bufflen){
+			psg->sg[i-1].count -= (byte_count - scsicmd->request_bufflen);
+			byte_count = scsicmd->request_bufflen;
+		}
+		/* Check for command underflow */
+		if(scsicmd->underflow && (byte_count < scsicmd->underflow)){
+			printk(KERN_WARNING"aacraid: cmd len %08lX cmd underflow %08X\n",
+					byte_count, scsicmd->underflow);
+		}
+	}
+	else if(scsicmd->request_bufflen) {
+		dma_addr_t addr; 
+		addr = pci_map_single(dev->pdev,
+				scsicmd->request_buffer,
+				scsicmd->request_bufflen,
+				scsi_to_pci_dma_dir(scsicmd->sc_data_direction));
+		psg->count = cpu_to_le32(1);
+		le_addr = cpu_to_le64(addr);
+		psg->sg[0].addr[1] = (u32)(le_addr>>32);
+		psg->sg[0].addr[0] = (u32)(le_addr & 0xffffffff);
+		psg->sg[0].count = cpu_to_le32(scsicmd->request_bufflen);  
+		scsicmd->SCp.ptr = (void *)addr;
+		byte_count = scsicmd->request_bufflen;
+	}
+	return byte_count;
+}
+
+#ifdef AAC_DETAILED_STATUS_INFO
+
+struct aac_srb_status_info {
+	u32	status;
+	char	*str;
+};
+
+
+static struct aac_srb_status_info srb_status_info[] = {
+	{ SRB_STATUS_PENDING,		"Pending Status"},
+	{ SRB_STATUS_SUCCESS,		"Success"},
+	{ SRB_STATUS_ABORTED,		"Aborted Command"},
+	{ SRB_STATUS_ABORT_FAILED,	"Abort Failed"},
+	{ SRB_STATUS_ERROR,		"Error Event"}, 
+	{ SRB_STATUS_BUSY,		"Device Busy"},
+	{ SRB_STATUS_INVALID_REQUEST,	"Invalid Request"},
+	{ SRB_STATUS_INVALID_PATH_ID,	"Invalid Path ID"},
+	{ SRB_STATUS_NO_DEVICE,		"No Device"},
+	{ SRB_STATUS_TIMEOUT,		"Timeout"},
+	{ SRB_STATUS_SELECTION_TIMEOUT,	"Selection Timeout"},
+	{ SRB_STATUS_COMMAND_TIMEOUT,	"Command Timeout"},
+	{ SRB_STATUS_MESSAGE_REJECTED,	"Message Rejected"},
+	{ SRB_STATUS_BUS_RESET,		"Bus Reset"},
+	{ SRB_STATUS_PARITY_ERROR,	"Parity Error"},
+	{ SRB_STATUS_REQUEST_SENSE_FAILED,"Request Sense Failed"},
+	{ SRB_STATUS_NO_HBA,		"No HBA"},
+	{ SRB_STATUS_DATA_OVERRUN,	"Data Overrun/Data Underrun"},
+	{ SRB_STATUS_UNEXPECTED_BUS_FREE,"Unexpected Bus Free"},
+	{ SRB_STATUS_PHASE_SEQUENCE_FAILURE,"Phase Error"},
+	{ SRB_STATUS_BAD_SRB_BLOCK_LENGTH,"Bad Srb Block Length"},
+	{ SRB_STATUS_REQUEST_FLUSHED,	"Request Flushed"},
+	{ SRB_STATUS_DELAYED_RETRY,	"Delayed Retry"},
+	{ SRB_STATUS_INVALID_LUN,	"Invalid LUN"}, 
+	{ SRB_STATUS_INVALID_TARGET_ID,	"Invalid TARGET ID"},
+	{ SRB_STATUS_BAD_FUNCTION,	"Bad Function"},
+	{ SRB_STATUS_ERROR_RECOVERY,	"Error Recovery"},
+	{ SRB_STATUS_NOT_STARTED,	"Not Started"},
+	{ SRB_STATUS_NOT_IN_USE,	"Not In Use"},
+    	{ SRB_STATUS_FORCE_ABORT,	"Force Abort"},
+	{ SRB_STATUS_DOMAIN_VALIDATION_FAIL,"Domain Validation Failure"},
+	{ 0xff,				"Unknown Error"}
+};
+
+char *aac_get_status_string(u32 status)
+{
+	int i;
+
+	for(i=0; i < (sizeof(srb_status_info)/sizeof(struct aac_srb_status_info)); i++ ){
+		if(srb_status_info[i].status == status){
+			return srb_status_info[i].str;
+		}
+	}
+
+	return "Bad Status Code";
+}
+
+#endif
diff -Nru a/drivers/scsi/aacraid/aacraid.h b/drivers/scsi/aacraid/aacraid.h
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/scsi/aacraid/aacraid.h	Mon Oct  7 11:25:40 2002
@@ -0,0 +1,1400 @@
+#define dprintk(x)
+/*#define dprintk(x) printk x */
+
+/*------------------------------------------------------------------------------
+ *              D E F I N E S
+ *----------------------------------------------------------------------------*/
+
+#define MAXIMUM_NUM_CONTAINERS	31
+#define MAXIMUM_NUM_ADAPTERS	8
+
+#define AAC_NUM_FIB	578
+#define AAC_NUM_IO_FIB	512
+
+#define AAC_MAX_TARGET (MAXIMUM_NUM_CONTAINERS+1)
+//#define AAC_MAX_TARGET 	(16)
+#define AAC_MAX_LUN	(8)
+
+/*
+ * These macros convert from physical channels to virtual channels
+ */
+#define CONTAINER_CHANNEL	(0)
+#define aac_phys_to_logical(x)  (x+1)
+#define aac_logical_to_phys(x)  (x?x-1:0)
+
+#define AAC_DETAILED_STATUS_INFO
+
+struct diskparm
+{
+	int heads;
+	int sectors;
+	int cylinders;
+};
+
+
+/*
+ *	DON'T CHANGE THE ORDER, this is set by the firmware
+ */
+ 
+#define		CT_NONE			0
+#define		CT_VOLUME		1
+#define		CT_MIRROR		2
+#define		CT_STRIPE		3
+#define		CT_RAID5		4
+#define		CT_SSRW			5
+#define		CT_SSRO			6
+#define		CT_MORPH		7
+#define		CT_PASSTHRU		8
+#define		CT_RAID4		9
+#define		CT_RAID10		10	/* stripe of mirror */
+#define		CT_RAID00		11	/* stripe of stripe */
+#define		CT_VOLUME_OF_MIRRORS	12	/* volume of mirror */
+#define		CT_PSEUDO_RAID		13	/* really raid4 */
+#define		CT_LAST_VOLUME_TYPE	14
+
+/*
+ *	Types of objects addressable in some fashion by the client.
+ *	This is a superset of those objects handled just by the filesystem
+ *	and includes "raw" objects that an administrator would use to
+ *	configure containers and filesystems.
+ */
+
+#define		FT_REG		1	/* regular file */
+#define		FT_DIR		2	/* directory */
+#define		FT_BLK		3	/* "block" device - reserved */
+#define		FT_CHR		4	/* "character special" device - reserved */
+#define		FT_LNK		5	/* symbolic link */
+#define		FT_SOCK		6	/* socket */
+#define		FT_FIFO		7	/* fifo */
+#define		FT_FILESYS	8	/* ADAPTEC's "FSA"(tm) filesystem */
+#define		FT_DRIVE	9	/* physical disk - addressable in scsi by bus/target/lun */
+#define		FT_SLICE	10	/* virtual disk - raw volume - slice */
+#define		FT_PARTITION	11	/* FSA partition - carved out of a slice - building block for containers */
+#define		FT_VOLUME	12	/* Container - Volume Set */
+#define		FT_STRIPE	13	/* Container - Stripe Set */
+#define		FT_MIRROR	14	/* Container - Mirror Set */
+#define		FT_RAID5	15	/* Container - Raid 5 Set */
+#define		FT_DATABASE	16	/* Storage object with "foreign" content manager */
+
+/*
+ *	Host side memory scatter gather list
+ *	Used by the adapter for read, write, and readdirplus operations
+ *	We have seperate 32 and 64 bit version because even
+ *	on 64 bit systems not all cards support the 64 bit version
+ */
+struct sgentry {
+	u32	addr;	/* 32-bit address. */
+	u32	count;	/* Length. */
+};
+
+struct sgentry64 {
+	u32	addr[2];	/* 64-bit addr. 2 pieces for data alignment */
+	u32	count;	/* Length. */
+};
+
+/*
+ *	SGMAP
+ *
+ *	This is the SGMAP structure for all commands that use
+ *	32-bit addressing.
+ */
+
+struct sgmap {
+	u32		count;
+	struct sgentry	sg[1]; 
+};
+
+struct sgmap64 {
+	u32		count;
+	struct sgentry64 sg[1];
+};
+
+struct creation_info
+{
+	u8 		buildnum;		/* e.g., 588 */
+	u8 		usec;			/* e.g., 588 */
+	u8	 	via;			/* e.g., 1 = FSU,
+						 * 	 2 = API
+						 */
+	u8	 	year;		 	/* e.g., 1997 = 97 */
+	u32		date;			/*
+						 * unsigned 	Month		:4;	// 1 - 12
+						 * unsigned 	Day		:6;	// 1 - 32
+						 * unsigned 	Hour		:6;	// 0 - 23
+						 * unsigned 	Minute		:6;	// 0 - 60
+						 * unsigned 	Second		:6;	// 0 - 60
+						 */
+	u32		serial[2];			/* e.g., 0x1DEADB0BFAFAF001 */
+};
+
+
+/*
+ *	Define all the constants needed for the communication interface
+ */
+
+/*
+ *	Define how many queue entries each queue will have and the total
+ *	number of entries for the entire communication interface. Also define
+ *	how many queues we support.
+ *
+ *	This has to match the controller
+ */
+
+#define NUMBER_OF_COMM_QUEUES  8   // 4 command; 4 response
+#define HOST_HIGH_CMD_ENTRIES  4
+#define HOST_NORM_CMD_ENTRIES  8
+#define ADAP_HIGH_CMD_ENTRIES  4
+#define ADAP_NORM_CMD_ENTRIES  512
+#define HOST_HIGH_RESP_ENTRIES 4
+#define HOST_NORM_RESP_ENTRIES 512
+#define ADAP_HIGH_RESP_ENTRIES 4
+#define ADAP_NORM_RESP_ENTRIES 8
+
+#define TOTAL_QUEUE_ENTRIES  \
+    (HOST_NORM_CMD_ENTRIES + HOST_HIGH_CMD_ENTRIES + ADAP_NORM_CMD_ENTRIES + ADAP_HIGH_CMD_ENTRIES + \
+	    HOST_NORM_RESP_ENTRIES + HOST_HIGH_RESP_ENTRIES + ADAP_NORM_RESP_ENTRIES + ADAP_HIGH_RESP_ENTRIES)
+
+
+/*
+ *	Set the queues on a 16 byte alignment
+ */
+ 
+#define QUEUE_ALIGNMENT		16
+
+/*
+ *	The queue headers define the Communication Region queues. These
+ *	are physically contiguous and accessible by both the adapter and the
+ *	host. Even though all queue headers are in the same contiguous block
+ *	they will be represented as individual units in the data structures.
+ */
+
+struct aac_entry {
+	u32 size;          /* Size in bytes of Fib which this QE points to */
+	u32 addr; /* Receiver address of the FIB */
+};
+
+/*
+ *	The adapter assumes the ProducerIndex and ConsumerIndex are grouped
+ *	adjacently and in that order.
+ */
+ 
+struct aac_qhdr {
+	u64 header_addr;		/* Address to hand the adapter to access to this queue head */
+	u32 *producer;			/* The producer index for this queue (host address) */
+	u32 *consumer;			/* The consumer index for this queue (host address) */
+};
+
+/*
+ *	Define all the events which the adapter would like to notify
+ *	the host of.
+ */
+ 
+#define		HostNormCmdQue		1	/* Change in host normal priority command queue */
+#define		HostHighCmdQue		2	/* Change in host high priority command queue */
+#define		HostNormRespQue		3	/* Change in host normal priority response queue */
+#define		HostHighRespQue		4	/* Change in host high priority response queue */
+#define		AdapNormRespNotFull	5
+#define		AdapHighRespNotFull	6
+#define		AdapNormCmdNotFull	7
+#define		AdapHighCmdNotFull	8
+#define		SynchCommandComplete	9
+#define		AdapInternalError	0xfe    /* The adapter detected an internal error shutting down */
+
+/*
+ *	Define all the events the host wishes to notify the
+ *	adapter of. The first four values much match the Qid the
+ *	corresponding queue.
+ */
+
+#define		AdapNormCmdQue		2
+#define		AdapHighCmdQue		3
+#define		AdapNormRespQue		6
+#define		AdapHighRespQue		7
+#define		HostShutdown		8
+#define		HostPowerFail		9
+#define		FatalCommError		10
+#define		HostNormRespNotFull	11
+#define		HostHighRespNotFull	12
+#define		HostNormCmdNotFull	13
+#define		HostHighCmdNotFull	14
+#define		FastIo			15
+#define		AdapPrintfDone		16
+
+/*
+ *	Define all the queues that the adapter and host use to communicate
+ *	Number them to match the physical queue layout.
+ */
+
+enum aac_queue_types {
+        HostNormCmdQueue = 0,	/* Adapter to host normal priority command traffic */
+        HostHighCmdQueue,	/* Adapter to host high priority command traffic */
+        AdapNormCmdQueue,	/* Host to adapter normal priority command traffic */
+        AdapHighCmdQueue,	/* Host to adapter high priority command traffic */
+        HostNormRespQueue,	/* Adapter to host normal priority response traffic */
+        HostHighRespQueue,	/* Adapter to host high priority response traffic */
+        AdapNormRespQueue,	/* Host to adapter normal priority response traffic */
+        AdapHighRespQueue	/* Host to adapter high priority response traffic */
+};
+
+/*
+ *	Assign type values to the FSA communication data structures
+ */
+
+#define		FIB_MAGIC	0x0001
+
+/*
+ *	Define the priority levels the FSA communication routines support.
+ */
+
+#define		FsaNormal	1
+#define		FsaHigh		2
+
+/*
+ * Define the FIB. The FIB is the where all the requested data and
+ * command information are put to the application on the FSA adapter.
+ */
+
+struct aac_fibhdr {
+	u32 XferState;			// Current transfer state for this CCB
+	u16 Command;			// Routing information for the destination
+	u8 StructType;			// Type FIB
+	u8 Flags;			// Flags for FIB
+	u16 Size;			// Size of this FIB in bytes
+	u16 SenderSize;			// Size of the FIB in the sender (for response sizing)
+	u32 SenderFibAddress;		// Host defined data in the FIB
+	u32 ReceiverFibAddress;		// Logical address of this FIB for the adapter
+	u32 SenderData;			// Place holder for the sender to store data
+	union {
+		struct {
+		    u32 _ReceiverTimeStart; 	// Timestamp for receipt of fib
+		    u32 _ReceiverTimeDone;	// Timestamp for completion of fib
+		} _s;
+		struct list_head _FibLinks;	// Used to link Adapter Initiated Fibs on the host
+	} _u;
+};
+
+#define FibLinks			_u._FibLinks
+
+#define FIB_DATA_SIZE_IN_BYTES (512 - sizeof(struct aac_fibhdr))
+
+
+struct hw_fib {
+	struct aac_fibhdr header;
+	u8 data[FIB_DATA_SIZE_IN_BYTES];		// Command specific data
+};
+
+/*
+ *	FIB commands
+ */
+
+#define 	TestCommandResponse		1
+#define		TestAdapterCommand		2
+/*
+ *	Lowlevel and comm commands
+ */
+#define		LastTestCommand			100
+#define		ReinitHostNormCommandQueue	101
+#define		ReinitHostHighCommandQueue	102
+#define		ReinitHostHighRespQueue		103
+#define		ReinitHostNormRespQueue		104
+#define		ReinitAdapNormCommandQueue	105
+#define		ReinitAdapHighCommandQueue	107
+#define		ReinitAdapHighRespQueue		108
+#define		ReinitAdapNormRespQueue		109
+#define		InterfaceShutdown		110
+#define		DmaCommandFib			120
+#define		StartProfile			121
+#define		TermProfile			122
+#define		SpeedTest			123
+#define		TakeABreakPt			124
+#define		RequestPerfData			125
+#define		SetInterruptDefTimer		126
+#define		SetInterruptDefCount		127
+#define		GetInterruptDefStatus		128
+#define		LastCommCommand			129
+/*
+ *	Filesystem commands
+ */
+#define		NuFileSystem			300
+#define		UFS				301
+#define		HostFileSystem			302
+#define		LastFileSystemCommand		303
+/*
+ *	Container Commands
+ */
+#define		ContainerCommand		500
+#define		ContainerCommand64		501
+/*
+ *	Cluster Commands
+ */
+#define		ClusterCommand	 		550
+/*
+ *	Scsi Port commands (scsi passthrough)
+ */
+#define		ScsiPortCommand			600
+#define		ScsiPortCommand64		601
+/*
+ *	Misc house keeping and generic adapter initiated commands
+ */
+#define		AifRequest			700
+#define		CheckRevision			701
+#define		FsaHostShutdown			702
+#define		RequestAdapterInfo		703
+#define		IsAdapterPaused			704
+#define		SendHostTime			705
+#define		LastMiscCommand			706
+
+//
+// Commands that will target the failover level on the FSA adapter
+//
+
+enum fib_xfer_state {
+	HostOwned 			= (1<<0),
+	AdapterOwned 			= (1<<1),
+	FibInitialized 			= (1<<2),
+	FibEmpty 			= (1<<3),
+	AllocatedFromPool 		= (1<<4),
+	SentFromHost 			= (1<<5),
+	SentFromAdapter 		= (1<<6),
+	ResponseExpected 		= (1<<7),
+	NoResponseExpected 		= (1<<8),
+	AdapterProcessed 		= (1<<9),
+	HostProcessed 			= (1<<10),
+	HighPriority 			= (1<<11),
+	NormalPriority 			= (1<<12),
+	Async				= (1<<13),
+	AsyncIo				= (1<<13),	// rpbfix: remove with new regime
+	PageFileIo			= (1<<14),	// rpbfix: remove with new regime
+	ShutdownRequest			= (1<<15),
+	LazyWrite			= (1<<16),	// rpbfix: remove with new regime
+	AdapterMicroFib			= (1<<17),
+	BIOSFibPath			= (1<<18),
+	FastResponseCapable		= (1<<19),
+	ApiFib				= (1<<20)	// Its an API Fib.
+};
+
+/*
+ *	The following defines needs to be updated any time there is an
+ *	incompatible change made to the aac_init structure.
+ */
+
+#define ADAPTER_INIT_STRUCT_REVISION		3
+
+struct aac_init
+{
+	u32	InitStructRevision;
+	u32	MiniPortRevision;
+	u32	fsrev;
+	u32	CommHeaderAddress;
+	u32	FastIoCommAreaAddress;
+	u32	AdapterFibsPhysicalAddress;
+	u32	AdapterFibsVirtualAddress;
+	u32	AdapterFibsSize;
+	u32	AdapterFibAlign;
+	u32	printfbuf;
+	u32	printfbufsiz;
+	u32	HostPhysMemPages;		// number of 4k pages of host physical memory
+	u32	HostElapsedSeconds;		// number of seconds since 1970.
+};
+
+enum aac_log_level {
+	LOG_INIT			= 10,
+	LOG_INFORMATIONAL		= 20,
+	LOG_WARNING			= 30,
+	LOG_LOW_ERROR			= 40,
+	LOG_MEDIUM_ERROR		= 50,
+	LOG_HIGH_ERROR			= 60,
+	LOG_PANIC			= 70,
+	LOG_DEBUG			= 80,
+	LOG_WINDBG_PRINT		= 90
+};
+
+#define FSAFS_NTC_GET_ADAPTER_FIB_CONTEXT	0x030b
+#define FSAFS_NTC_FIB_CONTEXT			0x030c
+
+struct aac_dev;
+
+struct adapter_ops
+{
+	void (*adapter_interrupt)(struct aac_dev *dev);
+	void (*adapter_notify)(struct aac_dev *dev, u32 event);
+	void (*adapter_enable_int)(struct aac_dev *dev, u32 event);
+	void (*adapter_disable_int)(struct aac_dev *dev, u32 event);
+	int  (*adapter_sync_cmd)(struct aac_dev *dev, u32 command, u32 p1, u32 *status);
+};
+
+/*
+ *	Define which interrupt handler needs to be installed
+ */
+
+struct aac_driver_ident
+{
+	u16	vendor;
+	u16	device;
+	u16	subsystem_vendor;
+	u16	subsystem_device;
+	int 	(*init)(struct aac_dev *dev, unsigned long num);
+	char *	name;
+	char *	vname;
+	char *	model;
+	u16	channels;
+};
+
+/*
+ *	The adapter interface specs all queues to be located in the same
+ *	physically contigous block. The host structure that defines the
+ *	commuication queues will assume they are each a seperate physically
+ *	contigous memory region that will support them all being one big
+ *	contigous block. 
+ *	There is a command and response queue for each level and direction of
+ *	commuication. These regions are accessed by both the host and adapter.
+ */
+ 
+struct aac_queue {
+	u64		 	logical;		/* This is the address we give the adapter */
+	struct aac_entry	*base;		   	/* This is the system virtual address */
+	struct aac_qhdr 	headers;       		/* A pointer to the producer and consumer queue headers for this queue */
+	u32	 		entries;	   	/* Number of queue entries on this queue */
+	wait_queue_head_t	qfull;		      	/* Event to wait on if the queue is full */
+	wait_queue_head_t	cmdready;	  	/* Indicates there is a Command ready from the adapter on this queue. */
+                                        		/* This is only valid for adapter to host command queues. */                      
+	spinlock_t	 	*lock;		     	/* Spinlock for this queue must take this lock before accessing the lock */
+	spinlock_t		lockdata;		/* Actual lock (used only on one side of the lock) */
+	unsigned long		SavedIrql;      	/* Previous IRQL when the spin lock is taken */
+	u32			padding;		/* Padding - FIXME - can remove I believe */
+	struct list_head 	cmdq;		   	/* A queue of FIBs which need to be prcessed by the FS thread. This is */
+                                		        /* only valid for command queues which receive entries from the adapter. */
+	struct list_head	pendingq;		/* A queue of outstanding fib's to the adapter. */
+	unsigned long		numpending;		/* Number of entries on outstanding queue. */
+	struct aac_dev *	dev;			/* Back pointer to adapter structure */
+};
+
+/*
+ *	Message queues. The order here is important, see also the 
+ *	queue type ordering
+ */
+
+struct aac_queue_block
+{
+	struct aac_queue queue[8];
+};
+
+/*
+ *	SaP1 Message Unit Registers
+ */
+ 
+struct sa_drawbridge_CSR {
+						//	 Offset |	Name
+	u32	reserved[10];			//	00h-27h |   Reserved
+	u8	LUT_Offset;			//	28h	|	Looup Table Offset
+	u8	reserved1[3];			// 	29h-2bh	|	Reserved
+	u32	LUT_Data;			//	2ch	|	Looup Table Data	
+	u32	reserved2[26];			//	30h-97h	|	Reserved
+	u16	PRICLEARIRQ;			//	98h	|	Primary Clear Irq
+	u16	SECCLEARIRQ;			//	9ah	|	Secondary Clear Irq
+	u16	PRISETIRQ;			//	9ch	|	Primary Set Irq
+	u16	SECSETIRQ;			//	9eh	|	Secondary Set Irq
+	u16	PRICLEARIRQMASK;		//	a0h	|	Primary Clear Irq Mask
+	u16	SECCLEARIRQMASK;		//	a2h	|	Secondary Clear Irq Mask
+	u16	PRISETIRQMASK;			//	a4h	|	Primary Set Irq Mask
+	u16	SECSETIRQMASK;			//	a6h	|	Secondary Set Irq Mask
+	u32	MAILBOX0;			//	a8h	|	Scratchpad 0
+	u32	MAILBOX1;			//	ach	|	Scratchpad 1
+	u32	MAILBOX2;			//	b0h	|	Scratchpad 2
+	u32	MAILBOX3;			//	b4h	|	Scratchpad 3
+	u32	MAILBOX4;			//	b8h	|	Scratchpad 4
+	u32	MAILBOX5;			//	bch	|	Scratchpad 5
+	u32	MAILBOX6;			//	c0h	|	Scratchpad 6
+	u32	MAILBOX7;			//	c4h	|	Scratchpad 7
+
+	u32	ROM_Setup_Data;			//	c8h | 	Rom Setup and Data
+	u32	ROM_Control_Addr;		//	cch | 	Rom Control and Address
+
+	u32	reserved3[12];			//	d0h-ffh	| 	reserved
+	u32	LUT[64];			// 100h-1ffh|	Lookup Table Entries
+
+	//
+	//  TO DO
+	//	need to add DMA, I2O, UART, etc registers form 80h to 364h
+	//
+
+};
+
+#define Mailbox0	SaDbCSR.MAILBOX0
+#define Mailbox1	SaDbCSR.MAILBOX1
+#define Mailbox2	SaDbCSR.MAILBOX2
+#define Mailbox3	SaDbCSR.MAILBOX3
+#define Mailbox4	SaDbCSR.MAILBOX4
+#define Mailbox5	SaDbCSR.MAILBOX5
+#define Mailbox7	SaDbCSR.MAILBOX7
+	
+#define DoorbellReg_p SaDbCSR.PRISETIRQ
+#define DoorbellReg_s SaDbCSR.SECSETIRQ
+#define DoorbellClrReg_p SaDbCSR.PRICLEARIRQ
+
+
+#define	DOORBELL_0	cpu_to_le16(0x0001)
+#define DOORBELL_1	cpu_to_le16(0x0002)
+#define DOORBELL_2	cpu_to_le16(0x0004)
+#define DOORBELL_3	cpu_to_le16(0x0008)
+#define DOORBELL_4	cpu_to_le16(0x0010)
+#define DOORBELL_5	cpu_to_le16(0x0020)
+#define DOORBELL_6	cpu_to_le16(0x0040)
+
+	
+#define PrintfReady	DOORBELL_5
+#define PrintfDone	DOORBELL_5
+	
+struct sa_registers {
+	struct sa_drawbridge_CSR	SaDbCSR;			/* 98h - c4h */
+};
+	
+
+#define Sa_MINIPORT_REVISION			1
+
+#define sa_readw(AEP, CSR)		readl(&((AEP)->regs.sa->CSR))
+#define sa_readl(AEP,  CSR)		readl(&((AEP)->regs.sa->CSR))
+#define sa_writew(AEP, CSR, value)	writew(value, &((AEP)->regs.sa->CSR))
+#define sa_writel(AEP, CSR, value)	writel(value, &((AEP)->regs.sa->CSR))
+
+/*
+ *	Rx Message Unit Registers
+ */
+
+struct rx_mu_registers {
+						//	 Local	|   PCI*	|	Name
+						//			|		|
+	u32	ARSR;				//	1300h	|	00h	|	APIC Register Select Register
+	u32	reserved0;			//	1304h	|	04h	|	Reserved
+	u32	AWR;				//	1308h	|	08h	|	APIC Window Register
+	u32	reserved1;			//	130Ch	|	0Ch	|	Reserved
+	u32	IMRx[2];			//	1310h	|	10h	|	Inbound Message Registers
+	u32	OMRx[2];			//	1318h	|	18h	|	Outbound Message Registers
+	u32	IDR;				//	1320h	|	20h	|	Inbound Doorbell Register
+	u32	IISR;				//	1324h	|	24h	|	Inbound Interrupt Status Register
+	u32	IIMR;				//	1328h	|	28h	|	Inbound Interrupt Mask Register
+	u32	ODR;				//	132Ch	|	2Ch	|	Outbound Doorbell Register
+	u32	OISR;				//	1330h	|	30h	|	Outbound Interrupt Status Register
+	u32	OIMR;				//	1334h	|	34h	|	Outbound Interrupt Mask Register
+						// * Must access through ATU Inbound Translation Window
+};
+
+struct rx_inbound {
+	u32	Mailbox[8];
+};
+
+#define	InboundMailbox0		IndexRegs.Mailbox[0]
+#define	InboundMailbox1		IndexRegs.Mailbox[1]
+#define	InboundMailbox2		IndexRegs.Mailbox[2]
+#define	InboundMailbox3		IndexRegs.Mailbox[3]
+#define	InboundMailbox4		IndexRegs.Mailbox[4]
+
+#define	INBOUNDDOORBELL_0	cpu_to_le32(0x00000001)
+#define INBOUNDDOORBELL_1	cpu_to_le32(0x00000002)
+#define INBOUNDDOORBELL_2	cpu_to_le32(0x00000004)
+#define INBOUNDDOORBELL_3	cpu_to_le32(0x00000008)
+#define INBOUNDDOORBELL_4	cpu_to_le32(0x00000010)
+#define INBOUNDDOORBELL_5	cpu_to_le32(0x00000020)
+#define INBOUNDDOORBELL_6	cpu_to_le32(0x00000040)
+
+#define	OUTBOUNDDOORBELL_0	cpu_to_le32(0x00000001)
+#define OUTBOUNDDOORBELL_1	cpu_to_le32(0x00000002)
+#define OUTBOUNDDOORBELL_2	cpu_to_le32(0x00000004)
+#define OUTBOUNDDOORBELL_3	cpu_to_le32(0x00000008)
+#define OUTBOUNDDOORBELL_4	cpu_to_le32(0x00000010)
+
+#define InboundDoorbellReg	MUnit.IDR
+#define OutboundDoorbellReg	MUnit.ODR
+
+struct rx_registers {
+	struct rx_mu_registers		MUnit;		// 1300h - 1334h
+	u32				reserved1[6];	// 1338h - 134ch
+	struct rx_inbound		IndexRegs;
+};
+
+#define rx_readb(AEP, CSR)		readb(&((AEP)->regs.rx->CSR))
+#define rx_readl(AEP, CSR)		readl(&((AEP)->regs.rx->CSR))
+#define rx_writeb(AEP, CSR, value)	writeb(value, &((AEP)->regs.rx->CSR))
+#define rx_writel(AEP, CSR, value)	writel(value, &((AEP)->regs.rx->CSR))
+
+struct fib;
+
+typedef void (*fib_callback)(void *ctxt, struct fib *fibctx);
+
+struct aac_fib_context {
+	s16	 		type;		// used for verification of structure	
+	s16	 		size;
+	ulong			jiffies;	// used for cleanup - dmb changed to ulong
+	struct list_head	next;		// used to link context's into a linked list
+	struct semaphore 	wait_sem;	// this is used to wait for the next fib to arrive.
+	int			wait;		// Set to true when thread is in WaitForSingleObject
+	unsigned long		count;		// total number of FIBs on FibList
+	struct list_head	fibs;
+};
+
+struct fsa_scsi_hba {
+	u32		size[MAXIMUM_NUM_CONTAINERS];
+	u32		type[MAXIMUM_NUM_CONTAINERS];
+	u8		valid[MAXIMUM_NUM_CONTAINERS];
+	u8		ro[MAXIMUM_NUM_CONTAINERS];
+	u8		locked[MAXIMUM_NUM_CONTAINERS];
+	u8		deleted[MAXIMUM_NUM_CONTAINERS];
+	u32		devno[MAXIMUM_NUM_CONTAINERS];
+};
+
+struct fib {
+	void			*next;	/* this is used by the allocator */
+	s16			type;
+	s16			size;
+	/*
+	 *	The Adapter that this I/O is destined for.
+	 */
+	struct aac_dev 		*dev;
+	u64			logicaladdr;	/* 64 bit */
+	/*
+	 *	This is the event the sendfib routine will wait on if the
+	 *	caller did not pass one and this is synch io.
+	 */
+	struct semaphore 	event_wait;
+	spinlock_t		event_lock;
+
+	u32			done;	/* gets set to 1 when fib is complete */
+	fib_callback 		callback;
+	void 			*callback_data;
+	u32			flags; // u32 dmb was ulong
+	/*
+	 *	The following is used to put this fib context onto the 
+	 *	Outstanding I/O queue.
+	 */
+	struct list_head	queue;
+
+	void 			*data;
+	struct hw_fib		*fib;		/* Actual shared object */
+};
+
+/*
+ *	Adapter Information Block
+ *
+ *	This is returned by the RequestAdapterInfo block
+ */
+ 
+struct aac_adapter_info
+{
+	u32	platform;
+	u32	cpu;
+	u32	subcpu;
+	u32	clock;
+	u32	execmem;
+	u32	buffermem;
+	u32	totalmem;
+	u32	kernelrev;
+	u32	kernelbuild;
+	u32	monitorrev;
+	u32	monitorbuild;
+	u32	hwrev;
+	u32	hwbuild;
+	u32	biosrev;
+	u32	biosbuild;
+	u32	cluster;
+	u32	serial[2];
+	u32	battery;
+	u32	options;
+	u32	OEM;
+};
+
+/*
+ * Battery platforms
+ */
+#define AAC_BAT_REQ_PRESENT	(1)
+#define AAC_BAT_REQ_NOTPRESENT	(2)
+#define AAC_BAT_OPT_PRESENT	(3)
+#define AAC_BAT_OPT_NOTPRESENT	(4)
+#define AAC_BAT_NOT_SUPPORTED	(5)
+/*
+ * cpu types
+ */
+#define AAC_CPU_SIMULATOR	(1)
+#define AAC_CPU_I960		(2)
+#define AAC_CPU_STRONGARM	(3)
+
+/*
+ * Supported Options
+ */
+#define AAC_OPT_SNAPSHOT	cpu_to_le32(1)
+#define AAC_OPT_CLUSTERS	cpu_to_le32(1<<1)
+#define AAC_OPT_WRITE_CACHE	cpu_to_le32(1<<2)
+#define AAC_OPT_64BIT_DATA	cpu_to_le32(1<<3)
+#define AAC_OPT_HOST_TIME_FIB	cpu_to_le32(1<<4)
+#define AAC_OPT_RAID50		cpu_to_le32(1<<5)
+#define AAC_OPT_4GB_WINDOW	cpu_to_le32(1<<6)
+#define AAC_OPT_SCSI_UPGRADEABLE cpu_to_le32(1<<7)
+#define AAC_OPT_SOFT_ERR_REPORT	cpu_to_le32(1<<8)
+#define AAC_OPT_SUPPORTED_RECONDITION cpu_to_le32(1<<9)
+#define AAC_OPT_SGMAP_HOST64	cpu_to_le32(1<<10)
+#define AAC_OPT_ALARM		cpu_to_le32(1<<11)
+#define AAC_OPT_NONDASD		cpu_to_le32(1<<12)
+
+struct aac_dev
+{
+	struct aac_dev		*next;
+	const char		*name;
+	int			id;
+
+	u16			irq_mask;
+	/*
+	 *	Map for 128 fib objects (64k)
+	 */	
+	dma_addr_t		hw_fib_pa;
+	struct hw_fib		*hw_fib_va;
+#if BITS_PER_LONG >= 64
+	ulong			fib_base_va;
+#endif
+	/*
+	 *	Fib Headers
+	 */
+	struct fib		fibs[AAC_NUM_FIB];
+	struct fib		*free_fib;
+	struct fib		*timeout_fib;
+	spinlock_t		fib_lock;
+	
+	struct aac_queue_block *queues;
+	/*
+	 *	The user API will use an IOCTL to register itself to receive
+	 *	FIBs from the adapter.  The following list is used to keep
+	 *	track of all the threads that have requested these FIBs.  The
+	 *	mutex is used to synchronize access to all data associated 
+	 *	with the adapter fibs.
+	 */
+	struct list_head	fib_list;
+
+	struct adapter_ops	a_ops;
+	unsigned long		fsrev;		/* Main driver's revision number */
+	
+	struct aac_init		*init;		/* Holds initialization info to communicate with adapter */
+	dma_addr_t		init_pa; 	/* Holds physical address of the init struct */
+	
+	struct pci_dev		*pdev;		/* Our PCI interface */
+	void *			printfbuf;	/* pointer to buffer used for printf's from the adapter */
+	void *			comm_addr;	/* Base address of Comm area */
+	dma_addr_t		comm_phys;	/* Physical Address of Comm area */
+	size_t			comm_size;
+
+	struct Scsi_Host	*scsi_host_ptr;
+	struct fsa_scsi_hba	fsa_dev;
+	int			thread_pid;
+	int			cardtype;
+	
+	/*
+	 *	The following is the device specific extension.
+	 */
+	union
+	{
+		struct sa_registers *sa;
+		struct rx_registers *rx;
+	} regs;
+	/*
+	 *	The following is the number of the individual adapter
+	 */
+	u32			devnum;
+	u32			aif_thread;
+	struct completion	aif_completion;
+	struct aac_adapter_info adapter_info;
+	/* These are in adapter info but they are in the io flow so
+	 * lets break them out so we don't have to do an AND to check them
+	 */
+	u8			nondasd_support; 
+	u8			pae_support;
+};
+
+#define AllocateAndMapFibSpace(dev, MapFibContext) \
+	dev->a_ops.AllocateAndMapFibSpace(dev, MapFibContext)
+
+#define UnmapAndFreeFibSpace(dev, MapFibContext) \
+	dev->a_ops.UnmapAndFreeFibSpace(dev, MapFibContext)
+
+#define aac_adapter_interrupt(dev) \
+	dev->a_ops.adapter_interrupt(dev)
+
+#define aac_adapter_notify(dev, event) \
+	dev->a_ops.adapter_notify(dev, event)
+
+#define aac_adapter_enable_int(dev, event) \
+	dev->a_ops.adapter_enable_int(dev, event)
+
+#define aac_adapter_disable_int(dev, event) \
+	dev->a_ops.adapter_disable_int(dev, event)
+
+
+
+#define FIB_CONTEXT_FLAG_TIMED_OUT		(0x00000001)
+
+/*
+ *	Define the command values
+ */
+ 
+#define		Null			0
+#define 	GetAttributes		1
+#define 	SetAttributes		2
+#define 	Lookup			3
+#define 	ReadLink		4
+#define 	Read			5
+#define 	Write			6
+#define		Create			7
+#define		MakeDirectory		8
+#define		SymbolicLink		9
+#define		MakeNode		10
+#define		Removex			11
+#define		RemoveDirectoryx	12
+#define		Rename			13
+#define		Link			14
+#define		ReadDirectory		15
+#define		ReadDirectoryPlus	16
+#define		FileSystemStatus	17
+#define		FileSystemInfo		18
+#define		PathConfigure		19
+#define		Commit			20
+#define		Mount			21
+#define		UnMount			22
+#define		Newfs			23
+#define		FsCheck			24
+#define		FsSync			25
+#define		SimReadWrite		26
+#define		SetFileSystemStatus	27
+#define		BlockRead		28
+#define		BlockWrite		29
+#define		NvramIoctl		30
+#define		FsSyncWait		31
+#define		ClearArchiveBit		32
+#define		SetAcl			33
+#define		GetAcl			34
+#define		AssignAcl		35
+#define		FaultInsertion		36	/* Fault Insertion Command */
+#define		CrazyCache		37	/* Crazycache */
+
+#define		MAX_FSACOMMAND_NUM	38
+
+
+/*
+ *	Define the status returns. These are very unixlike although
+ *	most are not in fact used
+ */
+
+#define		ST_OK		0
+#define		ST_PERM		1
+#define		ST_NOENT	2
+#define		ST_IO		5
+#define		ST_NXIO		6
+#define		ST_E2BIG	7
+#define		ST_ACCES	13
+#define		ST_EXIST	17
+#define		ST_XDEV		18
+#define		ST_NODEV	19
+#define		ST_NOTDIR	20
+#define		ST_ISDIR	21
+#define		ST_INVAL	22
+#define		ST_FBIG		27
+#define		ST_NOSPC	28
+#define		ST_ROFS		30
+#define		ST_MLINK	31
+#define		ST_WOULDBLOCK	35
+#define		ST_NAMETOOLONG	63
+#define		ST_NOTEMPTY	66
+#define		ST_DQUOT	69
+#define		ST_STALE	70
+#define		ST_REMOTE	71
+#define		ST_BADHANDLE	10001
+#define		ST_NOT_SYNC	10002
+#define		ST_BAD_COOKIE	10003
+#define		ST_NOTSUPP	10004
+#define		ST_TOOSMALL	10005
+#define		ST_SERVERFAULT	10006
+#define		ST_BADTYPE	10007
+#define		ST_JUKEBOX	10008
+#define		ST_NOTMOUNTED	10009
+#define		ST_MAINTMODE	10010
+#define		ST_STALEACL	10011
+
+/*
+ *	On writes how does the client want the data written.
+ */
+
+#define	CACHE_CSTABLE		1
+#define CACHE_UNSTABLE		2
+
+/*
+ *	Lets the client know at which level the data was commited on
+ *	a write request
+ */
+
+#define	CMFILE_SYNCH_NVRAM	1
+#define	CMDATA_SYNCH_NVRAM	2
+#define	CMFILE_SYNCH		3
+#define CMDATA_SYNCH		4
+#define CMUNSTABLE		5
+
+struct aac_read
+{
+	u32	 	command;
+	u32 		cid;
+	u32 		block;
+	u32 		count;
+	struct sgmap	sg;	// Must be last in struct because it is variable
+};
+
+struct aac_read64
+{
+	u32	 	command;
+	u16 		cid;
+	u16 		sector_count;
+	u32 		block;
+	u16		pad;
+	u16		flags;
+	struct sgmap64	sg;	// Must be last in struct because it is variable
+};
+
+struct aac_read_reply
+{
+	u32	 	status;
+	u32 		count;
+};
+
+struct aac_write
+{
+	u32		command;
+	u32 		cid;
+	u32 		block;
+	u32 		count;
+	u32	 	stable;	// Not used
+	struct sgmap	sg;	// Must be last in struct because it is variable
+};
+
+struct aac_write64
+{
+	u32	 	command;
+	u16 		cid;
+	u16 		sector_count;
+	u32 		block;
+	u16		pad;
+	u16		flags;
+	struct sgmap64	sg;	// Must be last in struct because it is variable
+};
+struct aac_write_reply
+{
+	u32		status;
+	u32 		count;
+	u32		committed;
+};
+
+struct aac_srb
+{
+	u32		function;
+	u32		channel;
+	u32		target;
+	u32		lun;
+	u32		timeout;
+	u32		flags;
+	u32		count;		// Data xfer size
+	u32		retry_limit;
+	u32		cdb_size;
+	u8		cdb[16];
+	struct	sgmap	sg;
+};
+
+
+
+#define		AAC_SENSE_BUFFERSIZE	 30
+
+struct aac_srb_reply
+{
+	u32		status;
+	u32		srb_status;
+	u32		scsi_status;
+	u32		data_xfer_length;
+	u32		sense_data_size;
+	u8		sense_data[AAC_SENSE_BUFFERSIZE]; // Can this be SCSI_SENSE_BUFFERSIZE
+};
+/*
+ * SRB Flags
+ */
+#define		SRB_NoDataXfer		 0x0000
+#define		SRB_DisableDisconnect	 0x0004
+#define		SRB_DisableSynchTransfer 0x0008
+#define 	SRB_BypassFrozenQueue	 0x0010
+#define		SRB_DisableAutosense	 0x0020
+#define		SRB_DataIn		 0x0040
+#define 	SRB_DataOut		 0x0080
+
+/*
+ * SRB Functions - set in aac_srb->function
+ */
+#define	SRBF_ExecuteScsi	0x0000
+#define	SRBF_ClaimDevice	0x0001
+#define	SRBF_IO_Control		0x0002
+#define	SRBF_ReceiveEvent	0x0003
+#define	SRBF_ReleaseQueue	0x0004
+#define	SRBF_AttachDevice	0x0005
+#define	SRBF_ReleaseDevice	0x0006
+#define	SRBF_Shutdown		0x0007
+#define	SRBF_Flush		0x0008
+#define	SRBF_AbortCommand	0x0010
+#define	SRBF_ReleaseRecovery	0x0011
+#define	SRBF_ResetBus		0x0012
+#define	SRBF_ResetDevice	0x0013
+#define	SRBF_TerminateIO	0x0014
+#define	SRBF_FlushQueue		0x0015
+#define	SRBF_RemoveDevice	0x0016
+#define	SRBF_DomainValidation	0x0017
+
+/* 
+ * SRB SCSI Status - set in aac_srb->scsi_status
+ */
+#define SRB_STATUS_PENDING                  0x00
+#define SRB_STATUS_SUCCESS                  0x01
+#define SRB_STATUS_ABORTED                  0x02
+#define SRB_STATUS_ABORT_FAILED             0x03
+#define SRB_STATUS_ERROR                    0x04
+#define SRB_STATUS_BUSY                     0x05
+#define SRB_STATUS_INVALID_REQUEST          0x06
+#define SRB_STATUS_INVALID_PATH_ID          0x07
+#define SRB_STATUS_NO_DEVICE                0x08
+#define SRB_STATUS_TIMEOUT                  0x09
+#define SRB_STATUS_SELECTION_TIMEOUT        0x0A
+#define SRB_STATUS_COMMAND_TIMEOUT          0x0B
+#define SRB_STATUS_MESSAGE_REJECTED         0x0D
+#define SRB_STATUS_BUS_RESET                0x0E
+#define SRB_STATUS_PARITY_ERROR             0x0F
+#define SRB_STATUS_REQUEST_SENSE_FAILED     0x10
+#define SRB_STATUS_NO_HBA                   0x11
+#define SRB_STATUS_DATA_OVERRUN             0x12
+#define SRB_STATUS_UNEXPECTED_BUS_FREE      0x13
+#define SRB_STATUS_PHASE_SEQUENCE_FAILURE   0x14
+#define SRB_STATUS_BAD_SRB_BLOCK_LENGTH     0x15
+#define SRB_STATUS_REQUEST_FLUSHED          0x16
+#define SRB_STATUS_DELAYED_RETRY	    0x17
+#define SRB_STATUS_INVALID_LUN              0x20
+#define SRB_STATUS_INVALID_TARGET_ID        0x21
+#define SRB_STATUS_BAD_FUNCTION             0x22
+#define SRB_STATUS_ERROR_RECOVERY           0x23
+#define SRB_STATUS_NOT_STARTED		    0x24
+#define SRB_STATUS_NOT_IN_USE		    0x30
+#define SRB_STATUS_FORCE_ABORT		    0x31
+#define SRB_STATUS_DOMAIN_VALIDATION_FAIL   0x32
+
+/*
+ * Object-Server / Volume-Manager Dispatch Classes
+ */
+
+#define		VM_Null			0
+#define		VM_NameServe		1
+#define		VM_ContainerConfig	2
+#define		VM_Ioctl		3
+#define		VM_FilesystemIoctl	4
+#define		VM_CloseAll		5
+#define		VM_CtBlockRead		6
+#define		VM_CtBlockWrite		7
+#define		VM_SliceBlockRead	8	/* raw access to configured "storage objects" */
+#define		VM_SliceBlockWrite	9
+#define		VM_DriveBlockRead	10	/* raw access to physical devices */
+#define		VM_DriveBlockWrite	11
+#define		VM_EnclosureMgt		12	/* enclosure management */
+#define		VM_Unused		13	/* used to be diskset management */
+#define		VM_CtBlockVerify	14
+#define		VM_CtPerf		15	/* performance test */
+#define		VM_CtBlockRead64	16
+#define		VM_CtBlockWrite64	17
+#define		VM_CtBlockVerify64	18
+#define		VM_CtHostRead64		19
+#define		VM_CtHostWrite64	20
+
+#define		MAX_VMCOMMAND_NUM	21	/* used for sizing stats array - leave last */
+
+/*
+ *	Descriptive information (eg, vital stats)
+ *	that a content manager might report.  The
+ *	FileArray filesystem component is one example
+ *	of a content manager.  Raw mode might be
+ *	another.
+ */
+
+struct aac_fsinfo {
+	u32  fsTotalSize;	/* Consumed by fs, incl. metadata */
+	u32  fsBlockSize;
+	u32  fsFragSize;
+	u32  fsMaxExtendSize;
+	u32  fsSpaceUnits;
+	u32  fsMaxNumFiles;
+	u32  fsNumFreeFiles;
+	u32  fsInodeDensity;
+};	/* valid iff ObjType == FT_FILESYS && !(ContentState & FSCS_NOTCLEAN) */
+
+union aac_contentinfo {
+	struct aac_fsinfo filesys;	/* valid iff ObjType == FT_FILESYS && !(ContentState & FSCS_NOTCLEAN) */
+};
+
+/*
+ *	Query for "mountable" objects, ie, objects that are typically
+ *	associated with a drive letter on the client (host) side.
+ */
+
+struct aac_mntent {
+	u32    			oid;
+	u8			name[16];	// if applicable
+	struct creation_info	create_info;	// if applicable
+	u32			capacity;
+	u32			vol;    	// substrate structure
+	u32			obj;	        // FT_FILESYS, FT_DATABASE, etc.
+	u32			state;		// unready for mounting, readonly, etc.
+	union aac_contentinfo	fileinfo;	// Info specific to content manager (eg, filesystem)
+	u32			altoid;		// != oid <==> snapshot or broken mirror exists
+};
+
+#define FSCS_READONLY	0x0002	/*	possible result of broken mirror */
+
+struct aac_query_mount {
+	u32		command;
+	u32		type;
+	u32		count;
+};
+
+struct aac_mount {
+	u32		status;
+	u32	   	type;           /* should be same as that requested */
+	u32		count;
+	struct aac_mntent mnt[1];
+};
+
+/*
+ * The following command is sent to shut down each container.
+ */
+
+struct aac_close {
+	u32	command;
+	u32	cid;
+};
+
+struct aac_query_disk
+{
+	s32	cnum;
+	s32	bus;
+	s32	target;
+	s32	lun;
+	u32	valid;
+	u32	locked;
+	u32	deleted;
+	s32	instance;
+	s8	name[10];
+	u32	unmapped;
+};
+
+struct aac_delete_disk {
+	u32	disknum;
+	u32	cnum;
+};
+
+struct fib_ioctl
+{
+	char	*fibctx;
+	int	wait;
+	char	*fib;
+};
+
+struct revision
+{
+	u32 compat;
+	u32 version;
+	u32 build;
+};
+	
+/*
+ * 	Ugly - non Linux like ioctl coding for back compat.
+ */
+
+#define CTL_CODE(function, method) (                 \
+    (4<< 16) | ((function) << 2) | (method) \
+)
+
+/*
+ *	Define the method codes for how buffers are passed for I/O and FS 
+ *	controls
+ */
+
+#define METHOD_BUFFERED                 0
+#define METHOD_NEITHER                  3
+
+/*
+ *	Filesystem ioctls
+ */
+
+#define FSACTL_SENDFIB                  	CTL_CODE(2050, METHOD_BUFFERED)
+#define FSACTL_SEND_RAW_SRB               	CTL_CODE(2067, METHOD_BUFFERED)
+#define FSACTL_DELETE_DISK			0x163
+#define FSACTL_QUERY_DISK			0x173
+#define FSACTL_OPEN_GET_ADAPTER_FIB		CTL_CODE(2100, METHOD_BUFFERED)
+#define FSACTL_GET_NEXT_ADAPTER_FIB		CTL_CODE(2101, METHOD_BUFFERED)
+#define FSACTL_CLOSE_GET_ADAPTER_FIB		CTL_CODE(2102, METHOD_BUFFERED)
+#define FSACTL_MINIPORT_REV_CHECK               CTL_CODE(2107, METHOD_BUFFERED)
+#define FSACTL_GET_PCI_INFO               	CTL_CODE(2119, METHOD_BUFFERED)
+#define FSACTL_FORCE_DELETE_DISK		CTL_CODE(2120, METHOD_NEITHER)
+
+
+struct aac_common
+{
+	/*
+	 *	If this value is set to 1 then interrupt moderation will occur 
+	 *	in the base commuication support.
+	 */
+	u32 irq_mod;
+	u32 peak_fibs;
+	u32 zero_fibs;
+	u32 fib_timeouts;
+	/*
+	 *	Statistical counters in debug mode
+	 */
+#ifdef DBG
+	u32 FibsSent;
+	u32 FibRecved;
+	u32 NoResponseSent;
+	u32 NoResponseRecved;
+	u32 AsyncSent;
+	u32 AsyncRecved;
+	u32 NormalSent;
+	u32 NormalRecved;
+#endif
+};
+
+extern struct aac_common aac_config;
+
+
+/*
+ *	The following macro is used when sending and receiving FIBs. It is
+ *	only used for debugging.
+ */
+ 
+#if DBG
+#define	FIB_COUNTER_INCREMENT(counter)		(counter)++
+#else
+#define	FIB_COUNTER_INCREMENT(counter)		
+#endif
+
+/*
+ *	Adapter direct commands
+ *	Monitor/Kernel API
+ */
+
+#define	BREAKPOINT_REQUEST		cpu_to_le32(0x00000004)
+#define	INIT_STRUCT_BASE_ADDRESS	cpu_to_le32(0x00000005)
+#define READ_PERMANENT_PARAMETERS	cpu_to_le32(0x0000000a)
+#define WRITE_PERMANENT_PARAMETERS	cpu_to_le32(0x0000000b)
+#define HOST_CRASHING			cpu_to_le32(0x0000000d)
+#define	SEND_SYNCHRONOUS_FIB		cpu_to_le32(0x0000000c)
+#define GET_ADAPTER_PROPERTIES		cpu_to_le32(0x00000019)
+#define RE_INIT_ADAPTER			cpu_to_le32(0x000000ee)
+
+/*
+ *	Adapter Status Register
+ *
+ *  Phase Staus mailbox is 32bits:
+ *	<31:16> = Phase Status
+ *	<15:0>  = Phase
+ *
+ *	The adapter reports is present state through the phase.  Only
+ *	a single phase should be ever be set.  Each phase can have multiple
+ *	phase status bits to provide more detailed information about the 
+ *	state of the board.  Care should be taken to ensure that any phase 
+ *	status bits that are set when changing the phase are also valid
+ *	for the new phase or be cleared out.  Adapter software (monitor,
+ *	iflash, kernel) is responsible for properly maintining the phase 
+ *	status mailbox when it is running.
+ *											
+ *	MONKER_API Phases							
+ *
+ *	Phases are bit oriented.  It is NOT valid  to have multiple bits set						
+ */					
+
+#define	SELF_TEST_FAILED		cpu_to_le32(0x00000004)
+#define	KERNEL_UP_AND_RUNNING		cpu_to_le32(0x00000080)
+#define	KERNEL_PANIC			cpu_to_le32(0x00000100)
+
+/*
+ *	Doorbell bit defines
+ */
+
+#define DoorBellPrintfDone		cpu_to_le32(1<<5)	// Host -> Adapter
+#define DoorBellAdapterNormCmdReady	cpu_to_le32(1<<1)	// Adapter -> Host
+#define DoorBellAdapterNormRespReady	cpu_to_le32(1<<2)	// Adapter -> Host
+#define DoorBellAdapterNormCmdNotFull	cpu_to_le32(1<<3)	// Adapter -> Host
+#define DoorBellAdapterNormRespNotFull	cpu_to_le32(1<<4)	// Adapter -> Host
+#define DoorBellPrintfReady		cpu_to_le32(1<<5)	// Adapter -> Host
+
+/*
+ *	For FIB communication, we need all of the following things
+ *	to send back to the user.
+ */
+ 
+#define 	AifCmdEventNotify	1	/* Notify of event */
+#define		AifCmdJobProgress	2	/* Progress report */
+#define		AifCmdAPIReport		3	/* Report from other user of API */
+#define		AifCmdDriverNotify	4	/* Notify host driver of event */
+#define		AifReqJobList		100	/* Gets back complete job list */
+#define		AifReqJobsForCtr	101	/* Gets back jobs for specific container */
+#define		AifReqJobsForScsi	102	/* Gets back jobs for specific SCSI device */ 
+#define		AifReqJobReport		103	/* Gets back a specific job report or list of them */ 
+#define		AifReqTerminateJob	104	/* Terminates job */
+#define		AifReqSuspendJob	105	/* Suspends a job */
+#define		AifReqResumeJob		106	/* Resumes a job */ 
+#define		AifReqSendAPIReport	107	/* API generic report requests */
+#define		AifReqAPIJobStart	108	/* Start a job from the API */
+#define		AifReqAPIJobUpdate	109	/* Update a job report from the API */
+#define		AifReqAPIJobFinish	110	/* Finish a job from the API */
+
+/*
+ *	Adapter Initiated FIB command structures. Start with the adapter
+ *	initiated FIBs that really come from the adapter, and get responded
+ *	to by the host.
+ */
+
+struct aac_aifcmd {
+	u32 command;		/* Tell host what type of notify this is */
+	u32 seqnum;		/* To allow ordering of reports (if necessary) */
+	u8 data[1];		/* Undefined length (from kernel viewpoint) */
+};
+
+static inline u32 fib2addr(struct hw_fib *hw)
+{
+	return (u32)hw;
+}
+
+static inline struct hw_fib *addr2fib(u32 addr)
+{
+	return (struct hw_fib *)addr;
+}
+
+const char *aac_driverinfo(struct Scsi_Host *);
+struct fib *fib_alloc(struct aac_dev *dev);
+int fib_setup(struct aac_dev *dev);
+void fib_map_free(struct aac_dev *dev);
+void fib_free(struct fib * context);
+void fib_init(struct fib * context);
+void fib_dealloc(struct fib * context);
+void aac_printf(struct aac_dev *dev, u32 val);
+int fib_send(u16 command, struct fib * context, unsigned long size, int priority, int wait, int reply, fib_callback callback, void *ctxt);
+int aac_consumer_get(struct aac_dev * dev, struct aac_queue * q, struct aac_entry **entry);
+int aac_consumer_avail(struct aac_dev * dev, struct aac_queue * q);
+void aac_consumer_free(struct aac_dev * dev, struct aac_queue * q, u32 qnum);
+int fib_complete(struct fib * context);
+#define fib_data(fibctx) ((void *)(fibctx)->fib->data)
+int aac_detach(struct aac_dev *dev);
+struct aac_dev *aac_init_adapter(struct aac_dev *dev);
+int aac_get_containers(struct aac_dev *dev);
+int aac_scsi_cmd(Scsi_Cmnd *scsi_cmnd_ptr);
+int aac_dev_ioctl(struct aac_dev *dev, int cmd, void *arg);
+int aac_do_ioctl(struct aac_dev * dev, int cmd, void *arg);
+int aac_rx_init(struct aac_dev *dev, unsigned long devNumber);
+int aac_sa_init(struct aac_dev *dev, unsigned long devNumber);
+unsigned int aac_response_normal(struct aac_queue * q);
+unsigned int aac_command_normal(struct aac_queue * q);
+int aac_command_thread(struct aac_dev * dev);
+int aac_close_fib_context(struct aac_dev * dev, struct aac_fib_context *fibctx);
+int fib_adapter_complete(struct fib * fibptr, unsigned short size);
+struct aac_driver_ident* aac_get_driver_ident(int devtype);
+int aac_get_adapter_info(struct aac_dev* dev);
diff -Nru a/drivers/scsi/aacraid/commctrl.c b/drivers/scsi/aacraid/commctrl.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/scsi/aacraid/commctrl.c	Mon Oct  7 11:25:41 2002
@@ -0,0 +1,432 @@
+/*
+ *	Adaptec AAC series RAID controller driver
+ *	(c) Copyright 2001 Red Hat Inc.	<alan@redhat.com>
+ *
+ * based on the old aacraid driver that is..
+ * Adaptec aacraid device driver for Linux.
+ *
+ * Copyright (c) 2000 Adaptec, Inc. (aacraid@adaptec.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, 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; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Module Name:
+ *  commctrl.c
+ *
+ * Abstract: Contains all routines for control of the AFA comm layer
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/pci.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/completion.h>
+#include <linux/blk.h>
+#include <asm/semaphore.h>
+#include <asm/uaccess.h>
+#include "scsi.h"
+#include "hosts.h"
+
+#include "aacraid.h"
+
+/**
+ *	ioctl_send_fib	-	send a FIB from userspace
+ *	@dev:	adapter is being processed
+ *	@arg:	arguments to the ioctl call
+ *	
+ *	This routine sends a fib to the adapter on behalf of a user level
+ *	program.
+ */
+ 
+static int ioctl_send_fib(struct aac_dev * dev, void *arg)
+{
+	struct hw_fib * kfib;
+	struct fib *fibptr;
+
+	fibptr = fib_alloc(dev);
+	if(fibptr == NULL)
+		return -ENOMEM;
+		
+	kfib = fibptr->fib;
+	/*
+	 *	First copy in the header so that we can check the size field.
+	 */
+	if (copy_from_user((void *)kfib, arg, sizeof(struct aac_fibhdr))) {
+		fib_free(fibptr);
+		return -EFAULT;
+	}
+	/*
+	 *	Since we copy based on the fib header size, make sure that we
+	 *	will not overrun the buffer when we copy the memory. Return
+	 *	an error if we would.
+	 */
+	if(le32_to_cpu(kfib->header.Size) > sizeof(struct hw_fib) - sizeof(struct aac_fibhdr)) {
+		fib_free(fibptr);
+		return -EINVAL;
+	}
+
+	if (copy_from_user((void *) kfib, arg, le32_to_cpu(kfib->header.Size) + sizeof(struct aac_fibhdr))) {
+		fib_free(fibptr);
+		return -EFAULT;
+	}
+
+	if (kfib->header.Command == cpu_to_le32(TakeABreakPt)) {
+		aac_adapter_interrupt(dev);
+		/*
+		 * Since we didn't really send a fib, zero out the state to allow 
+		 * cleanup code not to assert.
+		 */
+		kfib->header.XferState = 0;
+	} else {
+		if (fib_send(kfib->header.Command, fibptr, le32_to_cpu(kfib->header.Size) , FsaNormal,
+			1, 1, NULL, NULL) != 0) 
+		{
+			fib_free(fibptr);
+			return -EINVAL;
+		}
+		if (fib_complete(fibptr) != 0) {
+			fib_free(fibptr);
+			return -EINVAL;
+		}
+	}
+	/*
+	 *	Make sure that the size returned by the adapter (which includes
+	 *	the header) is less than or equal to the size of a fib, so we
+	 *	don't corrupt application data. Then copy that size to the user
+	 *	buffer. (Don't try to add the header information again, since it
+	 *	was already included by the adapter.)
+	 */
+
+	if (copy_to_user(arg, (void *)kfib, kfib->header.Size)) {
+		fib_free(fibptr);
+		return -EFAULT;
+	}
+	fib_free(fibptr);
+	return 0;
+}
+
+/**
+ *	open_getadapter_fib	-	Get the next fib
+ *
+ *	This routine will get the next Fib, if available, from the AdapterFibContext
+ *	passed in from the user.
+ */
+
+static int open_getadapter_fib(struct aac_dev * dev, void *arg)
+{
+	struct aac_fib_context * fibctx;
+	int status;
+	unsigned long flags;
+
+	fibctx = kmalloc(sizeof(struct aac_fib_context), GFP_KERNEL);
+	if (fibctx == NULL) {
+		status = -ENOMEM;
+	} else {
+		fibctx->type = FSAFS_NTC_GET_ADAPTER_FIB_CONTEXT;
+		fibctx->size = sizeof(struct aac_fib_context);
+		/*
+		 *	Initialize the mutex used to wait for the next AIF.
+		 */
+		init_MUTEX_LOCKED(&fibctx->wait_sem);
+		fibctx->wait = 0;
+		/*
+		 *	Initialize the fibs and set the count of fibs on
+		 *	the list to 0.
+		 */
+		fibctx->count = 0;
+		INIT_LIST_HEAD(&fibctx->fibs);
+		fibctx->jiffies = jiffies/HZ;
+		/*
+		 *	Now add this context onto the adapter's 
+		 *	AdapterFibContext list.
+		 */
+		spin_lock_irqsave(&dev->fib_lock, flags);
+		list_add_tail(&fibctx->next, &dev->fib_list);
+		spin_unlock_irqrestore(&dev->fib_lock, flags);
+		if (copy_to_user(arg,  &fibctx, sizeof(struct aac_fib_context *))) {
+			status = -EFAULT;
+		} else {
+			status = 0;
+		}	
+	}
+	return status;
+}
+
+/**
+ *	next_getadapter_fib	-	get the next fib
+ *	@dev: adapter to use
+ *	@arg: ioctl argument
+ *	
+ * 	This routine will get the next Fib, if available, from the AdapterFibContext
+ *	passed in from the user.
+ */
+
+static int next_getadapter_fib(struct aac_dev * dev, void *arg)
+{
+	struct fib_ioctl f;
+	struct aac_fib_context *fibctx, *aifcp;
+	struct hw_fib * fib;
+	int status;
+	struct list_head * entry;
+	int found;
+	unsigned long flags;
+	
+	if(copy_from_user((void *)&f, arg, sizeof(struct fib_ioctl)))
+		return -EFAULT;
+	/*
+	 *	Extract the AdapterFibContext from the Input parameters.
+	 */
+	fibctx = (struct aac_fib_context *) f.fibctx;
+
+	/*
+	 *	Verify that the HANDLE passed in was a valid AdapterFibContext
+	 *
+	 *	Search the list of AdapterFibContext addresses on the adapter
+	 *	to be sure this is a valid address
+	 */
+	found = 0;
+	entry = dev->fib_list.next;
+
+	while(entry != &dev->fib_list) {
+		aifcp = list_entry(entry, struct aac_fib_context, next);
+		if(fibctx == aifcp) {   /* We found a winner */
+			found = 1;
+			break;
+		}
+		entry = entry->next;
+	}
+	if (found == 0)
+		return -EINVAL;
+
+	if((fibctx->type != FSAFS_NTC_GET_ADAPTER_FIB_CONTEXT) ||
+		 (fibctx->size != sizeof(struct aac_fib_context)))
+		return -EINVAL;
+	status = 0;
+	spin_lock_irqsave(&dev->fib_lock, flags);
+	/*
+	 *	If there are no fibs to send back, then either wait or return
+	 *	-EAGAIN
+	 */
+return_fib:
+	if (!list_empty(&fibctx->fibs)) {
+		struct list_head * entry;
+		/*
+		 *	Pull the next fib from the fibs
+		 */
+		entry = fibctx->fibs.next;
+		list_del(entry);
+		
+		fib = list_entry(entry, struct hw_fib, header.FibLinks);
+		fibctx->count--;
+		spin_unlock_irqrestore(&dev->fib_lock, flags);
+		if (copy_to_user(f.fib, fib, sizeof(struct hw_fib))) {
+			kfree(fib);
+			return -EFAULT;
+		}	
+		/*
+		 *	Free the space occupied by this copy of the fib.
+		 */
+		kfree(fib);
+		status = 0;
+		fibctx->jiffies = jiffies/HZ;
+	} else {
+		spin_unlock_irqrestore(&dev->fib_lock, flags);
+		if (f.wait) {
+			if(down_interruptible(&fibctx->wait_sem) < 0) {
+				status = -EINTR;
+			} else {
+				/* Lock again and retry */
+				spin_lock_irqsave(&dev->fib_lock, flags);
+				goto return_fib;
+			}
+		} else {
+			status = -EAGAIN;
+		}	
+	}
+	return status;
+}
+
+int aac_close_fib_context(struct aac_dev * dev, struct aac_fib_context * fibctx)
+{
+	struct hw_fib *fib;
+
+	/*
+	 *	First free any FIBs that have not been consumed.
+	 */
+	while (!list_empty(&fibctx->fibs)) {
+		struct list_head * entry;
+		/*
+		 *	Pull the next fib from the fibs
+		 */
+		entry = fibctx->fibs.next;
+		list_del(entry);
+		fib = list_entry(entry, struct hw_fib, header.FibLinks);
+		fibctx->count--;
+		/*
+		 *	Free the space occupied by this copy of the fib.
+		 */
+		kfree(fib);
+	}
+	/*
+	 *	Remove the Context from the AdapterFibContext List
+	 */
+	list_del(&fibctx->next);
+	/*
+	 *	Invalidate context
+	 */
+	fibctx->type = 0;
+	/*
+	 *	Free the space occupied by the Context
+	 */
+	kfree(fibctx);
+	return 0;
+}
+
+/**
+ *	close_getadapter_fib	-	close down user fib context
+ *	@dev: adapter
+ *	@arg: ioctl arguments
+ *
+ *	This routine will close down the fibctx passed in from the user.
+ */
+ 
+static int close_getadapter_fib(struct aac_dev * dev, void *arg)
+{
+	struct aac_fib_context *fibctx, *aifcp;
+	int status;
+	unsigned long flags;
+	struct list_head * entry;
+	int found;
+
+	/*
+	 *	Extract the fibctx from the input parameters
+	 */
+	fibctx = arg;
+
+	/*
+	 *	Verify that the HANDLE passed in was a valid AdapterFibContext
+	 *
+	 *	Search the list of AdapterFibContext addresses on the adapter
+	 *	to be sure this is a valid address
+	 */
+
+	found = 0;
+	entry = dev->fib_list.next;
+
+	while(entry != &dev->fib_list) {
+		aifcp = list_entry(entry, struct aac_fib_context, next);
+		if(fibctx == aifcp) {   /* We found a winner */
+			found = 1;
+			break;
+		}
+		entry = entry->next;
+	}
+
+	if(found == 0)
+		return 0; /* Already gone */
+
+	if((fibctx->type != FSAFS_NTC_GET_ADAPTER_FIB_CONTEXT) ||
+		 (fibctx->size != sizeof(struct aac_fib_context)))
+		return -EINVAL;
+	spin_lock_irqsave(&dev->fib_lock, flags);
+	status = aac_close_fib_context(dev, fibctx);
+	spin_unlock_irqrestore(&dev->fib_lock, flags);
+	return status;
+}
+
+/**
+ *	check_revision	-	close down user fib context
+ *	@dev: adapter
+ *	@arg: ioctl arguments
+ *
+ *	This routine returns the firmware version.
+ *      Under Linux, there have been no version incompatibilities, so this is simple!
+ */
+
+static int check_revision(struct aac_dev *dev, void *arg)
+{
+	struct revision response;
+
+	response.compat = 1;
+	response.version = dev->adapter_info.kernelrev;
+	response.build = dev->adapter_info.kernelbuild;
+
+	if (copy_to_user(arg, &response, sizeof(response)))
+		return -EFAULT;
+	return 0;
+}
+
+
+struct aac_pci_info {
+        u32 bus;
+        u32 slot;
+};
+
+
+int aac_get_pci_info(struct aac_dev* dev, void* arg)
+{
+        struct aac_pci_info pci_info;
+
+	pci_info.bus = dev->pdev->bus->number;
+	pci_info.slot = PCI_SLOT(dev->pdev->devfn);
+
+       if(copy_to_user( arg, (void*)&pci_info, sizeof(struct aac_pci_info)))
+               return -EFAULT;
+        return 0;
+ }
+ 
+
+int aac_do_ioctl(struct aac_dev * dev, int cmd, void *arg)
+{
+	int status;
+	
+	/*
+	 *	HBA gets first crack
+	 */
+	 
+	status = aac_dev_ioctl(dev, cmd, arg);
+	if(status != -ENOTTY)
+		return status;
+
+	switch (cmd) {
+	case FSACTL_MINIPORT_REV_CHECK:
+		status = check_revision(dev, arg);
+		break;
+	case FSACTL_SENDFIB:
+		status = ioctl_send_fib(dev, arg);
+		break;
+	case FSACTL_OPEN_GET_ADAPTER_FIB:
+		status = open_getadapter_fib(dev, arg);
+		break;
+	case FSACTL_GET_NEXT_ADAPTER_FIB:
+		status = next_getadapter_fib(dev, arg);
+		break;
+	case FSACTL_CLOSE_GET_ADAPTER_FIB:
+		status = close_getadapter_fib(dev, arg);
+		break;
+	case FSACTL_GET_PCI_INFO:
+		status = aac_get_pci_info(dev,arg);
+		break;
+	default:
+		status = -ENOTTY;
+	  	break;	
+	}
+	return status;
+}
+
diff -Nru a/drivers/scsi/aacraid/comminit.c b/drivers/scsi/aacraid/comminit.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/scsi/aacraid/comminit.c	Mon Oct  7 11:25:40 2002
@@ -0,0 +1,341 @@
+/*
+ *	Adaptec AAC series RAID controller driver
+ *	(c) Copyright 2001 Red Hat Inc.	<alan@redhat.com>
+ *
+ * based on the old aacraid driver that is..
+ * Adaptec aacraid device driver for Linux.
+ *
+ * Copyright (c) 2000 Adaptec, Inc. (aacraid@adaptec.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, 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; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Module Name:
+ *  comminit.c
+ *
+ * Abstract: This supports the initialization of the host adapter commuication interface.
+ *    This is a platform dependent module for the pci cyclone board.
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/pci.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/blk.h>
+#include <linux/completion.h>
+#include <asm/semaphore.h>
+#include "scsi.h"
+#include "hosts.h"
+
+#include "aacraid.h"
+
+struct aac_common aac_config;
+
+static struct aac_dev *devices;
+
+static int aac_alloc_comm(struct aac_dev *dev, void **commaddr, unsigned long commsize, unsigned long commalign)
+{
+	unsigned char *base;
+	unsigned long size, align;
+	unsigned long fibsize = 4096;
+	unsigned long printfbufsiz = 256;
+	struct aac_init *init;
+	dma_addr_t phys;
+
+	/* FIXME: Adaptec add 128 bytes to this value - WHY ?? */
+	size = fibsize + sizeof(struct aac_init) + commsize + commalign + printfbufsiz;
+
+	base = pci_alloc_consistent(dev->pdev, size, &phys);
+	if(base == NULL)
+	{
+		printk(KERN_ERR "aacraid: unable to create mapping.\n");
+		return 0;
+	}
+	dev->comm_addr = (void *)base;
+	dev->comm_phys = phys;
+	dev->comm_size = size;
+
+	dev->init = (struct aac_init *)(base + fibsize);
+	dev->init_pa = phys + fibsize;
+
+	/*
+	 *	Cache the upper bits of the virtual mapping for 64bit boxes
+	 *	FIXME: this crap should be rewritten
+	 */
+#if BITS_PER_LONG >= 64 
+	dev->fib_base_va = ((ulong)base & 0xffffffff00000000);
+#endif
+
+	init = dev->init;
+
+	init->InitStructRevision = cpu_to_le32(ADAPTER_INIT_STRUCT_REVISION);
+	init->MiniPortRevision = cpu_to_le32(Sa_MINIPORT_REVISION);
+	init->fsrev = cpu_to_le32(dev->fsrev);
+
+	/*
+	 *	Adapter Fibs are the first thing allocated so that they
+	 *	start page aligned
+	 */
+	init->AdapterFibsVirtualAddress = cpu_to_le32((u32)base);
+	init->AdapterFibsPhysicalAddress = cpu_to_le32(phys);
+	init->AdapterFibsSize = cpu_to_le32(fibsize);
+	init->AdapterFibAlign = cpu_to_le32(sizeof(struct hw_fib));
+
+	/*
+	 * Increment the base address by the amount already used
+	 */
+	base = base + fibsize + sizeof(struct aac_init);
+	phys = phys + fibsize + sizeof(struct aac_init);
+	/*
+	 *	Align the beginning of Headers to commalign
+	 */
+	align = (commalign - ((unsigned long)(base) & (commalign - 1)));
+	base = base + align;
+	phys = phys + align;
+	/*
+	 *	Fill in addresses of the Comm Area Headers and Queues
+	 */
+	*commaddr = (unsigned long *)base;
+	init->CommHeaderAddress = cpu_to_le32(phys);
+	/*
+	 *	Increment the base address by the size of the CommArea
+	 */
+	base = base + commsize;
+	phys = phys + commsize;
+	/*
+	 *	 Place the Printf buffer area after the Fast I/O comm area.
+	 */
+	dev->printfbuf = (void *)base;
+	init->printfbuf = cpu_to_le32(phys);
+	init->printfbufsiz = cpu_to_le32(printfbufsiz);
+	memset(base, 0, printfbufsiz);
+	return 1;
+}
+    
+static void aac_queue_init(struct aac_dev * dev, struct aac_queue * q, u32 *mem, int qsize)
+{
+	q->numpending = 0;
+	q->dev = dev;
+	INIT_LIST_HEAD(&q->pendingq);
+	init_waitqueue_head(&q->cmdready);
+	INIT_LIST_HEAD(&q->cmdq);
+	init_waitqueue_head(&q->qfull);
+	spin_lock_init(&q->lockdata);
+	q->lock = &q->lockdata;
+	q->headers.producer = mem;
+	q->headers.consumer = mem+1;
+	*q->headers.producer = cpu_to_le32(qsize);
+	*q->headers.consumer = cpu_to_le32(qsize);
+	q->entries = qsize;
+}
+
+/**
+ *	aac_send_shutdown		-	shutdown an adapter
+ *	@dev: Adapter to shutdown
+ *
+ *	This routine will send a VM_CloseAll (shutdown) request to the adapter.
+ */
+
+static int aac_send_shutdown(struct aac_dev * dev)
+{
+	struct fib * fibctx;
+	struct aac_close *cmd;
+	int status;
+
+	fibctx = fib_alloc(dev);
+	fib_init(fibctx);
+
+	cmd = (struct aac_close *) fib_data(fibctx);
+
+	cmd->command = cpu_to_le32(VM_CloseAll);
+	cmd->cid = cpu_to_le32(0xffffffff);
+
+	status = fib_send(ContainerCommand,
+			  fibctx,
+			  sizeof(struct aac_close),
+			  FsaNormal,
+			  1, 1,
+			  NULL, NULL);
+
+	if (status == 0)
+		fib_complete(fibctx);
+	fib_free(fibctx);
+	return status;
+}
+
+/**
+ *	aac_detach	-	detach adapter
+ *	@detach: adapter to disconnect
+ *
+ *	Disconnect and shutdown an AAC based adapter, freeing resources
+ *	as we go.
+ */
+
+int aac_detach(struct aac_dev *detach)
+{
+	struct aac_dev **dev = &devices;
+	
+	while(*dev)
+	{
+		if(*dev == detach)
+		{
+			*dev = detach->next;
+			aac_send_shutdown(detach);
+			fib_map_free(detach);
+			pci_free_consistent(detach->pdev, detach->comm_size, detach->comm_addr, detach->comm_phys);
+			kfree(detach->queues);
+			return 1;
+		}
+		dev=&((*dev)->next);
+	}
+	BUG();
+	return 0;
+}
+
+/**
+ *	aac_comm_init	-	Initialise FSA data structures
+ *	@dev:	Adapter to intialise
+ *
+ *	Initializes the data structures that are required for the FSA commuication
+ *	interface to operate. 
+ *	Returns
+ *		1 - if we were able to init the commuication interface.
+ *		0 - If there were errors initing. This is a fatal error.
+ */
+ 
+int aac_comm_init(struct aac_dev * dev)
+{
+	unsigned long hdrsize = (sizeof(u32) * NUMBER_OF_COMM_QUEUES) * 2;
+	unsigned long queuesize = sizeof(struct aac_entry) * TOTAL_QUEUE_ENTRIES;
+	u32 *headers;
+	struct aac_entry * queues;
+	unsigned long size;
+	struct aac_queue_block * comm = dev->queues;
+
+	/*
+	 *	Now allocate and initialize the zone structures used as our 
+	 *	pool of FIB context records.  The size of the zone is based
+	 *	on the system memory size.  We also initialize the mutex used
+	 *	to protect the zone.
+	 */
+	spin_lock_init(&dev->fib_lock);
+
+	/*
+	 *	Allocate the physically contigous space for the commuication
+	 *	queue headers. 
+	 */
+
+	size = hdrsize + queuesize;
+
+	if (!aac_alloc_comm(dev, (void * *)&headers, size, QUEUE_ALIGNMENT))
+		return -ENOMEM;
+
+	queues = (struct aac_entry *)((unsigned char *)headers + hdrsize);
+
+	/* Adapter to Host normal proirity Command queue */ 
+	comm->queue[HostNormCmdQueue].base = queues;
+	aac_queue_init(dev, &comm->queue[HostNormCmdQueue], headers, HOST_NORM_CMD_ENTRIES);
+	queues += HOST_NORM_CMD_ENTRIES;
+	headers += 2;
+
+	/* Adapter to Host high priority command queue */
+	comm->queue[HostHighCmdQueue].base = queues;
+	aac_queue_init(dev, &comm->queue[HostHighCmdQueue], headers, HOST_HIGH_CMD_ENTRIES);
+    
+	queues += HOST_HIGH_CMD_ENTRIES;
+	headers +=2;
+
+	/* Host to adapter normal priority command queue */
+	comm->queue[AdapNormCmdQueue].base = queues;
+	aac_queue_init(dev, &comm->queue[AdapNormCmdQueue], headers, ADAP_NORM_CMD_ENTRIES);
+    
+	queues += ADAP_NORM_CMD_ENTRIES;
+	headers += 2;
+
+	/* host to adapter high priority command queue */
+	comm->queue[AdapHighCmdQueue].base = queues;
+	aac_queue_init(dev, &comm->queue[AdapHighCmdQueue], headers, ADAP_HIGH_CMD_ENTRIES);
+    
+	queues += ADAP_HIGH_CMD_ENTRIES;
+	headers += 2;
+
+	/* adapter to host normal priority response queue */
+	comm->queue[HostNormRespQueue].base = queues;
+	aac_queue_init(dev, &comm->queue[HostNormRespQueue], headers, HOST_NORM_RESP_ENTRIES);
+    
+	queues += HOST_NORM_RESP_ENTRIES;
+	headers += 2;
+
+	/* adapter to host high priority response queue */
+	comm->queue[HostHighRespQueue].base = queues;
+	aac_queue_init(dev, &comm->queue[HostHighRespQueue], headers, HOST_HIGH_RESP_ENTRIES);
+   
+	queues += HOST_HIGH_RESP_ENTRIES;
+	headers += 2;
+
+	/* host to adapter normal priority response queue */
+	comm->queue[AdapNormRespQueue].base = queues;
+	aac_queue_init(dev, &comm->queue[AdapNormRespQueue], headers, ADAP_NORM_RESP_ENTRIES);
+
+	queues += ADAP_NORM_RESP_ENTRIES;
+	headers += 2;
+	
+	/* host to adapter high priority response queue */ 
+	comm->queue[AdapHighRespQueue].base = queues;
+	aac_queue_init(dev, &comm->queue[AdapHighRespQueue], headers, ADAP_HIGH_RESP_ENTRIES);
+
+	comm->queue[AdapNormCmdQueue].lock = comm->queue[HostNormRespQueue].lock;
+	comm->queue[AdapHighCmdQueue].lock = comm->queue[HostHighRespQueue].lock;
+	comm->queue[AdapNormRespQueue].lock = comm->queue[HostNormCmdQueue].lock;
+	comm->queue[AdapHighRespQueue].lock = comm->queue[HostHighCmdQueue].lock;
+
+	return 0;
+}
+
+struct aac_dev *aac_init_adapter(struct aac_dev *dev)
+{
+	/*
+	 *	Ok now init the communication subsystem
+	 */
+	dev->queues = (struct aac_queue_block *) kmalloc(sizeof(struct aac_queue_block), GFP_KERNEL);
+	if (dev->queues == NULL) {
+		printk(KERN_ERR "Error could not allocate comm region.\n");
+		return NULL;
+	}
+	memset(dev->queues, 0, sizeof(struct aac_queue_block));
+
+	if (aac_comm_init(dev)<0)
+		return NULL;
+	/*
+	 *	Initialize the list of fibs
+	 */
+	if(fib_setup(dev)<0)
+		return NULL;
+		
+	INIT_LIST_HEAD(&dev->fib_list);
+	init_completion(&dev->aif_completion);
+	/*
+	 *	Add this adapter in to our dev List.
+	 */
+	dev->next = devices;
+	devices = dev;
+	return dev;
+}
+
+    
diff -Nru a/drivers/scsi/aacraid/commsup.c b/drivers/scsi/aacraid/commsup.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/scsi/aacraid/commsup.c	Mon Oct  7 11:26:06 2002
@@ -0,0 +1,948 @@
+/*
+ *	Adaptec AAC series RAID controller driver
+ *	(c) Copyright 2001 Red Hat Inc.	<alan@redhat.com>
+ *
+ * based on the old aacraid driver that is..
+
+ * Adaptec aacraid device driver for Linux.
+ *
+ * Copyright (c) 2000 Adaptec, Inc. (aacraid@adaptec.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, 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; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Module Name:
+ *  commsup.c
+ *
+ * Abstract: Contain all routines that are required for FSA host/adapter
+ *    commuication.
+ *
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/pci.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/completion.h>
+#include <asm/semaphore.h>
+#include <linux/blk.h>
+#include "scsi.h"
+#include "hosts.h"
+
+#include "aacraid.h"
+
+/**
+ *	fib_map_alloc		-	allocate the fib objects
+ *	@dev: Adapter to allocate for
+ *
+ *	Allocate and map the shared PCI space for the FIB blocks used to
+ *	talk to the Adaptec firmware.
+ */
+ 
+static int fib_map_alloc(struct aac_dev *dev)
+{
+	if((dev->hw_fib_va = pci_alloc_consistent(dev->pdev, sizeof(struct hw_fib) * AAC_NUM_FIB, &dev->hw_fib_pa))==NULL)
+		return -ENOMEM;
+	return 0;
+}
+
+/**
+ *	fib_map_free		-	free the fib objects
+ *	@dev: Adapter to free
+ *
+ *	Free the PCI mappings and the memory allocated for FIB blocks
+ *	on this adapter.
+ */
+
+void fib_map_free(struct aac_dev *dev)
+{
+	pci_free_consistent(dev->pdev, sizeof(struct hw_fib) * AAC_NUM_FIB, dev->hw_fib_va, dev->hw_fib_pa);
+}
+
+/**
+ *	fib_setup	-	setup the fibs
+ *	@dev: Adapter to set up
+ *
+ *	Allocate the PCI space for the fibs, map it and then intialise the
+ *	fib area, the unmapped fib data and also the free list
+ */
+
+int fib_setup(struct aac_dev * dev)
+{
+	struct fib *fibptr;
+	struct hw_fib *fib;
+	dma_addr_t fibpa;
+	int i;
+	
+	if(fib_map_alloc(dev)<0)
+		return -ENOMEM;
+		
+	fib = dev->hw_fib_va;
+	fibpa = dev->hw_fib_pa;
+	memset(fib, 0, sizeof(struct hw_fib) * AAC_NUM_FIB);
+	/*
+	 *	Initialise the fibs
+	 */
+	for (i = 0, fibptr = &dev->fibs[i]; i < AAC_NUM_FIB; i++, fibptr++) 
+	{
+		fibptr->dev = dev;
+		fibptr->fib = fib;
+		fibptr->data = (void *) fibptr->fib->data;
+		fibptr->next = fibptr+1;	/* Forward chain the fibs */
+		init_MUTEX_LOCKED(&fibptr->event_wait);
+		spin_lock_init(&fibptr->event_lock);
+		fib->header.XferState = cpu_to_le32(0xffffffff);
+		fib->header.SenderSize = cpu_to_le16(sizeof(struct hw_fib));
+		fibptr->logicaladdr = (unsigned long) fibpa;
+		fib = (struct hw_fib *)((unsigned char *)fib + sizeof(struct hw_fib));
+		fibpa = fibpa + sizeof(struct hw_fib);
+	}
+	/*
+	 *	Add the fib chain to the free list
+	 */
+	dev->fibs[AAC_NUM_FIB-1].next = NULL;
+	/*
+	 *	Enable this to debug out of queue space
+	 */
+	dev->free_fib = &dev->fibs[0];
+	return 0;
+}
+
+/**
+ *	fib_alloc	-	allocate a fib
+ *	@dev: Adapter to allocate the fib for
+ *
+ *	Allocate a fib from the adapter fib pool. If the pool is empty we
+ *	wait for fibs to become free.
+ */
+ 
+struct fib * fib_alloc(struct aac_dev *dev)
+{
+	struct fib * fibptr;
+	unsigned long flags;
+	
+	spin_lock_irqsave(&dev->fib_lock, flags);
+	fibptr = dev->free_fib;	
+	if(!fibptr)
+		BUG();
+	dev->free_fib = fibptr->next;
+	spin_unlock_irqrestore(&dev->fib_lock, flags);
+	/*
+	 *	Set the proper node type code and node byte size
+	 */
+	fibptr->type = FSAFS_NTC_FIB_CONTEXT;
+	fibptr->size = sizeof(struct fib);
+	/*
+	 *	Null out fields that depend on being zero at the start of
+	 *	each I/O
+	 */
+	fibptr->fib->header.XferState = cpu_to_le32(0);
+	fibptr->callback = NULL;
+	fibptr->callback_data = NULL;
+
+	return fibptr;
+}
+
+/**
+ *	fib_free	-	free a fib
+ *	@fibptr: fib to free up
+ *
+ *	Frees up a fib and places it on the appropriate queue
+ *	(either free or timed out)
+ */
+ 
+void fib_free(struct fib * fibptr)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&fibptr->dev->fib_lock, flags);
+
+	if (fibptr->flags & FIB_CONTEXT_FLAG_TIMED_OUT) {
+		aac_config.fib_timeouts++;
+		fibptr->next = fibptr->dev->timeout_fib;
+		fibptr->dev->timeout_fib = fibptr;
+	} else {
+		if (fibptr->fib->header.XferState != 0) {
+			printk(KERN_WARNING "fib_free, XferState != 0, fibptr = 0x%p, XferState = 0x%x\n", 
+				 (void *)fibptr, fibptr->fib->header.XferState);
+		}
+		fibptr->next = fibptr->dev->free_fib;
+		fibptr->dev->free_fib = fibptr;
+	}	
+	spin_unlock_irqrestore(&fibptr->dev->fib_lock, flags);
+}
+
+/**
+ *	fib_init	-	initialise a fib
+ *	@fibptr: The fib to initialize
+ *	
+ *	Set up the generic fib fields ready for use
+ */
+ 
+void fib_init(struct fib *fibptr)
+{
+	struct hw_fib *fib = fibptr->fib;
+
+	fib->header.StructType = FIB_MAGIC;
+	fib->header.Size = cpu_to_le16(sizeof(struct hw_fib));
+        fib->header.XferState = cpu_to_le32(HostOwned | FibInitialized | FibEmpty | FastResponseCapable);
+	fib->header.SenderFibAddress = cpu_to_le32(0);
+	fib->header.ReceiverFibAddress = cpu_to_le32(0);
+	fib->header.SenderSize = cpu_to_le16(sizeof(struct hw_fib));
+}
+
+/**
+ *	fib_deallocate		-	deallocate a fib
+ *	@fibptr: fib to deallocate
+ *
+ *	Will deallocate and return to the free pool the FIB pointed to by the
+ *	caller.
+ */
+ 
+void fib_dealloc(struct fib * fibptr)
+{
+	struct hw_fib *fib = fibptr->fib;
+	if(fib->header.StructType != FIB_MAGIC) 
+		BUG();
+	fib->header.XferState = cpu_to_le32(0);        
+}
+
+/*
+ *	Commuication primitives define and support the queuing method we use to
+ *	support host to adapter commuication. All queue accesses happen through
+ *	these routines and are the only routines which have a knowledge of the
+ *	 how these queues are implemented.
+ */
+ 
+/**
+ *	aac_get_entry		-	get a queue entry
+ *	@dev: Adapter
+ *	@qid: Queue Number
+ *	@entry: Entry return
+ *	@index: Index return
+ *	@nonotify: notification control
+ *
+ *	With a priority the routine returns a queue entry if the queue has free entries. If the queue
+ *	is full(no free entries) than no entry is returned and the function returns 0 otherwise 1 is
+ *	returned.
+ */
+ 
+static int aac_get_entry (struct aac_dev * dev, u32 qid, struct aac_entry **entry, u32 * index, unsigned long *nonotify)
+{
+	struct aac_queue * q;
+
+	/*
+	 *	All of the queues wrap when they reach the end, so we check
+	 *	to see if they have reached the end and if they have we just
+	 *	set the index back to zero. This is a wrap. You could or off
+	 *	the high bits in all updates but this is a bit faster I think.
+	 */
+
+	q = &dev->queues->queue[qid];
+	
+	*index = le32_to_cpu(*(q->headers.producer));
+		if (*index - 2 == le32_to_cpu(*(q->headers.consumer)))
+			*nonotify = 1; 
+
+	if (qid == AdapHighCmdQueue) {
+	        if (*index >= ADAP_HIGH_CMD_ENTRIES)
+        		*index = 0;
+	} else if (qid == AdapNormCmdQueue) {
+	        if (*index >= ADAP_NORM_CMD_ENTRIES) 
+			*index = 0; /* Wrap to front of the Producer Queue. */
+	}
+	else if (qid == AdapHighRespQueue) 
+	{
+	        if (*index >= ADAP_HIGH_RESP_ENTRIES)
+			*index = 0;
+	}
+	else if (qid == AdapNormRespQueue) 
+	{
+		if (*index >= ADAP_NORM_RESP_ENTRIES) 
+			*index = 0; /* Wrap to front of the Producer Queue. */
+	}
+	else BUG();
+
+        if (*index + 1 == le32_to_cpu(*(q->headers.consumer))) { /* Queue is full */
+		printk(KERN_WARNING "Queue %d full, %ld outstanding.\n", qid, q->numpending);
+		return 0;
+	} else {
+	        *entry = q->base + *index;
+		return 1;
+	}
+}   
+
+/**
+ *	aac_queue_get		-	get the next free QE
+ *	@dev: Adapter
+ *	@index: Returned index
+ *	@priority: Priority of fib
+ *	@fib: Fib to associate with the queue entry
+ *	@wait: Wait if queue full
+ *	@fibptr: Driver fib object to go with fib
+ *	@nonotify: Don't notify the adapter
+ *
+ *	Gets the next free QE off the requested priorty adapter command
+ *	queue and associates the Fib with the QE. The QE represented by
+ *	index is ready to insert on the queue when this routine returns
+ *	success.
+ */
+
+static int aac_queue_get(struct aac_dev * dev, u32 * index, u32 qid, struct hw_fib * fib, int wait, struct fib * fibptr, unsigned long *nonotify)
+{
+	struct aac_entry * entry = NULL;
+	int map = 0;
+	struct aac_queue * q = &dev->queues->queue[qid];
+		
+	spin_lock_irqsave(q->lock, q->SavedIrql);
+	    
+	if (qid == AdapHighCmdQueue || qid == AdapNormCmdQueue) 
+	{
+		/*  if no entries wait for some if caller wants to */
+        	while (!aac_get_entry(dev, qid, &entry, index, nonotify)) 
+        	{
+			printk(KERN_ERR "GetEntries failed\n");
+		}
+	        /*
+	         *	Setup queue entry with a command, status and fib mapped
+	         */
+	        entry->size = cpu_to_le32(le16_to_cpu(fib->header.Size));
+	        map = 1;
+	}
+	else if (qid == AdapHighRespQueue || qid == AdapNormRespQueue)
+	{
+	        while(!aac_get_entry(dev, qid, &entry, index, nonotify)) 
+	        {
+			/* if no entries wait for some if caller wants to */
+		}
+        	/*
+        	 *	Setup queue entry with command, status and fib mapped
+        	 */
+        	entry->size = cpu_to_le32(le16_to_cpu(fib->header.Size));
+        	entry->addr = cpu_to_le32(fib->header.SenderFibAddress);     		/* Restore adapters pointer to the FIB */
+		fib->header.ReceiverFibAddress = fib->header.SenderFibAddress;		/* Let the adapter now where to find its data */
+        	map = 0;
+	} 
+	/*
+	 *	If MapFib is true than we need to map the Fib and put pointers
+	 *	in the queue entry.
+	 */
+	if (map)
+		entry->addr = cpu_to_le32((unsigned long)(fibptr->logicaladdr));
+	return 0;
+}
+
+
+/**
+ *	aac_insert_entry	-	insert a queue entry
+ *	@dev: Adapter
+ *	@index: Index of entry to insert
+ *	@qid: Queue number
+ *	@nonotify: Suppress adapter notification
+ *
+ *	Gets the next free QE off the requested priorty adapter command
+ *	queue and associates the Fib with the QE. The QE represented by
+ *	index is ready to insert on the queue when this routine returns
+ *	success.
+ */
+ 
+static int aac_insert_entry(struct aac_dev * dev, u32 index, u32 qid, unsigned long nonotify) 
+{
+	struct aac_queue * q = &dev->queues->queue[qid];
+
+	if(q == NULL)
+		BUG();
+	*(q->headers.producer) = cpu_to_le32(index + 1);
+	spin_unlock_irqrestore(q->lock, q->SavedIrql);
+
+	if (qid == AdapHighCmdQueue ||
+	    qid == AdapNormCmdQueue ||
+	    qid == AdapHighRespQueue ||
+	    qid == AdapNormRespQueue)
+	{
+		if (!nonotify)
+			aac_adapter_notify(dev, qid);
+	}
+	else
+		printk("Suprise insert!\n");
+	return 0;
+}
+
+/*
+ *	Define the highest level of host to adapter communication routines. 
+ *	These routines will support host to adapter FS commuication. These 
+ *	routines have no knowledge of the commuication method used. This level
+ *	sends and receives FIBs. This level has no knowledge of how these FIBs
+ *	get passed back and forth.
+ */
+
+/**
+ *	fib_send	-	send a fib to the adapter
+ *	@command: Command to send
+ *	@fibptr: The fib
+ *	@size: Size of fib data area
+ *	@priority: Priority of Fib
+ *	@wait: Async/sync select
+ *	@reply: True if a reply is wanted
+ *	@callback: Called with reply
+ *	@callback_data: Passed to callback
+ *
+ *	Sends the requested FIB to the adapter and optionally will wait for a
+ *	response FIB. If the caller does not wish to wait for a response than
+ *	an event to wait on must be supplied. This event will be set when a
+ *	response FIB is received from the adapter.
+ */
+ 
+int fib_send(u16 command, struct fib * fibptr, unsigned long size,  int priority, int wait, int reply, fib_callback callback, void * callback_data)
+{
+	u32 index;
+	u32 qid;
+	struct aac_dev * dev = fibptr->dev;
+	unsigned long nointr = 0;
+	struct hw_fib * fib = fibptr->fib;
+	struct aac_queue * q;
+	unsigned long flags = 0;
+
+	if (!(le32_to_cpu(fib->header.XferState) & HostOwned))
+		return -EBUSY;
+	/*
+	 *	There are 5 cases with the wait and reponse requested flags. 
+	 *	The only invalid cases are if the caller requests to wait and
+	 *	does not request a response and if the caller does not want a
+	 *	response and the Fibis not allocated from pool. If a response
+	 *	is not requesed the Fib will just be deallocaed by the DPC
+	 *	routine when the response comes back from the adapter. No
+	 *	further processing will be done besides deleting the Fib. We 
+	 *	will have a debug mode where the adapter can notify the host
+	 *	it had a problem and the host can log that fact.
+	 */
+	if (wait && !reply) {
+		return -EINVAL;
+	} else if (!wait && reply) {
+		fib->header.XferState |= cpu_to_le32(Async | ResponseExpected);
+		FIB_COUNTER_INCREMENT(aac_config.AsyncSent);
+	} else if (!wait && !reply) {
+		fib->header.XferState |= cpu_to_le32(NoResponseExpected);
+		FIB_COUNTER_INCREMENT(aac_config.NoResponseSent);
+	} else if (wait && reply) {
+		fib->header.XferState |= cpu_to_le32(ResponseExpected);
+		FIB_COUNTER_INCREMENT(aac_config.NormalSent);
+	} 
+	/*
+	 *	Map the fib into 32bits by using the fib number
+	 */
+	fib->header.SenderData = fibptr-&dev->fibs[0];	/* for callback */
+	/*
+	 *	Set FIB state to indicate where it came from and if we want a
+	 *	response from the adapter. Also load the command from the
+	 *	caller.
+	 *
+	 *	Map the hw fib pointer as a 32bit value
+	 */
+	fib->header.SenderFibAddress = fib2addr(fib);
+	fib->header.Command = cpu_to_le16(command);
+	fib->header.XferState |= cpu_to_le32(SentFromHost);
+	fibptr->fib->header.Flags = 0;				/* Zero the flags field - its internal only...	 */
+	/*
+	 *	Set the size of the Fib we want to send to the adapter
+	 */
+	fib->header.Size = cpu_to_le16(sizeof(struct aac_fibhdr) + size);
+	if (le16_to_cpu(fib->header.Size) > le16_to_cpu(fib->header.SenderSize)) {
+		return -EMSGSIZE;
+	}                
+	/*
+	 *	Get a queue entry connect the FIB to it and send an notify
+	 *	the adapter a command is ready.
+	 */
+	if (priority == FsaHigh) {
+		fib->header.XferState |= cpu_to_le32(HighPriority);
+		qid = AdapHighCmdQueue;
+	} else {
+		fib->header.XferState |= cpu_to_le32(NormalPriority);
+		qid = AdapNormCmdQueue;
+	}
+	q = &dev->queues->queue[qid];
+
+	if(wait)
+		spin_lock_irqsave(&fibptr->event_lock, flags);
+	if(aac_queue_get( dev, &index, qid, fib, 1, fibptr, &nointr)<0)
+		return -EWOULDBLOCK;
+	dprintk((KERN_DEBUG "fib_send: inserting a queue entry at index %d.\n",index));
+	dprintk((KERN_DEBUG "Fib contents:.\n"));
+	dprintk((KERN_DEBUG "  Command =               %d.\n", fib->header.Command));
+	dprintk((KERN_DEBUG "  XferState  =            %x.\n", fib->header.XferState));
+	/*
+	 *	Fill in the Callback and CallbackContext if we are not
+	 *	going to wait.
+	 */
+	if (!wait) {
+		fibptr->callback = callback;
+		fibptr->callback_data = callback_data;
+	}
+	FIB_COUNTER_INCREMENT(aac_config.FibsSent);
+	list_add_tail(&fibptr->queue, &q->pendingq);
+	q->numpending++;
+
+	fibptr->done = 0;
+
+	if(aac_insert_entry(dev, index, qid, (nointr & aac_config.irq_mod)) < 0)
+		return -EWOULDBLOCK;
+	/*
+	 *	If the caller wanted us to wait for response wait now. 
+	 */
+    
+	if (wait) {
+		spin_unlock_irqrestore(&fibptr->event_lock, flags);
+		down(&fibptr->event_wait);
+		if(fibptr->done == 0)
+			BUG();
+			
+		if((fibptr->flags & FIB_CONTEXT_FLAG_TIMED_OUT))
+			return -ETIMEDOUT;
+		else
+			return 0;
+	}
+	/*
+	 *	If the user does not want a response than return success otherwise
+	 *	return pending
+	 */
+	if (reply)
+		return -EINPROGRESS;
+	else
+		return 0;
+}
+
+/** 
+ *	aac_consumer_get	-	get the top of the queue
+ *	@dev: Adapter
+ *	@q: Queue
+ *	@entry: Return entry
+ *
+ *	Will return a pointer to the entry on the top of the queue requested that
+ * 	we are a consumer of, and return the address of the queue entry. It does
+ *	not change the state of the queue. 
+ */
+
+int aac_consumer_get(struct aac_dev * dev, struct aac_queue * q, struct aac_entry **entry)
+{
+	u32 index;
+	int status;
+
+	if (*q->headers.producer == *q->headers.consumer) {
+		status = 0;
+	} else {
+		/*
+		 *	The consumer index must be wrapped if we have reached
+		 *	the end of the queue, else we just use the entry
+		 *	pointed to by the header index
+		 */
+		if (le32_to_cpu(*q->headers.consumer) >= q->entries) 
+			index = 0;		
+		else
+		        index = le32_to_cpu(*q->headers.consumer);
+		*entry = q->base + index;
+		status = 1;
+	}
+	return(status);
+}
+
+int aac_consumer_avail(struct aac_dev *dev, struct aac_queue * q)
+{
+	return (*q->headers.producer != *q->headers.consumer);
+}
+
+
+/**
+ *	aac_consumer_free	-	free consumer entry
+ *	@dev: Adapter
+ *	@q: Queue
+ *	@qid: Queue ident
+ *
+ *	Frees up the current top of the queue we are a consumer of. If the
+ *	queue was full notify the producer that the queue is no longer full.
+ */
+
+void aac_consumer_free(struct aac_dev * dev, struct aac_queue *q, u32 qid)
+{
+	int wasfull = 0;
+	u32 notify;
+
+	if (*q->headers.producer+1 == *q->headers.consumer)
+		wasfull = 1;
+        
+	if (le32_to_cpu(*q->headers.consumer) >= q->entries)
+		*q->headers.consumer = cpu_to_le32(1);
+	else
+		*q->headers.consumer = cpu_to_le32(le32_to_cpu(*q->headers.consumer)+1);
+        
+	if (wasfull) {
+		switch (qid) {
+
+		case HostNormCmdQueue:
+			notify = HostNormCmdNotFull;
+			break;
+		case HostHighCmdQueue:
+			notify = HostHighCmdNotFull;
+			break;
+		case HostNormRespQueue:
+			notify = HostNormRespNotFull;
+			break;
+		case HostHighRespQueue:
+			notify = HostHighRespNotFull;
+			break;
+		default:
+			BUG();
+			return;
+		}
+		aac_adapter_notify(dev, notify);
+	}
+}        
+
+/**
+ *	fib_adapter_complete	-	complete adapter issued fib
+ *	@fibptr: fib to complete
+ *	@size: size of fib
+ *
+ *	Will do all necessary work to complete a FIB that was sent from
+ *	the adapter.
+ */
+
+int fib_adapter_complete(struct fib * fibptr, unsigned short size)
+{
+	struct hw_fib * fib = fibptr->fib;
+	struct aac_dev * dev = fibptr->dev;
+	unsigned long nointr = 0;
+
+	if (le32_to_cpu(fib->header.XferState) == 0)
+        	return 0;
+	/*
+	 *	If we plan to do anything check the structure type first.
+	 */ 
+	if ( fib->header.StructType != FIB_MAGIC ) {
+        	return -EINVAL;
+	}
+	/*
+	 *	This block handles the case where the adapter had sent us a
+	 *	command and we have finished processing the command. We
+	 *	call completeFib when we are done processing the command 
+	 *	and want to send a response back to the adapter. This will 
+	 *	send the completed cdb to the adapter.
+	 */
+	if (fib->header.XferState & cpu_to_le32(SentFromAdapter)) {
+	        fib->header.XferState |= cpu_to_le32(HostProcessed);
+	        if (fib->header.XferState & cpu_to_le32(HighPriority)) {
+        		u32 index;
+       			if (size) 
+			{
+				size += sizeof(struct aac_fibhdr);
+				if (size > le16_to_cpu(fib->header.SenderSize))
+					return -EMSGSIZE;
+				fib->header.Size = cpu_to_le16(size);
+			}
+			if(aac_queue_get(dev, &index, AdapHighRespQueue, fib, 1, NULL, &nointr) < 0) {
+				return -EWOULDBLOCK;
+			}
+			if (aac_insert_entry(dev, index, AdapHighRespQueue,  (nointr & (int)aac_config.irq_mod)) != 0) {
+			}
+		}
+		else if (fib->header.XferState & NormalPriority) 
+		{
+			u32 index;
+
+			if (size) {
+				size += sizeof(struct aac_fibhdr);
+				if (size > le16_to_cpu(fib->header.SenderSize)) 
+					return -EMSGSIZE;
+				fib->header.Size = cpu_to_le16(size);
+			}
+			if (aac_queue_get(dev, &index, AdapNormRespQueue, fib, 1, NULL, &nointr) < 0) 
+				return -EWOULDBLOCK;
+			if (aac_insert_entry(dev, index, AdapNormRespQueue, 
+				(nointr & (int)aac_config.irq_mod)) != 0) 
+			{
+			}
+		}
+	}
+	else 
+	{
+        	printk(KERN_WARNING "fib_adapter_complete: Unknown xferstate detected.\n");
+        	BUG();
+	}   
+	return 0;
+}
+
+/**
+ *	fib_complete	-	fib completion handler
+ *	@fib: FIB to complete
+ *
+ *	Will do all necessary work to complete a FIB.
+ */
+ 
+int fib_complete(struct fib * fibptr)
+{
+	struct hw_fib * fib = fibptr->fib;
+
+	/*
+	 *	Check for a fib which has already been completed
+	 */
+
+	if (fib->header.XferState == cpu_to_le32(0))
+        	return 0;
+	/*
+	 *	If we plan to do anything check the structure type first.
+	 */ 
+
+	if (fib->header.StructType != FIB_MAGIC)
+	        return -EINVAL;
+	/*
+	 *	This block completes a cdb which orginated on the host and we 
+	 *	just need to deallocate the cdb or reinit it. At this point the
+	 *	command is complete that we had sent to the adapter and this
+	 *	cdb could be reused.
+	 */
+	if((fib->header.XferState & cpu_to_le32(SentFromHost)) &&
+		(fib->header.XferState & cpu_to_le32(AdapterProcessed)))
+	{
+		fib_dealloc(fibptr);
+	}
+	else if(fib->header.XferState & cpu_to_le32(SentFromHost))
+	{
+		/*
+		 *	This handles the case when the host has aborted the I/O
+		 *	to the adapter because the adapter is not responding
+		 */
+		fib_dealloc(fibptr);
+	} else if(fib->header.XferState & cpu_to_le32(HostOwned)) {
+		fib_dealloc(fibptr);
+	} else {
+		BUG();
+	}   
+	return 0;
+}
+
+/**
+ *	aac_printf	-	handle printf from firmware
+ *	@dev: Adapter
+ *	@val: Message info
+ *
+ *	Print a message passed to us by the controller firmware on the
+ *	Adaptec board
+ */
+
+void aac_printf(struct aac_dev *dev, u32 val)
+{
+	int length = val & 0xffff;
+	int level = (val >> 16) & 0xffff;
+	char *cp = dev->printfbuf;
+	
+	/*
+	 *	The size of the printfbuf is set in port.c
+	 *	There is no variable or define for it
+	 */
+	if (length > 255)
+		length = 255;
+	if (cp[length] != 0)
+		cp[length] = 0;
+	if (level == LOG_HIGH_ERROR)
+		printk(KERN_WARNING "aacraid:%s", cp);
+	else
+		printk(KERN_INFO "aacraid:%s", cp);
+	memset(cp, 0,  256);
+}
+
+
+/**
+ *	aac_handle_aif		-	Handle a message from the firmware
+ *	@dev: Which adapter this fib is from
+ *	@fibptr: Pointer to fibptr from adapter
+ *
+ *	This routine handles a driver notify fib from the adapter and
+ *	dispatches it to the appropriate routine for handling.
+ */
+
+static void aac_handle_aif(struct aac_dev * dev, struct fib * fibptr)
+{
+	struct hw_fib * fib = fibptr->fib;
+	/*
+	 * Set the status of this FIB to be Invalid parameter.
+	 *
+	 *	*(u32 *)fib->data = ST_INVAL;
+	 */
+	*(u32 *)fib->data = cpu_to_le32(ST_OK);
+	fib_adapter_complete(fibptr, sizeof(u32));
+}
+
+/**
+ *	aac_command_thread	-	command processing thread
+ *	@dev: Adapter to monitor
+ *
+ *	Waits on the commandready event in it's queue. When the event gets set
+ *	it will pull FIBs off it's queue. It will continue to pull FIBs off
+ *	until the queue is empty. When the queue is empty it will wait for
+ *	more FIBs.
+ */
+ 
+int aac_command_thread(struct aac_dev * dev)
+{
+	struct hw_fib *fib, *newfib;
+	struct fib fibptr; /* for error logging */
+	struct aac_queue_block *queues = dev->queues;
+	struct aac_fib_context *fibctx;
+	unsigned long flags;
+	DECLARE_WAITQUEUE(wait, current);
+
+	/*
+	 *	We can only have one thread per adapter for AIF's.
+	 */
+	if (dev->aif_thread)
+		return -EINVAL;
+	/*
+	 *	Set up the name that will appear in 'ps'
+	 *	stored in  task_struct.comm[16].
+	 */
+	sprintf(current->comm, "aacraid");
+	daemonize();
+	/*
+	 *	Let the DPC know it has a place to send the AIF's to.
+	 */
+	dev->aif_thread = 1;
+	memset(&fibptr, 0, sizeof(struct fib));
+	add_wait_queue(&queues->queue[HostNormCmdQueue].cmdready, &wait);
+	set_current_state(TASK_INTERRUPTIBLE);
+	while(1) 
+	{
+		spin_lock_irqsave(queues->queue[HostNormCmdQueue].lock, flags);
+		while(!list_empty(&(queues->queue[HostNormCmdQueue].cmdq))) {
+			struct list_head *entry;
+			struct aac_aifcmd * aifcmd;
+
+			set_current_state(TASK_RUNNING);
+		
+			entry = queues->queue[HostNormCmdQueue].cmdq.next;
+			list_del(entry);
+			
+			spin_unlock_irqrestore(queues->queue[HostNormCmdQueue].lock, flags);
+			fib = list_entry(entry, struct hw_fib, header.FibLinks);
+			/*
+			 *	We will process the FIB here or pass it to a 
+			 *	worker thread that is TBD. We Really can't 
+			 *	do anything at this point since we don't have
+			 *	anything defined for this thread to do.
+			 */
+			memset(&fibptr, 0, sizeof(struct fib));
+			fibptr.type = FSAFS_NTC_FIB_CONTEXT;
+			fibptr.size = sizeof( struct fib );
+			fibptr.fib = fib;
+			fibptr.data = fib->data;
+			fibptr.dev = dev;
+			/*
+			 *	We only handle AifRequest fibs from the adapter.
+			 */
+			aifcmd = (struct aac_aifcmd *) fib->data;
+			if (aifcmd->command == le16_to_cpu(AifCmdDriverNotify)) {
+				aac_handle_aif(dev, &fibptr);
+			} else {
+				/* The u32 here is important and intended. We are using
+				   32bit wrapping time to fit the adapter field */
+				   
+				u32 time_now, time_last;
+				unsigned long flagv;
+				
+				time_now = jiffies/HZ;
+
+				spin_lock_irqsave(&dev->fib_lock, flagv);
+				entry = dev->fib_list.next;
+				/*
+				 * For each Context that is on the 
+				 * fibctxList, make a copy of the
+				 * fib, and then set the event to wake up the
+				 * thread that is waiting for it.
+				 */
+				while (entry != &dev->fib_list) {
+					/*
+					 * Extract the fibctx
+					 */
+					fibctx = list_entry(entry, struct aac_fib_context, next);
+					/*
+					 * Check if the queue is getting
+					 * backlogged
+					 */
+					if (fibctx->count > 20)
+					{
+						time_last = fibctx->jiffies;
+						/*
+						 * Has it been > 2 minutes 
+						 * since the last read off
+						 * the queue?
+						 */
+						if ((time_now - time_last) > 120) {
+							entry = entry->next;
+							aac_close_fib_context(dev, fibctx);
+							continue;
+						}
+					}
+					/*
+					 * Warning: no sleep allowed while
+					 * holding spinlock
+					 */
+					newfib = kmalloc(sizeof(struct hw_fib), GFP_ATOMIC);
+					if (newfib) {
+						/*
+						 * Make the copy of the FIB
+						 */
+						memcpy(newfib, fib, sizeof(struct hw_fib));
+						/*
+						 * Put the FIB onto the
+						 * fibctx's fibs
+						 */
+						list_add_tail(&newfib->header.FibLinks, &fibctx->fibs);
+						fibctx->count++;
+						/* 
+						 * Set the event to wake up the
+						 * thread that will waiting.
+						 */
+						up(&fibctx->wait_sem);
+					} else {
+						printk(KERN_WARNING "aifd: didn't allocate NewFib.\n");
+					}
+					entry = entry->next;
+				}
+				/*
+				 *	Set the status of this FIB
+				 */
+				*(u32 *)fib->data = cpu_to_le32(ST_OK);
+				fib_adapter_complete(&fibptr, sizeof(u32));
+				spin_unlock_irqrestore(&dev->fib_lock, flagv);
+			}
+			spin_lock_irqsave(queues->queue[HostNormCmdQueue].lock, flags);
+		}
+		/*
+		 *	There are no more AIF's
+		 */
+		spin_unlock_irqrestore(queues->queue[HostNormCmdQueue].lock, flags);
+		schedule();
+
+		if(signal_pending(current))
+			break;
+		set_current_state(TASK_INTERRUPTIBLE);
+	}
+	remove_wait_queue(&queues->queue[HostNormCmdQueue].cmdready, &wait);
+	dev->aif_thread = 0;
+	complete_and_exit(&dev->aif_completion, 0);
+}
diff -Nru a/drivers/scsi/aacraid/dpcsup.c b/drivers/scsi/aacraid/dpcsup.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/scsi/aacraid/dpcsup.c	Mon Oct  7 11:26:06 2002
@@ -0,0 +1,201 @@
+/*
+ *	Adaptec AAC series RAID controller driver
+ *	(c) Copyright 2001 Red Hat Inc.	<alan@redhat.com>
+ *
+ * based on the old aacraid driver that is..
+ * Adaptec aacraid device driver for Linux.
+ *
+ * Copyright (c) 2000 Adaptec, Inc. (aacraid@adaptec.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, 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; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Module Name:
+ *  dpcsup.c
+ *
+ * Abstract: All DPC processing routines for the cyclone board occur here.
+ *
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/pci.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/completion.h>
+#include <linux/blk.h>
+#include <asm/semaphore.h>
+#include "scsi.h"
+#include "hosts.h"
+
+#include "aacraid.h"
+
+/**
+ *	aac_response_normal	-	Handle command replies
+ *	@q: Queue to read from
+ *
+ *	This DPC routine will be run when the adapter interrupts us to let us
+ *	know there is a response on our normal priority queue. We will pull off
+ *	all QE there are and wake up all the waiters before exiting. We will
+ *	take a spinlock out on the queue before operating on it.
+ */
+
+unsigned int aac_response_normal(struct aac_queue * q)
+{
+	struct aac_dev * dev = q->dev;
+	struct aac_entry *entry;
+	struct hw_fib * hwfib;
+	struct fib * fib;
+	int consumed = 0;
+	unsigned long flags;
+
+	spin_lock_irqsave(q->lock, flags);	
+
+	/*
+	 *	Keep pulling response QEs off the response queue and waking
+	 *	up the waiters until there are no more QEs. We then return
+	 *	back to the system. If no response was requesed we just
+	 *	deallocate the Fib here and continue.
+	 */
+	while(aac_consumer_get(dev, q, &entry))
+	{
+		int fast;
+
+		fast = (int) (entry->addr & 0x01);
+		hwfib = addr2fib(entry->addr & ~0x01);
+		aac_consumer_free(dev, q, HostNormRespQueue);
+		fib = &dev->fibs[hwfib->header.SenderData];
+		/*
+		 *	Remove this fib from the Outstanding I/O queue.
+		 *	But only if it has not already been timed out.
+		 *
+		 *	If the fib has been timed out already, then just 
+		 *	continue. The caller has already been notified that
+		 *	the fib timed out.
+		 */
+		if (!(fib->flags & FIB_CONTEXT_FLAG_TIMED_OUT)) {
+			list_del(&fib->queue);
+			dev->queues->queue[AdapNormCmdQueue].numpending--;
+		} else {
+			printk(KERN_WARNING "aacraid: FIB timeout (%x).\n", fib->flags);
+			continue;
+		}
+		spin_unlock_irqrestore(q->lock, flags);
+
+		if (fast) {
+			/*
+			 *	Doctor the fib
+			 */
+			*(u32 *)hwfib->data = cpu_to_le32(ST_OK);
+			hwfib->header.XferState |= cpu_to_le32(AdapterProcessed);
+		}
+
+		FIB_COUNTER_INCREMENT(aac_config.FibRecved);
+
+		if (hwfib->header.Command == cpu_to_le16(NuFileSystem))
+		{
+			u32 *pstatus = (u32 *)hwfib->data;
+			if (*pstatus & cpu_to_le32(0xffff0000))
+				*pstatus = cpu_to_le32(ST_OK);
+		}
+		if (hwfib->header.XferState & cpu_to_le32(NoResponseExpected | Async)) 
+		{
+	        	if (hwfib->header.XferState & cpu_to_le32(NoResponseExpected))
+				FIB_COUNTER_INCREMENT(aac_config.NoResponseRecved);
+			else 
+				FIB_COUNTER_INCREMENT(aac_config.AsyncRecved);
+			/*
+			 *	NOTE:  we cannot touch the fib after this
+			 *	    call, because it may have been deallocated.
+			 */
+			fib->callback(fib->callback_data, fib);
+		} else {
+			unsigned long flagv;
+			spin_lock_irqsave(&fib->event_lock, flagv);
+			fib->done = 1;
+			up(&fib->event_wait);
+			spin_unlock_irqrestore(&fib->event_lock, flagv);
+			FIB_COUNTER_INCREMENT(aac_config.NormalRecved);
+		}
+		consumed++;
+		spin_lock_irqsave(q->lock, flags);
+	}
+
+	if (consumed > aac_config.peak_fibs)
+		aac_config.peak_fibs = consumed;
+	if (consumed == 0) 
+		aac_config.zero_fibs++;
+
+	spin_unlock_irqrestore(q->lock, flags);
+	return 0;
+}
+
+
+/**
+ *	aac_command_normal	-	handle commands
+ *	@q: queue to process
+ *
+ *	This DPC routine will be queued when the adapter interrupts us to 
+ *	let us know there is a command on our normal priority queue. We will 
+ *	pull off all QE there are and wake up all the waiters before exiting.
+ *	We will take a spinlock out on the queue before operating on it.
+ */
+ 
+unsigned int aac_command_normal(struct aac_queue *q)
+{
+	struct aac_dev * dev = q->dev;
+	struct aac_entry *entry;
+	unsigned long flags;
+
+	spin_lock_irqsave(q->lock, flags);
+
+	/*
+	 *	Keep pulling response QEs off the response queue and waking
+	 *	up the waiters until there are no more QEs. We then return
+	 *	back to the system.
+	 */
+	while(aac_consumer_get(dev, q, &entry))
+	{
+		struct hw_fib * fib;
+		fib = addr2fib(entry->addr);
+
+		if (dev->aif_thread) {
+		        list_add_tail(&fib->header.FibLinks, &q->cmdq);
+	 	        aac_consumer_free(dev, q, HostNormCmdQueue);
+		        wake_up_interruptible(&q->cmdready);
+		} else {
+			struct fib fibctx;
+	 	        aac_consumer_free(dev, q, HostNormCmdQueue);
+			spin_unlock_irqrestore(q->lock, flags);
+			memset(&fibctx, 0, sizeof(struct fib));
+			fibctx.type = FSAFS_NTC_FIB_CONTEXT;
+			fibctx.size = sizeof(struct fib);
+			fibctx.fib = fib;
+			fibctx.data = fib->data;
+			fibctx.dev = dev;
+			/*
+			 *	Set the status of this FIB
+			 */
+			*(u32 *)fib->data = cpu_to_le32(ST_OK);
+			fib_adapter_complete(&fibctx, sizeof(u32));
+			spin_lock_irqsave(q->lock, flags);
+		}		
+	}
+	spin_unlock_irqrestore(q->lock, flags);
+	return 0;
+}
diff -Nru a/drivers/scsi/aacraid/linit.c b/drivers/scsi/aacraid/linit.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/scsi/aacraid/linit.c	Mon Oct  7 11:26:06 2002
@@ -0,0 +1,743 @@
+/*
+ *	Adaptec AAC series RAID controller driver
+ *	(c) Copyright 2001 Red Hat Inc.	<alan@redhat.com>
+ *
+ * based on the old aacraid driver that is..
+ * Adaptec aacraid device driver for Linux.
+ *
+ * Copyright (c) 2000 Adaptec, Inc. (aacraid@adaptec.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, 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; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Module Name:
+ *   linit.c
+ *
+ * Abstract: Linux Driver entry module for Adaptec RAID Array Controller
+ *				
+ *	Provides the following driver entry points:
+ *		aac_detect()
+ *		aac_release()
+ *		aac_queuecommand()
+ *		aac_resetcommand()
+ *		aac_biosparm()
+ *	
+ */
+
+#define AAC_DRIVER_VERSION		"0.9.9ac6-TEST"
+#define AAC_DRIVER_BUILD_DATE		__DATE__
+
+#include <linux/module.h>
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/pci.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/completion.h>
+#include <asm/semaphore.h>
+#include <linux/blk.h>
+#include "scsi.h"
+#include "hosts.h"
+
+#include <scsi/scsicam.h>
+
+#include "aacraid.h"
+#include "sd.h"
+
+#define AAC_DRIVERNAME	"aacraid"
+
+MODULE_AUTHOR("Red Hat Inc and Adaptec");
+MODULE_DESCRIPTION("Supports Dell PERC2, 2/Si, 3/Si, 3/Di, Adaptec 2120S, 2200S, 5400S, and HP NetRAID-4M devices. http://domsch.com/linux/ or http://linux.adaptec.com");
+MODULE_LICENSE("GPL");
+MODULE_PARM(nondasd, "i");
+MODULE_PARM_DESC(nondasd, "Control scanning of hba for nondasd devices. 0=off, 1=on");
+
+static int nondasd=-1;
+
+struct aac_dev *aac_devices[MAXIMUM_NUM_ADAPTERS];
+
+static unsigned aac_count = 0;
+static int aac_cfg_major = -1;
+
+/*
+ * Because of the way Linux names scsi devices, the order in this table has
+ * become important.  Check for on-board Raid first, add-in cards second.
+ *
+ * dmb - For now we add the number of channels to this structure.  
+ * In the future we should add a fib that reports the number of channels
+ * for the card.  At that time we can remove the channels from here
+ */
+ 
+static struct aac_driver_ident aac_drivers[] = {
+	{ 0x1028, 0x0001, 0x1028, 0x0001, aac_rx_init, "percraid", "DELL    ", "PERCRAID        ", 2 }, /* PERC 2/Si */
+	{ 0x1028, 0x0002, 0x1028, 0x0002, aac_rx_init, "percraid", "DELL    ", "PERCRAID        ", 2 }, /* PERC 3/Di */
+	{ 0x1028, 0x0003, 0x1028, 0x0003, aac_rx_init, "percraid", "DELL    ", "PERCRAID        ", 2 }, /* PERC 3/Si */
+	{ 0x1028, 0x0004, 0x1028, 0x00d0, aac_rx_init, "percraid", "DELL    ", "PERCRAID        ", 2 }, /* PERC 3/Si */
+	{ 0x1028, 0x0002, 0x1028, 0x00d1, aac_rx_init, "percraid", "DELL    ", "PERCRAID        ", 2 }, /* PERC 3/Di */
+	{ 0x1028, 0x0002, 0x1028, 0x00d9, aac_rx_init, "percraid", "DELL    ", "PERCRAID        ", 2 }, /* PERC 3/Di */
+	{ 0x1028, 0x000a, 0x1028, 0x0106, aac_rx_init, "percraid", "DELL    ", "PERCRAID        ", 2 }, /* PERC 3/Di */
+	{ 0x1028, 0x000a, 0x1028, 0x011b, aac_rx_init, "percraid", "DELL    ", "PERCRAID        ", 2 }, /* PERC 3/Di */
+	{ 0x1028, 0x000a, 0x1028, 0x0121, aac_rx_init, "percraid", "DELL    ", "PERCRAID        ", 2 }, /* PERC 3/Di */
+	{ 0x9005, 0x0283, 0x9005, 0x0283, aac_rx_init, "aacraid",  "ADAPTEC ", "catapult        ", 2 }, /* catapult*/
+	{ 0x9005, 0x0284, 0x9005, 0x0284, aac_rx_init, "aacraid",  "ADAPTEC ", "tomcat          ", 2 }, /* tomcat*/
+	{ 0x9005, 0x0285, 0x9005, 0x0286, aac_rx_init, "aacraid",  "ADAPTEC ", "Adaptec 2120S   ", 1 }, /* Adaptec 2120S (Crusader)*/
+	{ 0x9005, 0x0285, 0x9005, 0x0285, aac_rx_init, "aacraid",  "ADAPTEC ", "Adaptec 2200S   ", 2 }, /* Adaptec 2200S (Vulcan)*/
+	{ 0x9005, 0x0285, 0x9005, 0x0287, aac_rx_init, "aacraid",  "ADAPTEC ", "Adaptec 2200S   ", 2 }, /* Adaptec 2200S (Vulcan-2m)*/
+	{ 0x1011, 0x0046, 0x9005, 0x0365, aac_sa_init, "aacraid",  "ADAPTEC ", "Adaptec 5400S   ", 4 }, /* Adaptec 5400S (Mustang)*/
+	{ 0x1011, 0x0046, 0x9005, 0x0364, aac_sa_init, "aacraid",  "ADAPTEC ", "AAC-364         ", 4 }, /* Adaptec 5400S (Mustang)*/
+	{ 0x1011, 0x0046, 0x9005, 0x1364, aac_sa_init, "percraid", "DELL    ", "PERCRAID        ", 4 }, /* Dell PERC2 "Quad Channel" */
+	{ 0x1011, 0x0046, 0x103c, 0x10c2, aac_sa_init, "hpnraid",  "HP      ", "NetRAID-4M      ", 4 }  /* HP NetRAID-4M */
+};
+
+#define NUM_AACTYPES	(sizeof(aac_drivers) / sizeof(struct aac_driver_ident))
+static int num_aacdrivers = NUM_AACTYPES;
+
+static int aac_cfg_ioctl(struct inode * inode, struct file * file, unsigned int cmd, unsigned long arg);
+static int aac_cfg_open(struct inode * inode, struct file * file);
+static int aac_cfg_release(struct inode * inode,struct file * file);
+
+static struct file_operations aac_cfg_fops = {
+	owner: THIS_MODULE,
+	ioctl: aac_cfg_ioctl,
+	open: aac_cfg_open,
+	release: aac_cfg_release
+};
+
+static int aac_detect(Scsi_Host_Template *);
+static int aac_release(struct Scsi_Host *);
+static int aac_queuecommand(Scsi_Cmnd *, void (*CompletionRoutine)(Scsi_Cmnd *));
+static int aac_biosparm(Scsi_Disk *, struct block_device *, int *);
+static int aac_procinfo(char *, char **, off_t, int, int, int);
+static int aac_ioctl(Scsi_Device *, int, void *);
+static int aac_eh_abort(Scsi_Cmnd * cmd);
+static int aac_eh_device_reset(Scsi_Cmnd* cmd);
+static int aac_eh_bus_reset(Scsi_Cmnd* cmd);
+static int aac_eh_reset(Scsi_Cmnd* cmd);
+
+static void aac_queuedepth(struct Scsi_Host *, Scsi_Device *);
+
+/**
+ *	aac_detect	-	Probe for aacraid cards
+ *	@template: SCSI driver template
+ *
+ *	Probe for AAC Host Adapters initialize, register, and report the 
+ *	configuration of each AAC Host Adapter found.
+ *	Returns the number of adapters successfully initialized and 
+ *	registered.
+ *	Initializes all data necessary for this particular SCSI driver.
+ *	Notes:
+ *	The detect routine must not call any of the mid level functions 
+ *	to queue commands because things are not guaranteed to be set 
+ *	up yet. The detect routine can send commands to the host adapter 
+ *	as long as the program control will not be passed to scsi.c in 
+ *	the processing of the command. Note especially that 
+ *	scsi_malloc/scsi_free must not be called.
+ *
+ */
+ 
+static int aac_detect(Scsi_Host_Template *template)
+{
+	int index;
+	int container;
+	u16 vendor_id, device_id;
+	struct Scsi_Host *host_ptr;
+	struct pci_dev *dev = NULL;
+	struct aac_dev *aac;
+	struct fsa_scsi_hba *fsa_dev_ptr;
+	char *name = NULL;
+	
+	printk(KERN_INFO "Red Hat/Adaptec aacraid driver, %s\n", AAC_DRIVER_BUILD_DATE);
+
+	/* setting up the proc directory structure */
+	template->proc_name = "aacraid";
+
+	for( index = 0; index != num_aacdrivers; index++ )
+	{
+		device_id = aac_drivers[index].device;
+		vendor_id = aac_drivers[index].vendor;
+		name = aac_drivers[index].name;
+		dprintk((KERN_DEBUG "Checking %s %x/%x/%x/%x.\n", 
+			name, vendor_id, device_id,
+			aac_drivers[index].subsystem_vendor,
+			aac_drivers[index].subsystem_device));
+
+		dev = NULL;
+		while((dev = pci_find_device(vendor_id, device_id, dev))) {
+			if (pci_enable_device(dev))
+				continue;
+			pci_set_master(dev);
+			pci_set_dma_mask(dev, 0xFFFFFFFFULL);
+
+			if((dev->subsystem_vendor != aac_drivers[index].subsystem_vendor) || 
+			   (dev->subsystem_device != aac_drivers[index].subsystem_device))
+					continue;
+
+			dprintk((KERN_DEBUG "%s device detected.\n", name));
+			dprintk((KERN_DEBUG "%x/%x/%x/%x.\n", vendor_id, device_id, 
+				aac_drivers[index].subsystem_vendor, aac_drivers[index].subsystem_device));
+			/* Increment the host adapter count */
+			aac_count++;
+			/*
+			 * scsi_register() allocates memory for a Scsi_Hosts structure and
+			 * links it into the linked list of host adapters. This linked list
+			 * contains the data for all possible <supported> scsi hosts.
+			 * This is similar to the Scsi_Host_Template, except that we have
+			 * one entry for each actual physical host adapter on the system,
+			 * stored as a linked list. If there are two AAC boards, then we
+			 * will need to make two Scsi_Host entries, but there will be only
+			 * one Scsi_Host_Template entry. The second argument to scsi_register()
+			 * specifies the size of the extra memory we want to hold any device 
+			 * specific information.
+			 */
+			host_ptr = scsi_register( template, sizeof(struct aac_dev) );
+			/* 
+			 * These three parameters can be used to allow for wide SCSI 
+			 * and for host adapters that support multiple buses.
+			 */
+			host_ptr->max_id = 17;
+			host_ptr->max_lun = 8;
+			host_ptr->max_channel = 1;
+			host_ptr->irq = dev->irq;		/* Adapter IRQ number */
+			/* host_ptr->base = ( char * )(dev->resource[0].start & ~0xff); */
+			host_ptr->base = dev->resource[0].start;
+			scsi_set_pci_device(host_ptr, dev);
+			dprintk((KERN_DEBUG "Device base address = 0x%lx [0x%lx].\n", host_ptr->base, dev->resource[0].start));
+			dprintk((KERN_DEBUG "Device irq = 0x%x.\n", dev->irq));
+			/*
+			 * The unique_id field is a unique identifier that must
+			 * be assigned so that we have some way of identifying
+			 * each host adapter properly and uniquely. For hosts 
+			 * that do not support more than one card in the
+			 * system, this does not need to be set. It is
+			 * initialized to zero in scsi_register(). This is the 
+			 * value returned as aac->id.
+			 */
+			host_ptr->unique_id = aac_count - 1;
+			/*
+			 *	This function is called after the device list has
+			 *	been built to find the tagged queueing depth 
+			 *	supported for each device.
+			 */
+			host_ptr->select_queue_depths = aac_queuedepth;
+			aac = (struct aac_dev *)host_ptr->hostdata;
+			/* attach a pointer back to Scsi_Host */
+			aac->scsi_host_ptr = host_ptr;	
+			aac->pdev = dev;
+			aac->cardtype =  index;
+			aac->name = aac->scsi_host_ptr->hostt->name;
+			aac->id = aac->scsi_host_ptr->unique_id;
+			/* Initialize the ordinal number of the device to -1 */
+			fsa_dev_ptr = &(aac->fsa_dev);
+			for( container = 0; container < MAXIMUM_NUM_CONTAINERS; container++ )
+				fsa_dev_ptr->devno[container] = -1;
+
+			dprintk((KERN_DEBUG "Initializing Hardware...\n"));
+			if((*aac_drivers[index].init)(aac , host_ptr->unique_id) != 0)
+			{
+				/* device initialization failed */
+				printk(KERN_WARNING "aacraid: device initialization failed.\n");
+				scsi_unregister(host_ptr);
+				aac_count--;
+				continue;
+			} 
+			dprintk((KERN_DEBUG "%s:%d device initialization successful.\n", name, host_ptr->unique_id));
+			aac_get_adapter_info(aac);
+			if(nondasd != -1) 
+			{
+				/* someone told us how to set this on the cmdline */
+				aac->nondasd_support = (nondasd!=0);
+			}
+			if(aac->nondasd_support != 0){
+				printk(KERN_INFO "%s%d: Non-DASD support enabled\n", aac->name, aac->id);
+			}
+			dprintk((KERN_DEBUG "%s:%d options flag %04x.\n",name, host_ptr->unique_id,aac->adapter_info.options));
+			if(aac->nondasd_support == 1)
+			{
+				/*
+				 * max channel will be the physical channels plus 1 virtual channel 
+				 * all containers are on the virtual channel 0
+				 * physical channels are address by their actual physical number+1
+				 */
+				host_ptr->max_channel = aac_drivers[index].channels+1;
+			} else {
+				host_ptr->max_channel = 1;
+ 			}
+			dprintk((KERN_DEBUG "Device has %d logical channels\n", host_ptr->max_channel));
+			aac_get_containers(aac);
+			aac_devices[aac_count-1] = aac;
+
+			/*
+			 * dmb - we may need to move these 3 parms somewhere else once
+			 * we get a fib that can report the actual numbers
+			 */
+			host_ptr->max_id = AAC_MAX_TARGET;
+			host_ptr->max_lun = AAC_MAX_LUN;
+			
+			/*
+			 *  If we are PAE capable then our future DMA mappings
+			 *  (for read/write commands) are 64bit clean and don't 
+			 *  need bouncing. This assumes we do no other 32bit only
+			 *  allocations (eg fib table expands) after this point.
+			 */
+			 
+			if(aac->pae_support)
+				pci_set_dma_mask(dev, 0xFFFFFFFFFFFFFFFFUL);
+		}
+	}
+
+	if( aac_count ){
+		if((aac_cfg_major = register_chrdev( 0, "aac", &aac_cfg_fops))<0)
+			printk(KERN_WARNING "aacraid: unable to register \"aac\" device.\n");
+	}
+
+	template->present = aac_count; /* # of cards of this type found */
+	return aac_count;
+}
+
+/**
+ *	aac_release	-	release SCSI host resources
+ *	@host_ptr: SCSI host to clean up
+ *
+ *	Release all resources previously acquired to support a specific Host 
+ *	Adapter and unregister the AAC Host Adapter.
+ *
+ *	BUGS: Does not wait for the thread it kills to die.
+ */
+
+static int aac_release(struct Scsi_Host *host_ptr)
+{
+	struct aac_dev *dev;
+	dprintk((KERN_DEBUG "aac_release.\n"));
+	dev = (struct aac_dev *)host_ptr->hostdata;
+	/*
+	 *	kill any threads we started
+	 */
+	kill_proc(dev->thread_pid, SIGKILL, 0);
+	wait_for_completion(&dev->aif_completion);
+	/*
+	 *	Call the comm layer to detach from this adapter
+	 */
+	aac_detach(dev);
+	/* Check free orderings... */
+	/* remove interrupt binding */
+	free_irq(host_ptr->irq, dev);
+	iounmap((void * )dev->regs.sa);
+	/* unregister adapter */
+	scsi_unregister(host_ptr);
+	/*
+	 *	FIXME: This assumes no hot plugging is going on...
+	 */
+	if( aac_cfg_major >= 0 )
+	{
+		unregister_chrdev(aac_cfg_major, "aac");
+		aac_cfg_major = -1;
+	}
+	return 0;
+}
+
+/**
+ *	aac_queuecommand	-	queue a SCSI command
+ *	@scsi_cmnd_ptr:	SCSI command to queue
+ *	@CompletionRoutine: Function to call on command completion
+ *
+ *	Queues a command for execution by the associated Host Adapter.
+ */ 
+
+static int aac_queuecommand(Scsi_Cmnd *scsi_cmnd_ptr, void (*complete)(Scsi_Cmnd *))
+{
+	int ret;
+
+	scsi_cmnd_ptr->scsi_done = complete;
+	/*
+	 *	aac_scsi_cmd() handles command processing, setting the 
+	 *	result code and calling completion routine. 
+	 */
+	if((ret = aac_scsi_cmd(scsi_cmnd_ptr)) != 0)
+		dprintk((KERN_DEBUG "aac_scsi_cmd failed.\n"));
+	return ret;
+} 
+
+/**
+ *	aac_driverinfo		-	Returns the host adapter name
+ *	@host_ptr:	Scsi host to report on
+ *
+ *	Returns a static string describing the device in question
+ */
+
+const char *aac_driverinfo(struct Scsi_Host *host_ptr)
+{
+	struct aac_dev *dev = (struct aac_dev *)host_ptr->hostdata;
+	return aac_drivers[dev->cardtype].name;
+}
+
+/**
+ *	aac_get_driver_ident
+ * 	@devtype: index into lookup table
+ *
+ * 	Returns a pointer to the entry in the driver lookup table.
+ */
+struct aac_driver_ident* aac_get_driver_ident(int devtype)
+{
+	return  &aac_drivers[devtype];
+}
+
+/**
+ *	aac_biosparm	-	return BIOS parameters for disk
+ *	@disk: SCSI disk object to process
+ *	@device: Disk in question
+ *	@geom: geometry block to fill in
+ *
+ *	Return the Heads/Sectors/Cylinders BIOS Disk Parameters for Disk.  
+ *	The default disk geometry is 64 heads, 32 sectors, and the appropriate 
+ *	number of cylinders so as not to exceed drive capacity.  In order for 
+ *	disks equal to or larger than 1 GB to be addressable by the BIOS
+ *	without exceeding the BIOS limitation of 1024 cylinders, Extended 
+ *	Translation should be enabled.   With Extended Translation enabled, 
+ *	drives between 1 GB inclusive and 2 GB exclusive are given a disk 
+ *	geometry of 128 heads and 32 sectors, and drives above 2 GB inclusive 
+ *	are given a disk geometry of 255 heads and 63 sectors.  However, if 
+ *	the BIOS detects that the Extended Translation setting does not match 
+ *	the geometry in the partition table, then the translation inferred 
+ *	from the partition table will be used by the BIOS, and a warning may 
+ *	be displayed.
+ */
+ 
+static int aac_biosparm(Scsi_Disk *disk, struct block_device *bdev, int *geom)
+{
+	struct diskparm *param = (struct diskparm *)geom;
+	u8 *buf;
+
+	dprintk((KERN_DEBUG "aac_biosparm.\n"));
+
+	/*
+	 *	Assuming extended translation is enabled - #REVISIT#
+	 */
+	if( disk->capacity >= 2 * 1024 * 1024 ) /* 1 GB in 512 byte sectors */
+	{
+		if( disk->capacity >= 4 * 1024 * 1024 ) /* 2 GB in 512 byte sectors */
+		{
+			param->heads = 255;
+			param->sectors = 63;
+		}
+		else
+		{
+			param->heads = 128;
+			param->sectors = 32;
+		}
+	}
+	else
+	{
+		param->heads = 64;
+		param->sectors = 32;
+	}
+
+	param->cylinders = disk->capacity/(param->heads * param->sectors);
+
+	/*
+	 *	Read the partition table block
+	 */
+
+	buf = scsi_bios_ptable(bdev);
+	
+	/* 
+	 *	If the boot sector partition table is valid, search for a partition 
+	 *	table entry whose end_head matches one of the standard geometry 
+	 *	translations ( 64/32, 128/32, 255/63 ).
+	 */
+	 
+	if(*(unsigned short *)(buf + 0x40) == cpu_to_le16(0xaa55))
+	{
+		struct partition *first = (struct partition *)buf;
+		struct partition *entry = first;
+		int saved_cylinders = param->cylinders;
+		int num;
+		unsigned char end_head, end_sec;
+
+		for(num = 0; num < 4; num++)
+		{
+			end_head = entry->end_head;
+			end_sec = entry->end_sector & 0x3f;
+
+			if(end_head == 63)
+			{
+				param->heads = 64;
+				param->sectors = 32;
+				break;
+			}
+			else if(end_head == 127)
+			{
+				param->heads = 128;
+				param->sectors = 32;
+				break;
+			}
+			else if(end_head == 254) 
+			{
+				param->heads = 255;
+				param->sectors = 63;
+				break;
+			}
+			entry++;
+		}
+
+		if(num == 4)
+		{
+			end_head = first->end_head;
+			end_sec = first->end_sector & 0x3f;
+		}
+
+		param->cylinders = disk->capacity / (param->heads * param->sectors);
+
+		if(num < 4 && end_sec == param->sectors)
+		{
+			if(param->cylinders != saved_cylinders)
+				dprintk((KERN_DEBUG "Adopting geometry: heads=%d, sectors=%d from partition table %d.\n",
+					param->heads, param->sectors, num));
+		}
+		else if(end_head > 0 || end_sec > 0)
+		{
+			dprintk((KERN_DEBUG "Strange geometry: heads=%d, sectors=%d in partition table %d.\n",
+				end_head + 1, end_sec, num));
+			dprintk((KERN_DEBUG "Using geometry: heads=%d, sectors=%d.\n",
+					param->heads, param->sectors));
+		}
+	}
+	kfree(buf);
+	return 0;
+}
+
+/**
+ *	aac_queuedepth		-	compute queue depths
+ *	@host:	SCSI host in question
+ *	@dev:	SCSI device we are considering
+ *
+ *	Selects queue depths for each target device based on the host adapter's
+ *	total capacity and the queue depth supported by the target device.
+ *	A queue depth of one automatically disables tagged queueing.
+ */
+
+static void aac_queuedepth(struct Scsi_Host * host, Scsi_Device * dev )
+{
+	Scsi_Device * dptr;
+
+	dprintk((KERN_DEBUG "aac_queuedepth.\n"));
+	dprintk((KERN_DEBUG "Device #   Q Depth   Online\n"));
+	dprintk((KERN_DEBUG "---------------------------\n"));
+	for(dptr = dev; dptr != NULL; dptr = dptr->next)
+	{
+		if(dptr->host == host)
+		{
+			dptr->queue_depth = 10;		
+			dprintk((KERN_DEBUG "  %2d         %d        %d\n", 
+				dptr->id, dptr->queue_depth, dptr->online));
+		}
+	}
+}
+
+
+/**
+ *	aac_eh_abort	-	Abort command if possible.
+ *	@cmd:	SCSI command block to abort
+ *
+ *	Called when the midlayer wishes to abort a command. We don't support
+ *	this facility, and our firmware looks after life for us. We just
+ *	report this as failing
+ */
+ 
+static int aac_eh_abort(Scsi_Cmnd *cmd)
+{
+	return FAILED;
+}
+
+/**
+ *	aac_eh_device_reset	-	Reset command handling
+ *	@cmd:	SCSI command block causing the reset
+ *
+ *	Issue a reset of a SCSI device. We are ourselves not truely a SCSI
+ *	controller and our firmware will do the work for us anyway. Thus this
+ *	is a no-op. We just return FAILED.
+ */
+
+static int aac_eh_device_reset(Scsi_Cmnd *cmd)
+{
+	return FAILED;
+}
+
+/**
+ *	aac_eh_bus_reset	-	Reset command handling
+ *	@scsi_cmd:	SCSI command block causing the reset
+ *
+ *	Issue a reset of a SCSI bus. We are ourselves not truely a SCSI
+ *	controller and our firmware will do the work for us anyway. Thus this
+ *	is a no-op. We just return FAILED.
+ */
+
+static int aac_eh_bus_reset(Scsi_Cmnd* cmd)
+{
+	return FAILED;
+}
+
+/**
+ *	aac_eh_hba_reset	-	Reset command handling
+ *	@scsi_cmd:	SCSI command block causing the reset
+ *
+ *	Issue a reset of a SCSI host. If things get this bad then arguably we should
+ *	go take a look at what the host adapter is doing and see if something really
+ *	broke (as can occur at least on my Dell QC card if a drive keeps failing spinup)
+ */
+
+static int aac_eh_reset(Scsi_Cmnd* cmd)
+{
+	printk(KERN_ERR "aacraid: Host adapter reset request. SCSI hang ?\n");
+	return FAILED;
+}
+
+/**
+ *	aac_ioctl 	-	Handle SCSI ioctls
+ *	@scsi_dev_ptr: scsi device to operate upon
+ *	@cmd: ioctl command to use issue
+ *	@arg: ioctl data pointer
+ *
+ *	Issue an ioctl on an aacraid device. Returns a standard unix error code or
+ *	zero for success
+ */
+ 
+static int aac_ioctl(Scsi_Device * scsi_dev_ptr, int cmd, void * arg)
+{
+	struct aac_dev *dev;
+	dprintk((KERN_DEBUG "aac_ioctl.\n"));
+	dev = (struct aac_dev *)scsi_dev_ptr->host->hostdata;
+	return aac_do_ioctl(dev, cmd, arg);
+}
+
+/**
+ *	aac_cfg_open		-	open a configuration file
+ *	@inode: inode being opened
+ *	@file: file handle attached
+ *
+ *	Called when the configuration device is opened. Does the needed
+ *	set up on the handle and then returns
+ *
+ *	Bugs: This needs extending to check a given adapter is present
+ *	so we can support hot plugging, and to ref count adapters.
+ */
+
+static int aac_cfg_open(struct inode * inode, struct file * file )
+{
+	unsigned minor_number = minor(inode->i_rdev);
+	if(minor_number >= aac_count)
+		return -ENODEV;
+	return 0;
+}
+
+/**
+ *	aac_cfg_release		-	close down an AAC config device
+ *	@inode: inode of configuration file
+ *	@file: file handle of configuration file
+ *	
+ *	Called when the last close of the configuration file handle
+ *	is performed.
+ */
+ 
+static int aac_cfg_release(struct inode * inode, struct file * file )
+{
+	return 0;
+}
+
+/**
+ *	aac_cfg_ioctl		-	AAC configuration request
+ *	@inode: inode of device
+ *	@file: file handle
+ *	@cmd: ioctl command code
+ *	@arg: argument
+ *
+ *	Handles a configuration ioctl. Currently this involves wrapping it
+ *	up and feeding it into the nasty windowsalike glue layer.
+ *
+ *	Bugs: Needs locking against parallel ioctls lower down
+ *	Bugs: Needs to handle hot plugging
+ */
+ 
+static int aac_cfg_ioctl(struct inode * inode,  struct file * file, unsigned int cmd, unsigned long arg )
+{
+	struct aac_dev *dev = aac_devices[minor(inode->i_rdev)];
+	return aac_do_ioctl(dev, cmd, (void *)arg);
+}
+
+/*
+ *	To use the low level SCSI driver support using the linux kernel loadable 
+ *	module interface we should initialize the global variable driver_interface  
+ *	(datatype Scsi_Host_Template) and then include the file scsi_module.c.
+ */
+ 
+static Scsi_Host_Template driver_template = {
+	module:			THIS_MODULE,
+	name:           	"AAC",
+	proc_info:      	aac_procinfo,
+	detect:         	aac_detect,
+	release:        	aac_release,
+	info:           	aac_driverinfo,
+	ioctl:          	aac_ioctl,
+	queuecommand:   	aac_queuecommand,
+	bios_param:     	aac_biosparm,	
+	can_queue:      	AAC_NUM_IO_FIB,	
+	this_id:        	16,
+	sg_tablesize:   	16,
+	max_sectors:    	128,
+	cmd_per_lun:    	AAC_NUM_IO_FIB,
+	eh_abort_handler:       aac_eh_abort,
+	eh_device_reset_handler:aac_eh_device_reset,
+	eh_bus_reset_handler:	aac_eh_bus_reset,
+	eh_host_reset_handler:	aac_eh_reset,
+
+	use_clustering:		ENABLE_CLUSTERING,
+};
+
+#include "scsi_module.c"
+
+/**
+ *	aac_procinfo	-	Implement /proc/scsi/<drivername>/<n>
+ *	@proc_buffer: memory buffer for I/O
+ *	@start_ptr: pointer to first valid data
+ *	@offset: offset into file
+ *	@bytes_available: space left
+ *	@host_no: scsi host ident
+ *	@write: direction of I/O
+ *
+ *	Used to export driver statistics and other infos to the world outside 
+ *	the kernel using the proc file system. Also provides an interface to
+ *	feed the driver with information.
+ *
+ *		For reads
+ *			- if offset > 0 return 0
+ *			- if offset == 0 write data to proc_buffer and set the start_ptr to
+ *			beginning of proc_buffer, return the number of characters written.
+ *		For writes
+ *			- writes currently not supported, return 0
+ *
+ *	Bugs:	Only offset zero is handled
+ */
+
+static int aac_procinfo(char *proc_buffer, char **start_ptr,off_t offset,
+			int bytes_available, int host_no, int write)
+{
+	if(write || offset > 0)
+		return 0;
+	*start_ptr = proc_buffer;
+	return sprintf(proc_buffer, "%s  %d\n", "Raid Controller, scsi hba number", host_no);
+}
+
+EXPORT_NO_SYMBOLS;
diff -Nru a/drivers/scsi/aacraid/rx.c b/drivers/scsi/aacraid/rx.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/scsi/aacraid/rx.c	Mon Oct  7 11:26:06 2002
@@ -0,0 +1,409 @@
+/*
+ *	Adaptec AAC series RAID controller driver
+ *	(c) Copyright 2001 Red Hat Inc.	<alan@redhat.com>
+ *
+ * based on the old aacraid driver that is..
+ * Adaptec aacraid device driver for Linux.
+ *
+ * Copyright (c) 2000 Adaptec, Inc. (aacraid@adaptec.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, 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; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Module Name:
+ *  rx.c
+ *
+ * Abstract: Hardware miniport for Drawbridge specific hardware functions.
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/pci.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/blk.h>
+#include <linux/delay.h>
+#include <linux/completion.h>
+#include <asm/semaphore.h>
+#include "scsi.h"
+#include "hosts.h"
+
+#include "aacraid.h"
+
+static void aac_rx_intr(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct aac_dev *dev = dev_id;
+	unsigned long bellbits;
+	u8 intstat, mask;
+	intstat = rx_readb(dev, MUnit.OISR);
+	/*
+	 *	Read mask and invert because drawbridge is reversed.
+	 *	This allows us to only service interrupts that have 
+	 *	been enabled.
+	 */
+	mask = ~(rx_readb(dev, MUnit.OIMR));
+	/* Check to see if this is our interrupt.  If it isn't just return */
+	if (intstat & mask) 
+	{
+		bellbits = rx_readl(dev, OutboundDoorbellReg);
+		if (bellbits & DoorBellPrintfReady) {
+			aac_printf(dev, le32_to_cpu(rx_readl (dev, IndexRegs.Mailbox[5])));
+			rx_writel(dev, MUnit.ODR,DoorBellPrintfReady);
+			rx_writel(dev, InboundDoorbellReg,DoorBellPrintfDone);
+		}
+		else if (bellbits & DoorBellAdapterNormCmdReady) {
+			aac_command_normal(&dev->queues->queue[HostNormCmdQueue]);
+			rx_writel(dev, MUnit.ODR, DoorBellAdapterNormCmdReady);
+		}
+		else if (bellbits & DoorBellAdapterNormRespReady) {
+			aac_response_normal(&dev->queues->queue[HostNormRespQueue]);
+			rx_writel(dev, MUnit.ODR,DoorBellAdapterNormRespReady);
+		}
+		else if (bellbits & DoorBellAdapterNormCmdNotFull) {
+			rx_writel(dev, MUnit.ODR, DoorBellAdapterNormCmdNotFull);
+		}
+		else if (bellbits & DoorBellAdapterNormRespNotFull) {
+			rx_writel(dev, MUnit.ODR, DoorBellAdapterNormCmdNotFull);
+			rx_writel(dev, MUnit.ODR, DoorBellAdapterNormRespNotFull);
+		}
+	}
+}
+
+/**
+ *	aac_rx_enable_interrupt	-	Enable event reporting
+ *	@dev: Adapter
+ *	@event: Event to enable
+ *
+ *	Enable event reporting from the i960 for a given event.
+ */
+ 
+static void aac_rx_enable_interrupt(struct aac_dev * dev, u32 event)
+{
+	switch (event) {
+
+	case HostNormCmdQue:
+		dev->irq_mask &= ~(OUTBOUNDDOORBELL_1);
+		break;
+
+	case HostNormRespQue:
+		dev->irq_mask &= ~(OUTBOUNDDOORBELL_2);
+		break;
+
+	case AdapNormCmdNotFull:
+		dev->irq_mask &= ~(OUTBOUNDDOORBELL_3);
+		break;
+
+	case AdapNormRespNotFull:
+		dev->irq_mask &= ~(OUTBOUNDDOORBELL_4);
+		break;
+	}
+}
+
+/**
+ *	aac_rx_disable_interrupt	-	Disable event reporting
+ *	@dev: Adapter
+ *	@event: Event to enable
+ *
+ *	Disable event reporting from the i960 for a given event.
+ */
+
+static void aac_rx_disable_interrupt(struct aac_dev *dev, u32 event)
+{
+	switch (event) {
+
+	case HostNormCmdQue:
+		dev->irq_mask |= (OUTBOUNDDOORBELL_1);
+		break;
+
+	case HostNormRespQue:
+		dev->irq_mask |= (OUTBOUNDDOORBELL_2);
+		break;
+
+	case AdapNormCmdNotFull:
+		dev->irq_mask |= (OUTBOUNDDOORBELL_3);
+		break;
+
+	case AdapNormRespNotFull:
+		dev->irq_mask |= (OUTBOUNDDOORBELL_4);
+		break;
+	}
+}
+
+/**
+ *	rx_sync_cmd	-	send a command and wait
+ *	@dev: Adapter
+ *	@command: Command to execute
+ *	@p1: first parameter
+ *	@ret: adapter status
+ *
+ *	This routine will send a synchronous comamnd to the adapter and wait 
+ *	for its	completion.
+ */
+
+static int rx_sync_cmd(struct aac_dev *dev, u32 command, u32 p1, u32 *status)
+{
+	unsigned long start;
+	int ok;
+	/*
+	 *	Write the command into Mailbox 0
+	 */
+	rx_writel(dev, InboundMailbox0, cpu_to_le32(command));
+	/*
+	 *	Write the parameters into Mailboxes 1 - 4
+	 */
+	rx_writel(dev, InboundMailbox1, cpu_to_le32(p1));
+	rx_writel(dev, InboundMailbox2, 0);
+	rx_writel(dev, InboundMailbox3, 0);
+	rx_writel(dev, InboundMailbox4, 0);
+	/*
+	 *	Clear the synch command doorbell to start on a clean slate.
+	 */
+	rx_writel(dev, OutboundDoorbellReg, OUTBOUNDDOORBELL_0);
+	/*
+	 *	Disable doorbell interrupts
+	 */
+	rx_writeb(dev, MUnit.OIMR, rx_readb(dev, MUnit.OIMR) | 0x04);
+	/*
+	 *	Force the completion of the mask register write before issuing
+	 *	the interrupt.
+	 */
+	rx_readb (dev, MUnit.OIMR);
+	/*
+	 *	Signal that there is a new synch command
+	 */
+	rx_writel(dev, InboundDoorbellReg, INBOUNDDOORBELL_0);
+
+	ok = 0;
+	start = jiffies;
+
+	/*
+	 *	Wait up to 30 seconds
+	 */
+	while (time_before(jiffies, start+30*HZ)) 
+	{
+		udelay(5);	/* Delay 5 microseconds to let Mon960 get info. */
+		/*
+		 *	Mon960 will set doorbell0 bit when it has completed the command.
+		 */
+		if (rx_readl(dev, OutboundDoorbellReg) & OUTBOUNDDOORBELL_0) {
+			/*
+			 *	Clear the doorbell.
+			 */
+			rx_writel(dev, OutboundDoorbellReg, OUTBOUNDDOORBELL_0);
+			ok = 1;
+			break;
+		}
+		/*
+		 *	Yield the processor in case we are slow 
+		 */
+		set_current_state(TASK_UNINTERRUPTIBLE);
+		schedule_timeout(1);
+	}
+	if (ok != 1) {
+		/*
+		 *	Restore interrupt mask even though we timed out
+		 */
+		rx_writeb(dev, MUnit.OIMR, rx_readl(dev, MUnit.OIMR) & 0xfb);
+		return -ETIMEDOUT;
+	}
+	/*
+	 *	Pull the synch status from Mailbox 0.
+	 */
+	*status = le32_to_cpu(rx_readl(dev, IndexRegs.Mailbox[0]));
+	/*
+	 *	Clear the synch command doorbell.
+	 */
+	rx_writel(dev, OutboundDoorbellReg, OUTBOUNDDOORBELL_0);
+	/*
+	 *	Restore interrupt mask
+	 */
+	rx_writeb(dev, MUnit.OIMR, rx_readl(dev, MUnit.OIMR) & 0xfb);
+	return 0;
+
+}
+
+/**
+ *	aac_rx_interrupt_adapter	-	interrupt adapter
+ *	@dev: Adapter
+ *
+ *	Send an interrupt to the i960 and breakpoint it.
+ */
+
+static void aac_rx_interrupt_adapter(struct aac_dev *dev)
+{
+	u32 ret;
+	rx_sync_cmd(dev, BREAKPOINT_REQUEST, 0, &ret);
+}
+
+/**
+ *	aac_rx_notify_adapter		-	send an event to the adapter
+ *	@dev: Adapter
+ *	@event: Event to send
+ *
+ *	Notify the i960 that something it probably cares about has
+ *	happened.
+ */
+
+static void aac_rx_notify_adapter(struct aac_dev *dev, u32 event)
+{
+	switch (event) {
+
+	case AdapNormCmdQue:
+		rx_writel(dev, MUnit.IDR,INBOUNDDOORBELL_1);
+		break;
+	case HostNormRespNotFull:
+		rx_writel(dev, MUnit.IDR,INBOUNDDOORBELL_4);
+		break;
+	case AdapNormRespQue:
+		rx_writel(dev, MUnit.IDR,INBOUNDDOORBELL_2);
+		break;
+	case HostNormCmdNotFull:
+		rx_writel(dev, MUnit.IDR,INBOUNDDOORBELL_3);
+		break;
+	case HostShutdown:
+//		rx_sync_cmd(dev, HOST_CRASHING, 0, 0, 0, 0, &ret);
+		break;
+	case FastIo:
+		rx_writel(dev, MUnit.IDR,INBOUNDDOORBELL_6);
+		break;
+	case AdapPrintfDone:
+		rx_writel(dev, MUnit.IDR,INBOUNDDOORBELL_5);
+		break;
+	default:
+		BUG();
+		break;
+	}
+}
+
+/**
+ *	aac_rx_start_adapter		-	activate adapter
+ *	@dev:	Adapter
+ *
+ *	Start up processing on an i960 based AAC adapter
+ */
+
+static void aac_rx_start_adapter(struct aac_dev *dev)
+{
+	u32 status;
+	struct aac_init *init;
+
+	init = dev->init;
+	init->HostElapsedSeconds = cpu_to_le32(jiffies/HZ);
+	/*
+	 *	Tell the adapter we are back and up and running so it will scan
+	 *	its command queues and enable our interrupts
+	 */
+	dev->irq_mask = (DoorBellPrintfReady | OUTBOUNDDOORBELL_1 | OUTBOUNDDOORBELL_2 | OUTBOUNDDOORBELL_3 | OUTBOUNDDOORBELL_4);
+	/*
+	 *	First clear out all interrupts.  Then enable the one's that we
+	 *	can handle.
+	 */
+	rx_writeb(dev, MUnit.OIMR, 0xff);
+	rx_writel(dev, MUnit.ODR, 0xffffffff);
+//	rx_writeb(dev, MUnit.OIMR, ~(u8)OUTBOUND_DOORBELL_INTERRUPT_MASK);
+	rx_writeb(dev, MUnit.OIMR, 0xfb);
+
+	// We can only use a 32 bit address here
+	rx_sync_cmd(dev, INIT_STRUCT_BASE_ADDRESS, (u32)(ulong)dev->init_pa, &status);
+}
+
+/**
+ *	aac_rx_init	-	initialize an i960 based AAC card
+ *	@dev: device to configure
+ *	@devnum: adapter number
+ *
+ *	Allocate and set up resources for the i960 based AAC variants. The 
+ *	device_interface in the commregion will be allocated and linked 
+ *	to the comm region.
+ */
+
+int aac_rx_init(struct aac_dev *dev, unsigned long num)
+{
+	unsigned long start;
+	unsigned long status;
+	int instance;
+	const char * name;
+
+	dev->devnum = num;
+	instance = dev->id;
+	name     = dev->name;
+
+	/*
+	 *	Map in the registers from the adapter.
+	 */
+	if((dev->regs.rx = (struct rx_registers *)ioremap((unsigned long)dev->scsi_host_ptr->base, 8192))==NULL)
+	{	
+		printk(KERN_WARNING "aacraid: unable to map i960.\n" );
+		return -1;
+	}
+	/*
+	 *	Check to see if the board failed any self tests.
+	 */
+	if (rx_readl(dev, IndexRegs.Mailbox[7]) & SELF_TEST_FAILED) {
+		printk(KERN_ERR "%s%d: adapter self-test failed.\n", dev->name, instance);
+		return -1;
+	}
+	/*
+	 *	Check to see if the board panic'd while booting.
+	 */
+	if (rx_readl(dev, IndexRegs.Mailbox[7]) & KERNEL_PANIC) {
+		printk(KERN_ERR "%s%d: adapter kernel panic'd.\n", dev->name, instance);
+		return -1;
+	}
+	start = jiffies;
+	/*
+	 *	Wait for the adapter to be up and running. Wait up to 3 minutes
+	 */
+	while (!(rx_readl(dev, IndexRegs.Mailbox[7]) & KERNEL_UP_AND_RUNNING)) 
+	{
+		if(time_after(jiffies, start+180*HZ))
+		{
+			status = rx_readl(dev, IndexRegs.Mailbox[7]) >> 16;
+			printk(KERN_ERR "%s%d: adapter kernel failed to start, init status = %ld.\n", dev->name, instance, status);
+			return -1;
+		}
+		set_current_state(TASK_UNINTERRUPTIBLE);
+		schedule_timeout(1);
+	}
+	if (request_irq(dev->scsi_host_ptr->irq, aac_rx_intr, SA_SHIRQ|SA_INTERRUPT, "aacraid", (void *)dev)<0) 
+	{
+		printk(KERN_ERR "%s%d: Interrupt unavailable.\n", name, instance);
+		return -1;
+	}
+	/*
+	 *	Fill in the function dispatch table.
+	 */
+	dev->a_ops.adapter_interrupt = aac_rx_interrupt_adapter;
+	dev->a_ops.adapter_enable_int = aac_rx_enable_interrupt;
+	dev->a_ops.adapter_disable_int = aac_rx_disable_interrupt;
+	dev->a_ops.adapter_notify = aac_rx_notify_adapter;
+	dev->a_ops.adapter_sync_cmd = rx_sync_cmd;
+
+	if (aac_init_adapter(dev) == NULL)
+		return -1;
+	/*
+	 *	Start any kernel threads needed
+	 */
+	dev->thread_pid = kernel_thread((int (*)(void *))aac_command_thread, dev, 0);
+	/*
+	 *	Tell the adapter that all is configured, and it can start
+	 *	accepting requests
+	 */
+	aac_rx_start_adapter(dev);
+	return 0;
+}
diff -Nru a/drivers/scsi/aacraid/sa.c b/drivers/scsi/aacraid/sa.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/scsi/aacraid/sa.c	Mon Oct  7 11:26:06 2002
@@ -0,0 +1,396 @@
+/*
+ *	Adaptec AAC series RAID controller driver
+ *	(c) Copyright 2001 Red Hat Inc.	<alan@redhat.com>
+ *
+ * based on the old aacraid driver that is..
+ * Adaptec aacraid device driver for Linux.
+ *
+ * Copyright (c) 2000 Adaptec, Inc. (aacraid@adaptec.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, 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; see the file COPYING.  If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Module Name:
+ *  sa.c
+ *
+ * Abstract: Drawbridge specific support functions
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/pci.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/blk.h>
+#include <linux/delay.h>
+#include <linux/completion.h>
+#include <asm/semaphore.h>
+#include "scsi.h"
+#include "hosts.h"
+
+#include "aacraid.h"
+
+static void aac_sa_intr(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct aac_dev *dev = dev_id;
+	unsigned short intstat, mask;
+
+	intstat = sa_readw(dev, DoorbellReg_p);
+	/*
+	 *	Read mask and invert because drawbridge is reversed.
+	 *	This allows us to only service interrupts that have been enabled.
+	 */
+	mask = ~(sa_readw(dev, SaDbCSR.PRISETIRQMASK));
+
+	/* Check to see if this is our interrupt.  If it isn't just return */
+
+	if (intstat & mask) {
+		if (intstat & PrintfReady) {
+			aac_printf(dev, le32_to_cpu(sa_readl(dev, Mailbox5)));
+			sa_writew(dev, DoorbellClrReg_p, PrintfReady); /* clear PrintfReady */
+			sa_writew(dev, DoorbellReg_s, PrintfDone);
+		} else if (intstat & DOORBELL_1) {	// dev -> Host Normal Command Ready
+			aac_command_normal(&dev->queues->queue[HostNormCmdQueue]);
+			sa_writew(dev, DoorbellClrReg_p, DOORBELL_1);
+		} else if (intstat & DOORBELL_2) {	// dev -> Host Normal Response Ready
+			aac_response_normal(&dev->queues->queue[HostNormRespQueue]);
+			sa_writew(dev, DoorbellClrReg_p, DOORBELL_2);
+		} else if (intstat & DOORBELL_3) {	// dev -> Host Normal Command Not Full
+			sa_writew(dev, DoorbellClrReg_p, DOORBELL_3);
+		} else if (intstat & DOORBELL_4) {	// dev -> Host Normal Response Not Full
+			sa_writew(dev, DoorbellClrReg_p, DOORBELL_4);
+		}
+	}
+}
+
+/**
+ *	aac_sa_enable_interrupt	-	enable an interrupt event
+ *	@dev: Which adapter to enable.
+ *	@event: Which adapter event.
+ *
+ *	This routine will enable the corresponding adapter event to cause an interrupt on 
+ * 	the host.
+ */
+ 
+void aac_sa_enable_interrupt(struct aac_dev *dev, u32 event)
+{
+	switch (event) {
+
+	case HostNormCmdQue:
+		sa_writew(dev, SaDbCSR.PRICLEARIRQMASK, DOORBELL_1);
+		break;
+
+	case HostNormRespQue:
+		sa_writew(dev, SaDbCSR.PRICLEARIRQMASK, DOORBELL_2);
+		break;
+
+	case AdapNormCmdNotFull:
+		sa_writew(dev, SaDbCSR.PRICLEARIRQMASK, DOORBELL_3);
+		break;
+
+	case AdapNormRespNotFull:
+		sa_writew(dev, SaDbCSR.PRICLEARIRQMASK, DOORBELL_4);
+		break;
+	}
+}
+
+/**
+ *	aac_sa_disable_interrupt	-	disable an interrupt event
+ *	@dev: Which adapter to enable.
+ *	@event: Which adapter event.
+ *
+ *	This routine will enable the corresponding adapter event to cause an interrupt on 
+ * 	the host.
+ */
+
+void aac_sa_disable_interrupt (struct aac_dev *dev, u32 event)
+{
+	switch (event) {
+
+	case HostNormCmdQue:
+		sa_writew(dev, SaDbCSR.PRISETIRQMASK, DOORBELL_1);
+		break;
+
+	case HostNormRespQue:
+		sa_writew(dev, SaDbCSR.PRISETIRQMASK, DOORBELL_2);
+		break;
+
+	case AdapNormCmdNotFull:
+		sa_writew(dev, SaDbCSR.PRISETIRQMASK, DOORBELL_3);
+		break;
+
+	case AdapNormRespNotFull:
+		sa_writew(dev, SaDbCSR.PRISETIRQMASK, DOORBELL_4);
+		break;
+	}
+}
+
+/**
+ *	aac_sa_notify_adapter		-	handle adapter notification
+ *	@dev:	Adapter that notification is for
+ *	@event:	Event to notidy
+ *
+ *	Notify the adapter of an event
+ */
+ 
+void aac_sa_notify_adapter(struct aac_dev *dev, u32 event)
+{
+	switch (event) {
+
+	case AdapNormCmdQue:
+		sa_writew(dev, DoorbellReg_s,DOORBELL_1);
+		break;
+	case HostNormRespNotFull:
+		sa_writew(dev, DoorbellReg_s,DOORBELL_4);
+		break;
+	case AdapNormRespQue:
+		sa_writew(dev, DoorbellReg_s,DOORBELL_2);
+		break;
+	case HostNormCmdNotFull:
+		sa_writew(dev, DoorbellReg_s,DOORBELL_3);
+		break;
+	case HostShutdown:
+		//sa_sync_cmd(dev, HOST_CRASHING, 0, &ret);
+		break;
+	case FastIo:
+		sa_writew(dev, DoorbellReg_s,DOORBELL_6);
+		break;
+	case AdapPrintfDone:
+		sa_writew(dev, DoorbellReg_s,DOORBELL_5);
+		break;
+	default:
+		BUG();
+		break;
+	}
+}
+
+
+/**
+ *	sa_sync_cmd	-	send a command and wait
+ *	@dev: Adapter
+ *	@command: Command to execute
+ *	@p1: first parameter
+ *	@ret: adapter status
+ *
+ *	This routine will send a synchronous comamnd to the adapter and wait 
+ *	for its	completion.
+ */
+
+static int sa_sync_cmd(struct aac_dev *dev, u32 command, u32 p1, u32 *ret)
+{
+	unsigned long start;
+ 	int ok;
+	/*
+	 *	Write the Command into Mailbox 0
+	 */
+	sa_writel(dev, Mailbox0, cpu_to_le32(command));
+	/*
+	 *	Write the parameters into Mailboxes 1 - 4
+	 */
+	sa_writel(dev, Mailbox1, cpu_to_le32(p1));
+	sa_writel(dev, Mailbox2, 0);
+	sa_writel(dev, Mailbox3, 0);
+	sa_writel(dev, Mailbox4, 0);
+	/*
+	 *	Clear the synch command doorbell to start on a clean slate.
+	 */
+	sa_writew(dev, DoorbellClrReg_p, DOORBELL_0);
+	/*
+	 *	Signal that there is a new synch command
+	 */
+	sa_writew(dev, DoorbellReg_s, DOORBELL_0);
+
+	ok = 0;
+	start = jiffies;
+
+	while(time_before(jiffies, start+30*HZ))
+	{
+		/*
+		 *	Delay 5uS so that the monitor gets access
+		 */
+		udelay(5);
+		/*
+		 *	Mon110 will set doorbell0 bit when it has 
+		 *	completed the command.
+		 */
+		if(sa_readw(dev, DoorbellReg_p) & DOORBELL_0)  {
+			ok = 1;
+			break;
+		}
+		set_current_state(TASK_UNINTERRUPTIBLE);
+		schedule_timeout(1);
+	}
+
+	if (ok != 1)
+		return -ETIMEDOUT;
+	/*
+	 *	Clear the synch command doorbell.
+	 */
+	sa_writew(dev, DoorbellClrReg_p, DOORBELL_0);
+	/*
+	 *	Pull the synch status from Mailbox 0.
+	 */
+	*ret = le32_to_cpu(sa_readl(dev, Mailbox0));
+	return 0;
+}
+
+/**
+ *	aac_sa_interrupt_adapter	-	interrupt an adapter
+ *	@dev: Which adapter to enable.
+ *
+ *	Breakpoint an adapter.
+ */
+ 
+static void aac_sa_interrupt_adapter (struct aac_dev *dev)
+{
+	u32 ret;
+	sa_sync_cmd(dev, BREAKPOINT_REQUEST, 0, &ret);
+}
+
+/**
+ *	aac_sa_start_adapter		-	activate adapter
+ *	@dev:	Adapter
+ *
+ *	Start up processing on an ARM based AAC adapter
+ */
+
+static void aac_sa_start_adapter(struct aac_dev *dev)
+{
+	u32 ret;
+	struct aac_init *init;
+	/*
+	 * Fill in the remaining pieces of the init.
+	 */
+	init = dev->init;
+	init->HostElapsedSeconds = cpu_to_le32(jiffies/HZ);
+
+	dprintk(("INIT\n"));
+	/*
+	 * Tell the adapter we are back and up and running so it will scan its command
+	 * queues and enable our interrupts
+	 */
+	dev->irq_mask =	(PrintfReady | DOORBELL_1 | DOORBELL_2 | DOORBELL_3 | DOORBELL_4);
+	/*
+	 *	First clear out all interrupts.  Then enable the one's that 
+	 *	we can handle.
+	 */
+	dprintk(("MASK\n"));
+	sa_writew(dev, SaDbCSR.PRISETIRQMASK, cpu_to_le16(0xffff));
+	sa_writew(dev, SaDbCSR.PRICLEARIRQMASK, (PrintfReady | DOORBELL_1 | DOORBELL_2 | DOORBELL_3 | DOORBELL_4));
+	dprintk(("SYNCCMD\n"));
+	/* We can only use a 32 bit address here */
+	sa_sync_cmd(dev, INIT_STRUCT_BASE_ADDRESS, (u32)(ulong)dev->init_pa, &ret);
+}
+
+/**
+ *	aac_sa_init	-	initialize an ARM based AAC card
+ *	@dev: device to configure
+ *	@devnum: adapter number
+ *
+ *	Allocate and set up resources for the ARM based AAC variants. The 
+ *	device_interface in the commregion will be allocated and linked 
+ *	to the comm region.
+ */
+
+int aac_sa_init(struct aac_dev *dev, unsigned long devnum)
+{
+	unsigned long start;
+	unsigned long status;
+	int instance;
+	const char *name;
+
+	dev->devnum = devnum;
+
+	dprintk(("PREINST\n"));
+	instance = dev->id;
+	name     = dev->name;
+
+	/*
+	 *	Map in the registers from the adapter.
+	 */
+	dprintk(("PREMAP\n"));
+
+	if((dev->regs.sa = (struct sa_registers *)ioremap((unsigned long)dev->scsi_host_ptr->base, 8192))==NULL)
+	{	
+		printk(KERN_WARNING "aacraid: unable to map ARM.\n" );
+		return -1;
+	}
+	/*
+	 *	Check to see if the board failed any self tests.
+	 */
+	if (sa_readl(dev, Mailbox7) & SELF_TEST_FAILED) {
+		printk(KERN_WARNING "%s%d: adapter self-test failed.\n", name, instance);
+		return -1;
+	}
+	/*
+	 *	Check to see if the board panic'd while booting.
+	 */
+	if (sa_readl(dev, Mailbox7) & KERNEL_PANIC) {
+		printk(KERN_WARNING "%s%d: adapter kernel panic'd.\n", name, instance);
+		return -1;
+	}
+	start = jiffies;
+	/*
+	 *	Wait for the adapter to be up and running. Wait up to 3 minutes.
+	 */
+	while (!(sa_readl(dev, Mailbox7) & KERNEL_UP_AND_RUNNING)) {
+		if (time_after(start+180*HZ, jiffies)) {
+			status = sa_readl(dev, Mailbox7) >> 16;
+			printk(KERN_WARNING "%s%d: adapter kernel failed to start, init status = %d.\n", name, instance, le32_to_cpu(status));
+			return -1;
+		}
+		set_current_state(TASK_UNINTERRUPTIBLE);
+		schedule_timeout(1);
+	}
+
+	dprintk(("ATIRQ\n"));
+	if (request_irq(dev->scsi_host_ptr->irq, aac_sa_intr, SA_SHIRQ|SA_INTERRUPT, "aacraid", (void *)dev ) < 0) {
+		printk(KERN_WARNING "%s%d: Interrupt unavailable.\n", name, instance);
+		return -1;
+	}
+
+	/*
+	 *	Fill in the function dispatch table.
+	 */
+
+	dev->a_ops.adapter_interrupt = aac_sa_interrupt_adapter;
+	dev->a_ops.adapter_enable_int = aac_sa_enable_interrupt;
+	dev->a_ops.adapter_disable_int = aac_sa_disable_interrupt;
+	dev->a_ops.adapter_notify = aac_sa_notify_adapter;
+	dev->a_ops.adapter_sync_cmd = sa_sync_cmd;
+
+	dprintk(("FUNCDONE\n"));
+
+	if(aac_init_adapter(dev) == NULL)
+		return -1;
+
+	dprintk(("NEWADAPTDONE\n"));
+	/*
+	 *	Start any kernel threads needed
+	 */
+	dev->thread_pid = kernel_thread((int (*)(void *))aac_command_thread, dev, 0);
+	/*
+	 *	Tell the adapter that all is configure, and it can start 
+	 *	accepting requests
+	 */
+	dprintk(("STARTING\n"));
+	aac_sa_start_adapter(dev);
+	dprintk(("STARTED\n"));
+	return 0;
+}
+
diff -Nru a/drivers/scsi/aha152x.c b/drivers/scsi/aha152x.c
--- a/drivers/scsi/aha152x.c	Mon Oct  7 11:25:25 2002
+++ b/drivers/scsi/aha152x.c	Mon Oct  7 11:25:25 2002
@@ -1893,7 +1893,7 @@
 		printk(KERN_ERR "aha152x: done() called outside of command\n");
 }
 
-static struct tq_struct aha152x_tq;
+static struct work_struct aha152x_tq;
 
 /*
  * Run service completions on the card with interrupts enabled.
@@ -1940,9 +1940,8 @@
 	
 	/* Poke the BH handler */
 	HOSTDATA(shpnt)->service++;
-	aha152x_tq.routine = (void *) run;
-	queue_task(&aha152x_tq, &tq_immediate);
-	mark_bh(IMMEDIATE_BH);
+	INIT_WORK(&aha152x_tq, (void *) run, NULL);
+	schedule_work(&aha152x_tq);
 }
 
 /*
diff -Nru a/drivers/scsi/aic7xxx_old.c b/drivers/scsi/aic7xxx_old.c
--- a/drivers/scsi/aic7xxx_old.c	Mon Oct  7 11:25:33 2002
+++ b/drivers/scsi/aic7xxx_old.c	Mon Oct  7 11:25:33 2002
@@ -234,7 +234,6 @@
 #include <linux/pci.h>
 #include <linux/proc_fs.h>
 #include <linux/blk.h>
-#include <linux/tqueue.h>
 #include <linux/init.h>
 #include <linux/spinlock.h>
 #include <linux/smp.h>
diff -Nru a/drivers/scsi/atari_NCR5380.c b/drivers/scsi/atari_NCR5380.c
--- a/drivers/scsi/atari_NCR5380.c	Mon Oct  7 11:25:35 2002
+++ b/drivers/scsi/atari_NCR5380.c	Mon Oct  7 11:25:35 2002
@@ -640,13 +640,11 @@
  * interrupt or bottom half.
  */
 
-#include <linux/tqueue.h>
+#include <linux/workqueue.h>
 #include <linux/interrupt.h>
 
 static volatile int main_running = 0;
-static struct tq_struct NCR5380_tqueue = {
-    routine:	(void (*)(void*))NCR5380_main	/* must have (void *) arg... */
-};
+static DECLARE_WORK(NCR5380_tqueue, (void (*)(void*))NCR5380_main, NULL);
 
 static __inline__ void queue_main(void)
 {
@@ -655,8 +653,7 @@
 	   queue it on the 'immediate' task queue, to be processed
 	   immediately after the current interrupt processing has
 	   finished. */
-	queue_task(&NCR5380_tqueue, &tq_immediate);
-	mark_bh(IMMEDIATE_BH);
+	schedule_work(&NCR5380_tqueue);
     }
     /* else: nothing to do: the running NCR5380_main() will pick up
        any newly queued command. */
diff -Nru a/drivers/scsi/g_NCR5380.c b/drivers/scsi/g_NCR5380.c
--- a/drivers/scsi/g_NCR5380.c	Mon Oct  7 11:25:26 2002
+++ b/drivers/scsi/g_NCR5380.c	Mon Oct  7 11:25:26 2002
@@ -779,15 +779,15 @@
 	Scsi_Device *dev;
 	extern const char *const scsi_device_types[MAX_SCSI_DEVICE_CODE];
 #endif
-	save_flags(flags);
-	cli();
 
+	/* For now this is constant so we may walk it */
 	for (scsi_ptr = first_instance; scsi_ptr; scsi_ptr = scsi_ptr->next)
 		if (scsi_ptr->host_no == hostno)
 			break;
 	NCR5380_setup(scsi_ptr);
 	hostdata = (struct NCR5380_hostdata *) scsi_ptr->hostdata;
 
+	spin_lock_irqsave(scsi_ptr->host_lock, flags);
 	PRINTP("SCSI host number %d : %s\n" ANDP scsi_ptr->host_no ANDP scsi_ptr->hostt->name);
 	PRINTP("Generic NCR5380 driver version %d\n" ANDP GENERIC_NCR5380_PUBLIC_RELEASE);
 	PRINTP("NCR5380 core version %d\n" ANDP NCR5380_PUBLIC_RELEASE);
@@ -874,7 +874,7 @@
 	len -= offset;
 	if (len > length)
 		len = length;
-	restore_flags(flags);
+	spin_unlock_irqrestore(scsi_ptr->host_lock, flags);
 	return len;
 }
 
diff -Nru a/drivers/scsi/g_NCR5380.h b/drivers/scsi/g_NCR5380.h
--- a/drivers/scsi/g_NCR5380.h	Mon Oct  7 11:25:25 2002
+++ b/drivers/scsi/g_NCR5380.h	Mon Oct  7 11:25:25 2002
@@ -48,7 +48,9 @@
 int generic_NCR5380_detect(Scsi_Host_Template *);
 int generic_NCR5380_release_resources(struct Scsi_Host *);
 int generic_NCR5380_queue_command(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
-int generic_NCR5380_reset(Scsi_Cmnd *, unsigned int);
+int generic_NCR5380_bus_reset(Scsi_Cmnd *);
+int generic_NCR5380_host_reset(Scsi_Cmnd *);
+int generic_NCR5380_device_reset(Scsi_Cmnd *);
 int notyet_generic_proc_info (char *buffer ,char **start, off_t offset,
                      int length, int hostno, int inout);
 const char* generic_NCR5380_info(struct Scsi_Host *);
@@ -77,8 +79,10 @@
 	release:        generic_NCR5380_release_resources,		\
 	info:           (void *)generic_NCR5380_info,			\
 	queuecommand:   generic_NCR5380_queue_command,			\
-	abort:          generic_NCR5380_abort,				\
-	reset:          generic_NCR5380_reset, 				\
+	eh_abort_handler:generic_NCR5380_abort,				\
+	eh_bus_reset_handler:generic_NCR5380_bus_reset,			\
+	eh_device_reset_handler:generic_NCR5380_device_reset,		\
+	eh_host_reset_handler:generic_NCR5380_host_reset,			\
 	bios_param:     NCR5380_BIOSPARAM,				\
 	can_queue:      CAN_QUEUE,					\
         this_id:        7,						\
@@ -154,7 +158,9 @@
 #define do_NCR5380_intr do_generic_NCR5380_intr
 #define NCR5380_queue_command generic_NCR5380_queue_command
 #define NCR5380_abort generic_NCR5380_abort
-#define NCR5380_reset generic_NCR5380_reset
+#define NCR5380_bus_reset generic_NCR5380_bus_reset
+#define NCR5380_device_reset generic_NCR5380_device_reset
+#define NCR5380_host_reset generic_NCR5380_host_reset
 #define NCR5380_pread generic_NCR5380_pread
 #define NCR5380_pwrite generic_NCR5380_pwrite
 #define NCR5380_proc_info notyet_generic_proc_info
diff -Nru a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c
--- a/drivers/scsi/hosts.c	Mon Oct  7 11:25:25 2002
+++ b/drivers/scsi/hosts.c	Mon Oct  7 11:25:25 2002
@@ -209,8 +209,9 @@
     retval->ehandler = NULL;    /* Initial value until the thing starts up. */
     retval->eh_notify   = NULL;    /* Who we notify when we exit. */
 
+    retval->max_host_blocked = tpnt->max_host_blocked ? tpnt->max_host_blocked : SCSI_DEFAULT_HOST_BLOCKED;
 
-    retval->host_blocked = FALSE;
+    retval->host_blocked = 0;
     retval->host_self_blocked = FALSE;
 
 #ifdef DEBUG
diff -Nru a/drivers/scsi/hosts.h b/drivers/scsi/hosts.h
--- a/drivers/scsi/hosts.h	Mon Oct  7 11:25:29 2002
+++ b/drivers/scsi/hosts.h	Mon Oct  7 11:25:29 2002
@@ -297,6 +297,19 @@
      */
     char *proc_name;
 
+    /*
+     * countdown for host blocking with no commands outstanding
+     */
+    unsigned int max_host_blocked;
+
+    /*
+     * Default value for the blocking.  If the queue is empty, host_blocked
+     * counts down in the request_fn until it restarts host operations as
+     * zero is reached.  
+     *
+     * FIXME: This should probably be a value in the template */
+    #define SCSI_DEFAULT_HOST_BLOCKED	7
+
 } Scsi_Host_Template;
 
 /*
@@ -395,11 +408,6 @@
     unsigned use_blk_tcq:1;
 
     /*
-     * Host has rejected a command because it was busy.
-     */
-    unsigned host_blocked:1;
-
-    /*
      * Host has requested that no further requests come through for the
      * time being.
      */
@@ -417,6 +425,16 @@
      */
     unsigned some_device_starved:1;
    
+    /*
+     * Host has rejected a command because it was busy.
+     */
+    unsigned int host_blocked;
+
+    /*
+     * Value host_blocked counts down from
+     */
+    unsigned int max_host_blocked;
+
     void (*select_queue_depths)(struct Scsi_Host *, Scsi_Device *);
 
     /*
diff -Nru a/drivers/scsi/imm.c b/drivers/scsi/imm.c
--- a/drivers/scsi/imm.c	Mon Oct  7 11:25:39 2002
+++ b/drivers/scsi/imm.c	Mon Oct  7 11:25:39 2002
@@ -36,7 +36,7 @@
     int mode;			/* Transfer mode                */
     int host;			/* Host number (for proc)       */
     Scsi_Cmnd *cur_cmd;		/* Current queued command       */
-    struct tq_struct imm_tq;	/* Polling interrupt stuff       */
+    struct work_struct imm_tq;		/* Polling interrupt stuff       */
     unsigned long jstart;	/* Jiffies at start             */
     unsigned failed:1;		/* Failure flag                 */
     unsigned dp:1;		/* Data phase present           */
@@ -51,7 +51,6 @@
 	mode:		IMM_AUTODETECT,	\
 	host:		-1,		\
 	cur_cmd:	NULL,		\
-	imm_tq:		{ routine: imm_interrupt },    \
 	jstart:		0,		\
 	failed:		0,		\
 	dp:		0,		\
@@ -896,9 +895,8 @@
 	return;
     }
     if (imm_engine(tmp, cmd)) {
-	tmp->imm_tq.data = (void *) tmp;
-	tmp->imm_tq.sync = 0;
-	queue_task(&tmp->imm_tq, &tq_timer);
+	INIT_WORK(&tmp->imm_tq, imm_interrupt, (void *)tmp);
+	schedule_delayed_work(&tmp->imm_tq, 1);
 	return;
     }
     /* Command must of completed hence it is safe to let go... */
@@ -1103,10 +1101,8 @@
 
     imm_pb_claim(host_no);
 
-    imm_hosts[host_no].imm_tq.data = imm_hosts + host_no;
-    imm_hosts[host_no].imm_tq.sync = 0;
-    queue_task(&imm_hosts[host_no].imm_tq, &tq_immediate);
-    mark_bh(IMMEDIATE_BH);
+    INIT_WORK(&imm_hosts[host_no].imm_tq, imm_interrupt, imm_hosts + host_no);
+    schedule_work(&imm_hosts[host_no].imm_tq);
 
     return 0;
 }
diff -Nru a/drivers/scsi/inia100.c b/drivers/scsi/inia100.c
--- a/drivers/scsi/inia100.c	Mon Oct  7 11:25:29 2002
+++ b/drivers/scsi/inia100.c	Mon Oct  7 11:25:29 2002
@@ -208,7 +208,7 @@
 	/*
 	 * PCI-bus probe.
 	 */
-	if (pcibios_present()) {
+	if (pci_present()) {
 		/*
 		 * Note: I removed the struct pci_device_list stuff since this
 		 * driver only cares about one device ID.  If that changes in
diff -Nru a/drivers/scsi/mac_NCR5380.c b/drivers/scsi/mac_NCR5380.c
--- a/drivers/scsi/mac_NCR5380.c	Mon Oct  7 11:25:37 2002
+++ b/drivers/scsi/mac_NCR5380.c	Mon Oct  7 11:25:37 2002
@@ -657,13 +657,11 @@
  * interrupt or bottom half.
  */
 
-#include <linux/tqueue.h>
+#include <linux/workqueue.h>
 #include <linux/interrupt.h>
 
 static volatile int main_running = 0;
-static struct tq_struct NCR5380_tqueue = {
-    routine:	(void (*)(void*))NCR5380_main	/* must have (void *) arg... */
-};
+static DECLARE_WORK(NCR5380_tqueue, (void (*)(void*))NCR5380_main, NULL);
 
 static __inline__ void queue_main(void)
 {
@@ -672,8 +670,7 @@
 	   queue it on the 'immediate' task queue, to be processed
 	   immediately after the current interrupt processing has
 	   finished. */
-	queue_task(&NCR5380_tqueue, &tq_immediate);
-	mark_bh(IMMEDIATE_BH);
+	schedule_work(&NCR5380_tqueue);
     }
     /* else: nothing to do: the running NCR5380_main() will pick up
        any newly queued command. */
diff -Nru a/drivers/scsi/megaraid.c b/drivers/scsi/megaraid.c
--- a/drivers/scsi/megaraid.c	Mon Oct  7 11:25:25 2002
+++ b/drivers/scsi/megaraid.c	Mon Oct  7 11:25:25 2002
@@ -491,7 +491,7 @@
 #include <linux/proc_fs.h>
 #include <linux/blk.h>
 #include <linux/wait.h>
-#include <linux/tqueue.h>
+#include <linux/workqueue.h>
 #include <linux/interrupt.h>
 #include <linux/mm.h>
 #include <asm/pgtable.h>
diff -Nru a/drivers/scsi/nsp32.c b/drivers/scsi/nsp32.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/scsi/nsp32.c	Mon Oct  7 11:25:41 2002
@@ -0,0 +1,3526 @@
+/*
+ * NinjaSCSI-32Bi Cardbus, NinjaSCSI-32UDE PCI/CardBus SCSI driver
+ * Copyright (C) 2001, 2002
+ *      YOKOTA Hiroshi <yokota@netlab.is.tsukuba.ac.jp>
+ *      GOTO Masanori <gotom@debian.or.jp>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/ioport.h>
+#include <linux/major.h>
+#include <linux/blk.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/ctype.h>
+
+#include <asm/dma.h>
+#include <asm/system.h>
+#include <asm/io.h>
+
+#include "scsi.h"
+#include "hosts.h"
+#include <scsi/scsi_ioctl.h>
+#include <scsi/scsi.h>
+
+#include "nsp32.h"
+
+
+static int trans_mode = 0;	/* default: BIOS */
+static int auto_param = 0;	/* default: ON */
+MODULE_PARM(trans_mode, "i");
+MODULE_PARM(auto_param, "i");
+MODULE_PARM_DESC(trans_mode, "transfer mode (0: BIOS 1: Async 2: Ultra20M");
+MODULE_PARM_DESC(auto_param, "AutoParameter mode (0: ON 1: OFF)");
+#define ASYNC_MODE    1
+#define ULTRA20M_MODE 2
+
+MODULE_AUTHOR("YOKOTA Hiroshi <yokota@netlab.is.tsukuba.ac.jp>, GOTO Masanori <gotom@debian.or.jp>");
+MODULE_DESCRIPTION("Workbit NinjaSCSI-32Bi/UDE PCI/CardBus SCSI host bus adapter module");
+MODULE_LICENSE("GPL");
+
+static const char *nsp32_release_version = "1.0";
+
+
+/*
+ * structure for DMA/Scatter Gather list
+ */
+#define AUTOPARAM_SIZE		(sizeof(int)*0x15)	/* 4x15H = 0x60 */
+#define NSP_SG_SIZE		SG_ALL
+#define NSP32_SG_END_SGT	0x80000000		/* Last SGT marker */
+#define NSP32_SG_CNT_MASK	0x1FFFF
+
+struct nsp32_sgtable {
+	unsigned long		addr;		/* transfer address */
+	unsigned long		len;		/* transfer length.
+						   Bit (24-32) is for SGT_END */
+};
+
+struct nsp32_sglun {
+	struct nsp32_sgtable sgt[NSP_SG_SIZE+1];	/* SG table */
+};
+
+
+/*
+ * host data structure
+ */
+/* message in/out buffer */
+#define MSGOUTBUF_MAX	13  /* 13 is ok ? */
+#define MSGINBUF_MAX	13
+
+/* flag for trans_method */
+#define NSP32_TRANSFER_BUSMASTER	BIT(0)
+#define NSP32_TRANSFER_MMIO		BIT(1)	/* Not supported yet */
+#define NSP32_TRANSFER_PIO		BIT(2)	/* Not supported yet */
+
+
+/*
+ * SCSI TARGET/LUN definition
+ */
+#define NSP32_HOST_SCSIID	7	/* SCSI initiator is everytime defined as 7 */
+#define MAX_TARGET		8
+#define MAX_LUN			8	/* XXX: In SPI3, max number of LUN is 64. */
+
+
+/*
+ * structure for synchronous transfer negotiation data
+ */
+#define SYNC_NOT_YET	0
+#define SYNC_OK		1
+#define SYNC_NG		2
+
+struct nsp32_sync_table {
+	unsigned char	period_num;	/* period number */
+	unsigned char	ackwidth;	/* ack width designated by period */
+	unsigned char	start_period;	/* search range - start period */
+	unsigned char	end_period;	/* search range - end period */
+};
+
+
+/*
+ * structure for target device static data
+ */
+/* flag for nsp32_target.sync_flag */
+#define SDTR_INITIATOR		BIT(0)	/* sending SDTR from initiator */
+#define SDTR_TARGET		BIT(1)	/* sending SDTR from target */
+#define SDTR_DONE		BIT(2)	/* exchanging SDTR has been processed */
+
+/* syncronous period value for nsp32_target.config_max */
+#define FAST5M			0x32
+#define FAST10M			0x19
+#define ULTRA20M		0x0c
+
+/* flag for nsp32_target.{sync_offset}, period */
+#define ASYNC_OFFSET		0	/* asynchronous transfer */
+#define SYNC_OFFSET		0xf	/* synchronous transfer max offset */
+
+/* syncreg:
+      07 06 05 04 03 02 01 00
+      ---PERIOD-- ---OFFSET--   */
+#define TO_SYNCREG(period, offset)	(period << 4 | offset)
+
+struct nsp32_target {
+	unsigned char	syncreg;	/* value for SYNCREG  */
+	unsigned char	ackwidth;	/* value for ACKWIDTH */
+	unsigned char	offset;		/* sync offset (0-15) */
+	int		sync_flag;	/* SDTR_*, 0 */
+	int		limit_entry;	/* max speed limit entry designated
+					   by EEPROM configuration */
+};
+
+typedef struct _nsp32_hw_data {
+	int IrqNumber;
+	int BaseAddress;
+	int NumAddress;
+#define NSP32_MMIO_OFFSET 0x0800
+	unsigned long MmioAddress;
+	unsigned long length;
+
+	Scsi_Cmnd *CurrentSC;
+
+	struct pci_dev *Pci;
+	const struct pci_device_id *pci_devid;
+	struct Scsi_Host *Host;
+	spinlock_t Lock;
+
+	char info_str[100];
+
+	/* allocated memory region */
+	struct nsp32_lunt	*lunt_list;	/* kmalloc region for lunt */
+	struct nsp32_sglun	*sg_list;	/* sglist virtual address */
+	dma_addr_t		sgaddr;		/* physical address of hw_sg_table */
+	unsigned char		*autoparam;	/* auto parameter transfer region */
+	dma_addr_t		apaddr;		/* physical address of autoparam */
+	int 			cur_entry;	/* current sgt entry */
+
+	/* target/LUN */
+	struct nsp32_lunt	*curlunt;	/* Current connected LUN table */
+	struct nsp32_lunt	*lunt[MAX_TARGET][MAX_LUN];  /* All LUN table */
+	struct nsp32_target	*curtarget;	/* Current connected SCSI ID */
+	struct nsp32_target	target[MAX_TARGET];	     /* SCSI ID */
+	int			pid;		/* Current connected target ID */
+	int			plun;		/* Current connected target LUN */
+
+	/* behavior setting parameters */
+	int			trans_method;	/* transfer method flag */
+	int			resettime;	/* Reset time */
+	int 			clock;	       	/* clock dividing flag */
+	struct nsp32_sync_table	*synct;		/* sync_table determined by clock */
+	int			syncnum;	/* the max number of synct element */
+
+	/* message buffer */
+        unsigned char	msgoutbuf[MSGOUTBUF_MAX]; /* msgout buffer */
+	char		msgoutlen;		  /* msgoutbuf length */
+	unsigned char	msginbuf[MSGINBUF_MAX];	  /* megin buffer */
+	char		msginlen;		  /* msginbuf length */
+
+
+} nsp32_hw_data;
+static nsp32_hw_data nsp32_data_base;  /* probe <-> detect glue */
+
+
+/*
+ * TIME definition
+ */
+#define RESET_HOLD_TIME		10000	/* reset time in us (SCSI-2 says the
+					   minimum is 25us) */
+#define SEL_TIMEOUT_TIME	10000	/* 250ms defined in SCSI specification
+					   (25.6us/1unit) */
+#define ARBIT_TIMEOUT_TIME	100	/* 100us */
+#define REQSACK_TIMEOUT_TIME	10000	/* max wait time for REQ/SACK assertion
+					   or negation, 10000us == 10ms */
+
+/*
+ * structure for connected LUN dynamic data
+ *
+ * Note: Currently tagged queuing is disabled, each nsp32_lunt holds
+ *       one SCSI command and one state.
+ */
+#define DISCPRIV_OK		BIT(0)		/* DISCPRIV Enable mode */
+#define MSGIN03			BIT(1)		/* Auto Msg In 03 Flag */
+
+struct nsp32_lunt {
+	Scsi_Cmnd		*SCpnt;		/* Current Handling Scsi_Cmnd */
+	unsigned long		save_datp;	/* Save Data Pointer - saved position from initial address */
+	int			msgin03;	/* auto msg in 03 flag */
+	unsigned int		sg_num;		/* Total number of SG entries */
+	int			cur_entry;	/* Current SG entry number */
+	struct nsp32_sglun	*sglun;		/* sg table per lun */
+	long			sglun_paddr;	/* sglun physical address */
+};
+
+
+/*
+ * Period/AckWidth speed conversion table
+ *
+ * Note: This period/ackwidth speed table must be in descending order.
+ */
+static struct nsp32_sync_table nsp32_sync_table_40M[] = {
+     /* {PNo, AW,  SP,   EP}  Speed(MB/s) Period AckWidth */
+	{0x1, 0, 0x0c, 0x0c},	/*  20.0 :  50ns,  25ns */
+	{0x2, 0, 0x0d, 0x18},	/*  13.3 :  75ns,  25ns */
+	{0x3, 1, 0x19, 0x19},	/*  10.0 : 100ns,  50ns */
+	{0x4, 1, 0x1a, 0x1f},	/*   8.0 : 125ns,  50ns */
+	{0x5, 2, 0x20, 0x25},	/*   6.7 : 150ns,  75ns */
+	{0x6, 2, 0x26, 0x31},	/*   5.7 : 175ns,  75ns */
+	{0x7, 3, 0x32, 0x32},	/*   5.0 : 200ns, 100ns */
+	{0x8, 3, 0x33, 0x38},	/*   4.4 : 225ns, 100ns */
+	{0x9, 3, 0x39, 0x3e},	/*   4.0 : 250ns, 100ns */
+};
+static const int nsp32_table_40M_num =
+		sizeof(nsp32_sync_table_40M)/sizeof(struct nsp32_sync_table);
+
+static struct nsp32_sync_table nsp32_sync_table_20M[] = {
+	{0x1, 0, 0x19, 0x19},	/* 10.0 : 100ns,  50ns */
+	{0x2, 0, 0x1a, 0x25},	/*  6.7 : 150ns,  50ns */
+	{0x3, 1, 0x26, 0x32},	/*  5.0 : 200ns, 100ns */
+	{0x4, 1, 0x33, 0x3e},	/*  4.0 : 250ns, 100ns */
+	{0x5, 2, 0x3f, 0x4b},	/*  3.3 : 300ns, 150ns */
+	{0x6, 2, 0x4c, 0x57},	/*  2.8 : 350ns, 150ns */
+	{0x7, 3, 0x58, 0x64},	/*  2.5 : 400ns, 200ns */
+	{0x8, 3, 0x65, 0x70},	/*  2.2 : 450ns, 200ns */
+	{0x9, 3, 0x71, 0x7d},	/*  2.0 : 500ns, 200ns */
+};
+static const int nsp32_table_20M_num =
+		sizeof(nsp32_sync_table_20M)/sizeof(struct nsp32_sync_table);
+
+static struct nsp32_sync_table nsp32_sync_table_pci[] = {
+	{0x1, 0, 0x0c, 0x0f},	/* 16.6 :  60ns,  30ns */
+	{0x2, 0, 0x10, 0x16},	/* 11.1 :  90ns,  30ns */
+	{0x3, 1, 0x17, 0x1e},	/*  8.3 : 120ns,  60ns */
+	{0x4, 1, 0x1f, 0x25},	/*  6.7 : 150ns,  60ns */
+	{0x5, 2, 0x26, 0x2d},	/*  5.6 : 180ns,  90ns */
+	{0x6, 2, 0x2e, 0x34},	/*  4.8 : 210ns,  90ns */
+	{0x7, 3, 0x35, 0x3c},	/*  4.2 : 240ns, 120ns */
+	{0x8, 3, 0x3d, 0x43},	/*  3.7 : 270ns, 120ns */
+	{0x9, 3, 0x44, 0x4b}, 	/*  3.3 : 300ns, 120ns */
+};
+static const int nsp32_table_pci_num =
+		sizeof(nsp32_sync_table_pci)/sizeof(struct nsp32_sync_table);
+
+/*
+ * function declaration
+ */
+static int nsp32_detect(Scsi_Host_Template *);
+static int nsp32_queuecommand(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
+static const char *nsp32_info(struct Scsi_Host *);
+static int nsp32_eh_abort(Scsi_Cmnd *);
+static int nsp32_eh_bus_reset(Scsi_Cmnd *);
+static int nsp32_eh_host_reset(Scsi_Cmnd *);
+static int nsp32_reset(Scsi_Cmnd *, unsigned int);
+static int nsp32_release(struct Scsi_Host *);
+static int nsp32_proc_info(char *, char **, off_t, int, int, int);
+static int __devinit nsp32_probe(struct pci_dev *, const struct pci_device_id *);
+static void __devexit nsp32_remove(struct pci_dev *);
+static int __init init_nsp32(void);
+static void __exit exit_nsp32(void);
+
+static void nsp32_message(char *, int, char *, char *, ...);
+static void nsp32_dmessage(char *, int, int, char *, ...);
+static void nsp32_build_identify(nsp32_hw_data *, Scsi_Cmnd *);
+static void nsp32_build_sdtr(nsp32_hw_data *, unsigned char, unsigned char);
+static void nsp32_build_nop(nsp32_hw_data *);
+static void nsp32_build_reject(nsp32_hw_data *);
+static int nsp32hw_start_selection(Scsi_Cmnd *, nsp32_hw_data *);
+static int nsp32_selection_autoscsi(Scsi_Cmnd *, nsp32_hw_data *);
+static int nsp32_reselection(nsp32_hw_data *, unsigned char);
+static int nsp32hw_setup_sg_table(Scsi_Cmnd *, nsp32_hw_data *);
+static int nsp32hw_init(struct Scsi_Host *);
+static void nsp32_scsi_done(nsp32_hw_data *, Scsi_Cmnd *);
+static int nsp32_busfree_occur(nsp32_hw_data *, unsigned short);
+static void nsp32_adjust_busfree(nsp32_hw_data *, unsigned int);
+static void nsp32_msgout_occur(nsp32_hw_data *);
+static void nsp32_restart_autoscsi(nsp32_hw_data *, unsigned short);
+static void nsp32_msgin_occur(nsp32_hw_data *, unsigned long, unsigned short);
+static void nsp32_analyze_sdtr(nsp32_hw_data *);
+static int nsp32_search_period_entry(nsp32_hw_data *,struct nsp32_target *, unsigned char);
+static void nsp32_set_async(nsp32_hw_data *, struct nsp32_target *);
+static void nsp32_set_max_sync(nsp32_hw_data *, struct nsp32_target *, unsigned char *, unsigned char *);
+static void nsp32_set_sync_entry(nsp32_hw_data *, struct nsp32_target *, int, unsigned char);
+static void nsp32_wait_req(nsp32_hw_data *, int);
+static void nsp32_wait_sack(nsp32_hw_data *, int);
+static void nsp32_sack_assert(nsp32_hw_data *);
+static void nsp32_sack_negate(nsp32_hw_data *);
+static void nsp32_do_bus_reset(nsp32_hw_data *);
+static void do_nsp32_isr(int, void *, struct pt_regs *);
+
+static int nsp32_getprom_param(nsp32_hw_data *);
+static int nsp32_getprom_new(nsp32_hw_data *);
+static int nsp32_getprom_standard(nsp32_hw_data *);
+static int nsp32_prom_read(nsp32_hw_data *, int);
+static void nsp32_prom_start(nsp32_hw_data *);
+static void nsp32_prom_stop(nsp32_hw_data *);
+static void nsp32_prom_write(nsp32_hw_data *, int);
+static int nsp32_prom_fetch(nsp32_hw_data *);
+static inline void nsp32_prom_set(nsp32_hw_data *, int, int);
+static inline int nsp32_prom_get(nsp32_hw_data *, int);
+
+
+/*
+ * max_sectors is currently limited up to 128.
+ */
+static Scsi_Host_Template driver_template = {
+	.proc_name =			"nsp32",
+	.name =				"Workbit NinjaSCSI-32Bi/UDE",
+	.proc_info =			nsp32_proc_info,
+	.detect =			nsp32_detect,
+	.info =				nsp32_info,
+	.queuecommand =			nsp32_queuecommand,
+	.can_queue =			1,
+	.sg_tablesize =			NSP_SG_SIZE,
+	.max_sectors =			128,
+	.cmd_per_lun =			1,
+	.this_id =			7,
+	.use_clustering =		DISABLE_CLUSTERING,
+	.eh_abort_handler =       	nsp32_eh_abort,
+	.eh_device_reset_handler =	NULL,
+	.eh_bus_reset_handler =		nsp32_eh_bus_reset,
+	.eh_host_reset_handler =	nsp32_eh_host_reset,
+	.reset =			nsp32_reset,
+	.release =			nsp32_release,
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,2))
+	.use_new_eh_code =        	1,
+#else
+	/* .highmem_io =		1,	*/
+#endif
+};
+
+#include "nsp32_io.h"
+
+/*
+ * debug, error print
+ */
+#define nsp32_msg(type, args...) \
+	nsp32_message(__FUNCTION__, __LINE__, (type), ## args)
+#define nsp32_dbg(mask, args...) \
+	nsp32_dmessage(__FUNCTION__, __LINE__, (mask), ## args)
+
+#ifndef NSP32_DEBUG
+# define NSP32_DEBUG_MASK		0x000000
+#else
+# define NSP32_DEBUG_MASK		0xffffff
+#endif
+
+#define NSP32_DEBUG_QUEUECOMMAND	0x000001
+#define NSP32_DEBUG_REGISTER		0x000002
+#define NSP32_DEBUG_AUTOSCSI		0x000004
+#define NSP32_DEBUG_INTR		0x000008
+#define NSP32_DEBUG_SGLIST		0x000010
+#define NSP32_DEBUG_BUSFREE		0x000020
+#define NSP32_DEBUG_CDB_CONTENTS	0x000040
+#define NSP32_DEBUG_RESELECTION		0x000080
+#define NSP32_DEBUG_MSGINOCCUR		0x000100
+#define NSP32_DEBUG_EEPROM		0x000200
+#define NSP32_DEBUG_MSGOUTOCCUR		0x000400
+#define NSP32_DEBUG_BUSRESET		0x000800
+#define NSP32_DEBUG_RESTART		0x001000
+#define NSP32_DEBUG_SYNC		0x002000
+#define NSP32_DEBUG_WAIT		0x004000
+#define NSP32_DEBUG_TARGETFLAG		0x008000
+#define NSP32_DEBUG_PROC		0x010000
+#define NSP32_DEBUG_INIT		0x020000
+#define NSP32_SPECIAL_PRINT_REGISTER	0x100000
+
+#define NSP32_DEBUG_BUF_LEN		100
+
+static void nsp32_message(char *func, int line, char *type, char *fmt, ...)
+{
+	va_list args;
+	char buf[NSP32_DEBUG_BUF_LEN];
+
+	va_start(args, fmt);
+	vsprintf(buf, fmt, args);
+	va_end(args);
+
+#ifndef NSP32_DEBUG
+	printk("%snsp32: %s\n", type, buf);
+#else
+	printk("%snsp32: %s (%d): %s\n", type, func, line, buf);
+#endif
+}
+
+static void nsp32_dmessage(char *func, int line, int mask, char *fmt, ...)
+{
+	va_list args;
+	char buf[NSP32_DEBUG_BUF_LEN];
+
+	va_start(args, fmt);
+	vsprintf(buf, fmt, args);
+	va_end(args);
+
+	if (mask & NSP32_DEBUG_MASK) {
+		printk("Ninja: %d %s (%d): %s\n", mask, func, line, buf);
+	}
+}
+
+#ifdef NSP32_DEBUG
+# include "nsp32_debug.c"
+#else
+# define show_command(arg)   /* */
+# define show_busphase(arg)  /* */
+# define show_autophase(arg) /* */
+#endif
+
+#ifdef NSP32_DEBUG
+static int pc_debug = NSP32_DEBUG;
+MODULE_PARM(pc_debug, "i");
+#define DEBUG(n, args...) if (pc_debug>(n)) printk(/*KERN_DEBUG*/ args)
+#else
+#define DEBUG(n, args...)
+#endif
+
+
+/*
+ * IDENTIFY Message
+ */
+static void nsp32_build_identify(nsp32_hw_data *data, Scsi_Cmnd *SCpnt)
+{
+	int pos = data->msgoutlen;
+
+	data->msgoutbuf[pos++] =
+		0x80 |		/* Identify */
+#if 0
+		/* XXX: Auto DiscPriv detection is progressing... */
+		0x40 |		/* DiscPriv */
+#endif
+		SCpnt->lun;	/* LUNTRN */
+
+	data->msgoutlen = pos;
+}
+
+/*
+ * SDTR Message Routine
+ */
+static void nsp32_build_sdtr(nsp32_hw_data *data,
+			     unsigned char period, unsigned char offset)
+{
+	int pos = data->msgoutlen;
+
+	data->msgoutbuf[pos++] = EXTENDED_MESSAGE;
+	data->msgoutbuf[pos++] = EXTENDED_SDTR_LEN;
+	data->msgoutbuf[pos++] = EXTENDED_SDTR;
+	data->msgoutbuf[pos++] = period;
+	data->msgoutbuf[pos++] = offset;
+
+	data->msgoutlen = pos;
+}
+
+/*
+ * No Operation Message
+ */
+static void nsp32_build_nop(nsp32_hw_data *data)
+{
+	int pos = data->msgoutlen;
+
+	if (pos != 0) {
+		nsp32_msg(KERN_WARNING,
+			  "Some messages are already contained!");
+		return;
+	}
+
+	data->msgoutbuf[pos++] = NOP;
+	data->msgoutlen = pos;
+}
+
+/*
+ * Reject Message
+ */
+static void nsp32_build_reject(nsp32_hw_data *data)
+{
+	int pos = data->msgoutlen;
+
+	data->msgoutbuf[pos++] = MESSAGE_REJECT;
+	data->msgoutlen = pos;
+}
+	
+/*
+ * timer
+ */
+#if 0
+static void nsp32_start_timer(Scsi_Cmnd *SCpnt, int time)
+{
+	unsigned int base = SCpnt->host->io_port;
+
+	DEBUG(0, __func__ " time=%d\n", time);
+
+	if (time & (~TIMER_CNT_MASK)) {
+		printk("timer set overflow\n");
+	}
+
+	nsp32_write2(base, TIMER_SET, time & TIMER_CNT_MASK);
+}
+#endif
+
+
+/*
+ * set SCSI command and other parameter to asic, and start selection phase
+ */
+static int nsp32hw_start_selection(Scsi_Cmnd *SCpnt, nsp32_hw_data *data)
+{
+	unsigned int   host_id = SCpnt->host->this_id;
+	unsigned int   base    = SCpnt->host->io_port;
+	unsigned char  target  = SCpnt->target;
+	unsigned char  *param  = data->autoparam;
+	unsigned char  phase, arbit;
+	int	       i, time;
+	unsigned int   msgout;
+	unsigned long  l;
+	unsigned short s;
+
+	/*
+	 * check bus free
+	 */
+	phase = nsp32_read1(base, SCSI_BUS_MONITOR);
+	if (phase != BUSMON_BUS_FREE) {
+		nsp32_msg(KERN_WARNING, "bus busy");
+		show_busphase(phase & BUSMON_PHASE_MASK);
+		SCpnt->result = DID_BUS_BUSY << 16;
+		return FALSE;
+	}
+
+	/*
+	 * message out
+	 *
+	 * Note: If the range of msgoutlen is 1 - 3, fill scsi_msgout.
+	 *       over 3 messages needs another routine.
+	 */
+	if (data->msgoutlen == 0) {
+		nsp32_msg(KERN_ERR, "SCSI MsgOut without any message!");
+		SCpnt->result = DID_ERROR << 16;
+		return FALSE;
+	} else if (data->msgoutlen > 0 && data->msgoutlen <= 3) {
+		msgout = 0;
+		for (i=0; i<data->msgoutlen; i++) {
+			/*
+			 * the sending order of the message is:
+			 *  MCNT 3: MSG#0 -> MSG#1 -> MSG#2
+			 *  MCNT 2:          MSG#1 -> MSG#2
+			 *  MCNT 1:                   MSG#2    
+			 */
+			msgout >>= 8;
+			msgout |= (unsigned int)(data->msgoutbuf[i] << 24);
+		}
+		msgout |= MV_VALID;	/* MV valid */
+		msgout |= (unsigned int)data->msgoutlen; /* len */
+	} else {
+		/* data->msgoutlen > 3 */
+		msgout = 0;
+	}
+
+	/*
+	 * setup asic parameter
+	 */
+	memset(param, 0, AUTOPARAM_SIZE);
+
+	/* cdb */
+	for (i=0; i<SCpnt->cmd_len; i++) {
+		param[4*i] = SCpnt->cmnd[i];
+	}
+
+	/* message out */
+	param[4*0x10 +0] = (msgout & 0x000000ff) >> 0;
+	param[4*0x10 +1] = (msgout & 0x0000ff00) >> 8;
+	param[4*0x10 +2] = (msgout & 0x00ff0000) >> 16;
+	param[4*0x10 +3] = (msgout & 0xff000000) >> 24;
+
+	/* syncreg, ackwidth, target id, sampleing rate */
+	param[4*0x11 +0] = data->curtarget->syncreg;
+	param[4*0x11 +1] = data->curtarget->ackwidth;
+	param[4*0x11 +2] = BIT(host_id) | BIT(target);
+	param[4*0x11 +3] = 0;
+
+	/* command control */
+	s = (CLEAR_CDB_FIFO_POINTER | AUTOSCSI_START |
+	     AUTO_MSGIN_00_OR_04 | AUTO_MSGIN_02 | AUTO_ATN);
+	param[4*0x12 +0] = (s & 0x00ff) >> 0;
+	param[4*0x12 +1] = (s & 0xff00) >> 8;
+
+	/* transfer control */
+	s = 0;
+	switch (data->trans_method) {
+	case NSP32_TRANSFER_BUSMASTER:
+		s |= BM_START;
+		break;
+	case NSP32_TRANSFER_MMIO:
+		s |= CB_MMIO_MODE;
+		break;
+	case NSP32_TRANSFER_PIO:
+		s |= CB_IO_MODE;
+		break;
+	default:
+		nsp32_msg(KERN_ERR, "unknown trans_method");
+	}
+	/*
+	 * ORed BLIEND_MODE, FIFO intr is decreased, instead of PCI bus waits.
+	 * For bus master transfer, it's taken off.
+	 */
+	s |= (TRANSFER_GO | ALL_COUNTER_CLR);
+	param[4*0x12 +2] = (s & 0x00ff) >> 0;
+	param[4*0x12 +3] = (s & 0xff00) >> 8;
+
+	/* sg table addr */
+	l = data->curlunt->sglun_paddr;
+	param[4*0x13 +0] = (l & 0x000000ff) >> 0;
+	param[4*0x13 +1] = (l & 0x0000ff00) >> 8;
+	param[4*0x13 +2] = (l & 0x00ff0000) >> 16;
+	param[4*0x13 +3] = (l & 0xff000000) >> 24;
+
+	/*
+	 * transfer parameter to asic
+	 */
+	nsp32_write4(base, SGT_ADR,         virt_to_bus(param));
+	nsp32_write2(base, COMMAND_CONTROL, CLEAR_CDB_FIFO_POINTER |
+		                            AUTO_PARAMETER         );
+
+	/*
+	 * Arbitration Status Check
+	 *	
+	 * Note: Arbitration counter is wait during ARBIT_GO is not lifting.
+	 *	 Using udelay(1) consumes CPU time and system time, but 
+	 *	 arbitration delay time is defined minimal 2.4us in SCSI
+	 *	 specification, thus udelay works as coarse grained wait timer.
+	 */
+	time = 0;
+	do {
+		arbit = nsp32_read1(base, ARBIT_STATUS);
+		nsp32_dbg(NSP32_DEBUG_AUTOSCSI, "arbit=0x%x", arbit);
+	} while ((arbit & (ARBIT_WIN | ARBIT_FAIL)) == 0 &&
+		 (time++ <= 1000));
+
+	nsp32_dbg(NSP32_DEBUG_AUTOSCSI,
+		  "arbit: 0x%x, delay time: %d", arbit, time);
+
+	if (arbit & ARBIT_WIN) {
+		SCpnt->result = DID_OK << 16;
+		/* PCI LED on! */
+		nsp32_index_write1(base, EXT_PORT, LED_ON);
+	} else if (arbit & ARBIT_FAIL) {
+		SCpnt->result = DID_BUS_BUSY << 16;
+		nsp32_write1(base, SET_ARBIT, ARBIT_CLEAR);
+		return FALSE;
+	} else {
+		/* unknown error or ARBIT_GO timeout */
+		nsp32_dbg(NSP32_DEBUG_AUTOSCSI, "arbit fail");
+		SCpnt->result = DID_NO_CONNECT << 16;
+		nsp32_write1(base, SET_ARBIT, ARBIT_CLEAR);
+		return FALSE;
+        }
+
+	/*
+	 * clear Arbit
+	 */
+	nsp32_write1(base, SET_ARBIT, ARBIT_CLEAR);
+
+	return TRUE;
+}
+
+
+/*
+ * Selection with AUTO SCSI (without AUTO PARAMETER)
+ */
+static int nsp32_selection_autoscsi(Scsi_Cmnd *SCpnt, nsp32_hw_data *data)
+{
+	unsigned char	phase;
+	unsigned char	arbit;
+	int		status;
+	int		i;
+	unsigned short	command	= 0;
+	int		time = 0;
+	unsigned int	msgout = 0;
+	unsigned short	execph;
+	unsigned int	base = data->BaseAddress;
+
+	/*
+	 * IRQ disable
+	 */
+	nsp32_write2(base, IRQ_CONTROL, IRQ_CONTROL_ALL_IRQ_MASK);
+
+	/*
+	 * check bus line
+	 */
+	phase = nsp32_read1(base, SCSI_BUS_MONITOR);
+	if(((phase & BUSMON_BSY) == 1) ||
+	   (phase & BUSMON_SEL) == 1) {
+		nsp32_msg(KERN_WARNING, "bus busy");
+		SCpnt->result = DID_BUS_BUSY << 16;
+		status = 1;
+		goto out;
+        }
+
+	/*
+	 * clear execph
+	 */
+	execph = nsp32_read2(base, SCSI_EXECUTE_PHASE);
+
+	/*
+	 * clear FIFO counter to set CDBs
+	 */
+	nsp32_write2(base, COMMAND_CONTROL, CLEAR_CDB_FIFO_POINTER);
+
+	/*
+	 * set CDB0 - CDB15
+	 */
+	for (i=0; i<SCpnt->cmd_len; i++) {
+		nsp32_write1(base, COMMAND_DATA, SCpnt->cmnd[i]);
+        }
+	nsp32_dbg(NSP32_DEBUG_CDB_CONTENTS, "CDB[0]=[0x%x]", SCpnt->cmnd[i]);
+
+	/*
+	 * set SCSIOUT LATCH(initiator)/TARGET(target) (ORed) ID
+	 */
+	nsp32_write1(base, SCSI_OUT_LATCH_TARGET_ID,
+		((1 << NSP32_HOST_SCSIID) | (1 << SCpnt->target)));
+
+	/*
+	 * set SCSI MSGOUT REG
+	 *
+	 * Note: If the range of msgoutlen is 1 - 3, fill scsi_msgout.
+	 *       over 3 messages needs another routine.
+	 */
+	if (data->msgoutlen == 0) {
+		nsp32_msg(KERN_ERR, 
+			  "SCSI MsgOut without any message!");
+		SCpnt->result = DID_ERROR << 16;
+		status = 1;
+		goto out;
+	} else if (data->msgoutlen > 0 && data->msgoutlen <= 3) {
+		msgout = 0;
+		for (i=0; i<data->msgoutlen; i++) {
+			/*
+			 * the sending order of the message is:
+			 *  MCNT 3: MSG#0 -> MSG#1 -> MSG#2
+			 *  MCNT 2:          MSG#1 -> MSG#2
+			 *  MCNT 1:                   MSG#2    
+			 */
+			msgout >>= 8;
+			msgout |= (unsigned int)(data->msgoutbuf[i] << 24);
+		}
+		msgout |= MV_VALID;	/* MV valid */
+		msgout |= (unsigned int)data->msgoutlen; /* len */
+		nsp32_write4(base, SCSI_MSG_OUT, msgout);
+	} else {
+		/* data->msgoutlen > 3 */
+		nsp32_write4(base, SCSI_MSG_OUT, 0);
+	}
+
+	/*
+	 * set selection timeout(= 250ms)
+	 */
+	nsp32_write2(base, SEL_TIME_OUT, SEL_TIMEOUT_TIME);
+
+	/*
+	 * set smpl rate
+	 * 
+	 * TODO: smpl_rate (BASE+0F) is 0 when internal clock = 40MHz.
+	 *      check other internal clock!
+	 */
+	nsp32_write1(base, SREQ_SMPL_RATE, 0);
+
+	/*
+	 * clear Arbit
+	 */
+	nsp32_write1(base, SET_ARBIT, ARBIT_CLEAR);
+
+	/*
+	 * set SYNCREG
+	 * Don't set BM_START_ADR before setting this register.
+	 */
+	nsp32_write1(base, SYNC_REG, data->curtarget->syncreg);
+
+	/*
+	 * set ACKWIDTH
+	 */
+	nsp32_write1(base, ACK_WIDTH, data->curtarget->ackwidth);
+
+	nsp32_dbg(NSP32_DEBUG_AUTOSCSI,
+		  "syncreg=0x%x, ackwidth=0x%x, sgtpaddr=0x%x, id=0x%x",
+		  nsp32_read1(base, SYNC_REG), nsp32_read1(base, ACK_WIDTH),
+		  nsp32_read4(base, SGT_ADR), nsp32_read1(base, SCSI_OUT_LATCH_TARGET_ID));
+	nsp32_dbg(NSP32_DEBUG_AUTOSCSI, "msgoutlen=%d, msgout=0x%x",
+		  data->msgoutlen, msgout);
+
+	/*
+	 * set SGT ADDR (physical address)
+	 */
+	nsp32_write4(base, SGT_ADR, data->curlunt->sglun_paddr);
+
+	/*
+	 * set TRANSFER CONTROL REG
+	 */
+	command = 0;
+	command |= ( TRANSFER_GO | ALL_COUNTER_CLR);
+	if (data->trans_method & NSP32_TRANSFER_BUSMASTER) {
+		if (SCpnt->request_bufflen > 0) {
+			command |= BM_START;
+		}
+	} else if (data->trans_method & NSP32_TRANSFER_MMIO) {
+		command |= CB_MMIO_MODE;
+	} else if (data->trans_method & NSP32_TRANSFER_PIO) {
+		command |= CB_IO_MODE;
+	}
+	nsp32_write2(base, TRANSFER_CONTROL, command);
+
+	/*
+	 * start AUTO SCSI, kick off arbitration
+	 */
+	command = 0;
+	command |= (CLEAR_CDB_FIFO_POINTER
+		    | AUTOSCSI_START
+		    | AUTO_MSGIN_00_OR_04
+		    | AUTO_MSGIN_02 
+		    | AUTO_ATN);
+	nsp32_write2(base, COMMAND_CONTROL, command);
+
+	/*
+	 * Arbitration Status Check
+	 *	
+	 * Note: Arbitration counter is wait during ARBIT_GO is not lifting.
+	 *	 Using udelay(1) consumes CPU time and system time, but 
+	 *	 arbitration delay time is defined minimal 2.4us in SCSI
+	 *	 specification, thus udelay works as coarse grained wait timer.
+	 */
+	time = 0;
+	while(1) {
+		arbit = nsp32_read1(base, ARBIT_STATUS);
+		if(arbit & ARBIT_GO) {
+			udelay(1);
+			time++;
+			if ( time > ARBIT_TIMEOUT_TIME ) {
+				/* something lock up! guess no connection */
+				SCpnt->result = DID_NO_CONNECT << 16;
+				status = FALSE;
+				goto out;
+			}
+		} else {
+			break;
+		}
+	};
+
+	nsp32_dbg(NSP32_DEBUG_AUTOSCSI, "arbit: 0x%x, delay time: %d", arbit, time);
+
+	/*
+	 * check Arbitration Status Result
+	 */
+	if(arbit & ARBIT_WIN) {
+		/* Arbitration succeeded */
+		status = TRUE;
+		SCpnt->result = DID_OK << 16;
+		/* PCI LED on! */
+		nsp32_index_write1(base, EXT_PORT, LED_ON);
+	} else if(arbit & ARBIT_FAIL) {
+		/* Arbitration failed */
+		status = FALSE;
+		SCpnt->result = DID_BUS_BUSY << 16;
+	} else {
+		/* unknown error? */
+		status = FALSE;
+		SCpnt->result = DID_ERROR << 16;
+		SCpnt->result = DID_NO_CONNECT << 16;
+	}
+
+	/*
+	 * clear Arbit
+	 */
+	nsp32_write1(base, SET_ARBIT, ARBIT_CLEAR);
+
+ out:
+	/*
+	 * IRQ enable
+	 */
+	nsp32_write2(base, IRQ_CONTROL, 0);
+
+	return(status);
+}
+
+
+/*
+ * reselection
+ *
+ * Note: This reselection routine is called from msgin_occur,
+ *	 reselection target id&lun must be already set.
+ *	 SCSI-2 says IDENTIFY implies RESTORE_POINTER operation.
+ */
+static int nsp32_reselection(nsp32_hw_data *data, unsigned char newlun)
+{
+	unsigned int base = data->BaseAddress;
+	unsigned char tmpid, newid;
+
+	nsp32_dbg(NSP32_DEBUG_RESELECTION, "enter");
+
+	/*
+	 * calculate reselected SCSI ID
+	 */
+	tmpid = nsp32_read1(base, RESELECT_ID);
+	tmpid &= 0x7f;
+	newid = 0;
+	while (tmpid) {
+		if (tmpid & 1) {
+			break;
+		}
+		tmpid >>= 1;
+		newid++;
+	}
+
+	/*
+	 * If reselected New ID:LUN is not existed
+	 * or current nexus is not existed, unexpected
+	 * reselection is occured. Send reject message.
+	 */
+	if (newid >= MAX_TARGET || newlun >= MAX_LUN) {
+		nsp32_msg(KERN_WARNING, "unknown id/lun");
+		return FALSE;
+	} else if(data->lunt[newid][newlun]->SCpnt == NULL) {
+		nsp32_msg(KERN_WARNING, "no SCSI command is processing");
+		return FALSE;
+	}
+
+	data->pid       = newid;
+	data->plun      = newlun;
+	data->curtarget = &data->target[newid];
+	data->curlunt   = data->lunt[newid][newlun];
+
+	/* reset SACK/SavedACK counter (or ALL clear?) */
+	nsp32_write4(base, CLR_COUNTER, CLRCOUNTER_ALLMASK);
+
+	return TRUE;
+}
+
+
+/*
+ * nsp32hw_setup_sg_table - build scatter gather list for transfer data
+ *			    with bus master.
+ *
+ * Note: NinjaSCSI-32Bi/UDE bus master can not transfer over 64KB at a time.
+ */
+static int nsp32hw_setup_sg_table(Scsi_Cmnd *SCpnt, nsp32_hw_data *data)
+{
+	struct scatterlist *sgl;
+	struct nsp32_sgtable *sgt = data->curlunt->sglun->sgt;
+	int num, i;
+
+	if (SCpnt->request_bufflen == 0) {
+		return TRUE;
+	}
+
+	if (sgt == NULL) {
+		nsp32_dbg(NSP32_DEBUG_SGLIST, "SGT == null");
+		return FALSE;
+	}
+
+	if (SCpnt->use_sg) {
+		sgl = (struct scatterlist *)SCpnt->request_buffer;
+		num = pci_map_sg(data->Pci, sgl, SCpnt->use_sg,
+				 scsi_to_pci_dma_dir(SCpnt->sc_data_direction));
+		for (i=0; i<num; i++) {
+			/*
+			 * Build nsp32_sglist, substitute sg dma addresses.
+			 */
+			sgt[i].addr = cpu_to_le32(sg_dma_address(sgl));
+			sgt[i].len  = cpu_to_le32(sg_dma_len(sgl));
+			sgl++;
+
+			if (sgt[i].len > 65536) {
+				nsp32_msg(KERN_ERR,
+					"can't transfer over 64KB at a time");
+				return FALSE;
+			}
+			nsp32_dbg(NSP32_DEBUG_SGLIST,
+				  "num 0x%x : addr 0x%lx len 0x%x",
+				  i, sgt[i].addr, sgt[i].len);
+		}
+		sgt[num-1].len |= NSP32_SG_END_SGT; /* set end mark */
+	} else {
+		SCpnt->SCp.have_data_in	= pci_map_single(data->Pci,
+			SCpnt->request_buffer, SCpnt->request_bufflen,
+			scsi_to_pci_dma_dir(SCpnt->sc_data_direction));
+		sgt[0].addr = cpu_to_le32(SCpnt->SCp.have_data_in);
+		sgt[0].len  = cpu_to_le32(SCpnt->request_bufflen);
+		sgt[0].len |= NSP32_SG_END_SGT; /* set end mark */
+
+		nsp32_dbg(NSP32_DEBUG_SGLIST, "single : addr 0x%lx len=0x%x",
+			  sgt[0].addr, sgt[0].len);
+	}
+
+	return TRUE;
+}
+
+static int nsp32_queuecommand(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *))
+{
+	nsp32_hw_data *data = (nsp32_hw_data *)SCpnt->host->hostdata;
+	struct nsp32_target *target;
+	struct nsp32_lunt *curlunt;
+	int ret;
+
+	nsp32_dbg(NSP32_DEBUG_QUEUECOMMAND,
+		  "enter. target: 0x%x LUN: 0x%x cmnd: 0x%x cmndlen: 0x%x "
+		  "use_sg: 0x%x reqbuf: 0x%lx reqlen: 0x%x",
+		  SCpnt->target, SCpnt->lun, SCpnt->cmnd[0], SCpnt->cmd_len,
+		  SCpnt->use_sg, SCpnt->request_buffer, SCpnt->request_bufflen);
+
+	if (data->CurrentSC != NULL ) {
+		nsp32_msg(KERN_ERR, "Currentsc != NULL. Cancel this command request");
+		data->CurrentSC = NULL;
+		SCpnt->result   = DID_NO_CONNECT << 16;
+		done(SCpnt);
+
+		return 1;
+	}
+
+	/* check target ID is not same as this initiator ID */
+	if (SCpnt->target == NSP32_HOST_SCSIID) {
+		SCpnt->result = DID_BAD_TARGET << 16;
+		done(SCpnt);
+		return 1;
+	}
+
+	/* check target LUN is allowable value */
+	if (SCpnt->lun >= MAX_LUN) {
+		SCpnt->result = DID_BAD_TARGET << 16;
+		done(SCpnt);
+		return 1;
+	}
+
+	show_command(SCpnt);
+
+	SCpnt->scsi_done     = done;
+	data->CurrentSC      = SCpnt;
+	SCpnt->SCp.Status    = CHECK_CONDITION;
+	SCpnt->SCp.Message   = 0;
+	SCpnt->resid         = 0; //SCpnt->request_bufflen;
+
+	SCpnt->SCp.ptr		    = (char *) SCpnt->request_buffer;
+	SCpnt->SCp.this_residual    = SCpnt->request_bufflen;
+	SCpnt->SCp.buffer	    = NULL;
+	SCpnt->SCp.buffers_residual = 0;
+
+	/* initialize data */
+	data->msgoutlen		= 0;
+	data->msginlen		= 0;
+	curlunt			= data->lunt[SCpnt->target][SCpnt->lun];
+	curlunt->SCpnt		= SCpnt;
+	curlunt->save_datp	= 0;
+	curlunt->msgin03	= FALSE;
+	data->curlunt		= curlunt;
+	data->pid		= SCpnt->target;
+	data->plun		= SCpnt->lun;
+
+	ret = nsp32hw_setup_sg_table(SCpnt, data);
+	if (ret == FALSE) {
+		SCpnt->result = DID_ERROR << 16;
+		nsp32_scsi_done(data, SCpnt);
+	}
+
+	/* Build IDENTIFY */
+	nsp32_build_identify(data, SCpnt);
+
+	/* 
+	 * If target is the first time to transfer after the reset
+	 * (target don't have SDTR_DONE and SDTR_INITIATOR), sync
+	 * message SDTR is needed to do synchronous transfer.
+	 */
+	target = &data->target[SCpnt->target];
+	data->curtarget = target;
+
+	if (!(target->sync_flag & (SDTR_DONE | SDTR_INITIATOR | SDTR_TARGET))) {
+		unsigned char period, offset;
+
+		if (trans_mode != ASYNC_MODE) {
+			nsp32_set_max_sync(data, target, &period, &offset);
+			nsp32_build_sdtr(data, period, offset);
+			target->sync_flag |= SDTR_INITIATOR;
+		} else {
+			nsp32_set_async(data, target);
+			target->sync_flag |= SDTR_DONE;
+		}
+
+		nsp32_dbg(NSP32_DEBUG_QUEUECOMMAND,
+			  "SDTR: entry: %d start_period: 0x%x offset: 0x%x\n",
+			  target->limit_entry, period, offset);
+	} else if (target->sync_flag & SDTR_INITIATOR) {
+		/*
+		 * It was negotiating SDTR with target, sending from the
+		 * initiator, but there are no chance to remove this flag.
+		 * Set async because we don't get proper negotiation.
+		 */
+		nsp32_set_async(data, target);
+		target->sync_flag &= ~SDTR_INITIATOR;
+		target->sync_flag |= SDTR_DONE;
+
+		nsp32_dbg(NSP32_DEBUG_QUEUECOMMAND,
+			  "SDTR_INITIATOR: fall back to async");
+	} else if (target->sync_flag & SDTR_TARGET) {
+		/*
+		 * It was negotiating SDTR with target, sending from target,
+		 * but there are no chance to remove this flag.  Set async
+		 * because we don't get proper negotiation.
+		 */
+		nsp32_set_async(data, target);
+		target->sync_flag &= ~SDTR_TARGET;
+		target->sync_flag |= SDTR_DONE;
+
+		nsp32_dbg(NSP32_DEBUG_QUEUECOMMAND,
+			  "Unknown SDTR from target is reached, fall back to async.");
+	}
+
+	nsp32_dbg(NSP32_DEBUG_TARGETFLAG,
+		  "target: %d sync_flag: 0x%x syncreg: 0x%x ackwidth: 0x%x",
+		  SCpnt->target, target->sync_flag, target->syncreg,
+		  target->ackwidth);
+
+	/* Selection */
+	if (auto_param == 0) {
+		ret = nsp32hw_start_selection(SCpnt, data);
+	} else {
+		ret = nsp32_selection_autoscsi(SCpnt, data);
+	}
+
+	if (ret != TRUE) {
+		nsp32_scsi_done(data, SCpnt);
+		return 1;
+	}
+
+	return 0;
+}
+
+/* initialize asic */
+static int nsp32hw_init(struct Scsi_Host *host)
+{
+	unsigned int  base = host->io_port;
+	unsigned short irq_stat;
+	unsigned long lc_reg;
+	unsigned char power;
+	nsp32_hw_data *data = (nsp32_hw_data *)host->hostdata;
+
+	lc_reg = nsp32_index_read4(base, CFG_LATE_CACHE);
+	if ((lc_reg & 0xff00) == 0) {
+		lc_reg |= (0x20 << 8);
+		nsp32_index_write2(base, CFG_LATE_CACHE, lc_reg & 0xffff);
+	}
+
+	nsp32_write2(base, IRQ_CONTROL,      IRQ_CONTROL_ALL_IRQ_MASK);
+	nsp32_write2(base, TRANSFER_CONTROL, 0);
+	nsp32_write4(base, BM_CNT,           0);
+	nsp32_write2(base, SCSI_EXECUTE_PHASE, 0);
+
+	do {
+		irq_stat = nsp32_read2(base, IRQ_STATUS);
+	} while (irq_stat & IRQSTATUS_ANY_IRQ);
+	nsp32_dbg(NSP32_DEBUG_INIT, "irq_stat 0x%x", irq_stat);
+
+	/*
+	 * Fill FIFO_FULL_SHLD, FIFO_EMPTY_SHLD. Below parameter is
+	 *  designated by specification.
+	 */
+	if ((data->trans_method & NSP32_TRANSFER_PIO) ||
+	    (data->trans_method & NSP32_TRANSFER_MMIO)) {
+		nsp32_index_write1(base, FIFO_FULL_SHLD_COUNT, 0x40);
+	} else if (data->trans_method & NSP32_TRANSFER_BUSMASTER) {
+		nsp32_index_write1(base, FIFO_FULL_SHLD_COUNT, 0x10);
+	}
+	nsp32_index_write1(base, FIFO_EMPTY_SHLD_COUNT, 0x60);
+
+	nsp32_dbg(NSP32_DEBUG_INIT, "full 0x%x emp 0x%x",
+		  nsp32_index_read1(base, FIFO_FULL_SHLD_COUNT),
+		  nsp32_index_read1(base, FIFO_EMPTY_SHLD_COUNT));
+
+	nsp32_index_write1(base, CLOCK_DIV, data->clock);
+	nsp32_index_write1(base, BM_CYCLE, MEMRD_CMD1 | SGT_AUTO_PARA_MEMED_CMD);
+	nsp32_write1(base, PARITY_CONTROL, 0);	/* parity check is disable */
+
+	/*
+	 * initialize I_MISC_WRRD register
+	 * 
+	 * Note: Designated parameters is obeyed as following:
+	 *	MISC_SCSI_DIRECTION_DETECTOR_SELECT: It must be set.
+	 *	MISC_MASTER_TERMINATION_SELECT:      It must be set.
+	 *	MISC_BMREQ_NEGATE_TIMING_SEL:	     It should be set.
+	 *	MISC_AUTOSEL_TIMING_SEL:	     It should be set.
+	 *	MISC_BMSTOP_CHANGE2_NONDATA_PHASE:   It should be set.
+	 *	MISC_DELAYED_BMSTART:		     It's selected for safety.
+	 *
+	 * Note: If MISC_BMSTOP_CHANGE2_NONDATA_PHASE is set, then
+	 *	we have to set TRANSFERCONTROL_BM_START as 0 and set
+	 *	appropriate value before restarting bus master transfer.
+	 */
+	nsp32_index_write2(base, MISC_WR,
+			   (SCSI_DIRECTION_DETECTOR_SELECT |
+			    DELAYED_BMSTART |
+			    MASTER_TERMINATION_SELECT |
+			    BMREQ_NEGATE_TIMING_SEL |
+			    AUTOSEL_TIMING_SEL |
+			    BMSTOP_CHANGE2_NONDATA_PHASE));
+
+	nsp32_index_write1(base, TERM_PWR_CONTROL, 0);
+	power = nsp32_index_read1(base, TERM_PWR_CONTROL);
+	if (!(power & SENSE)) {
+		nsp32_msg(KERN_INFO, "term power on");
+		nsp32_index_write1(base, TERM_PWR_CONTROL, BPWR);
+	}
+
+	nsp32_write2(base, TIMER_SET, TIMER_STOP);
+	nsp32_write2(base, TIMER_SET, TIMER_STOP);
+
+	nsp32_write1(base, SYNC_REG,  0);
+	nsp32_write1(base, ACK_WIDTH, 0);
+	nsp32_write2(base, SEL_TIME_OUT, 10000); /* 25us x10000 = 250ms defined in SCSI */
+
+	/*
+	 * enable to select designated IRQ (except for
+	 * IRQSELECT_SERR, IRQSELECT_PERR, IRQSELECT_BMCNTERR)
+	 */
+	nsp32_index_write2(base, IRQ_SELECT, IRQSELECT_TIMER_IRQ         |
+			                     IRQSELECT_SCSIRESET_IRQ     |
+			                     IRQSELECT_FIFO_SHLD_IRQ     |
+			                     IRQSELECT_RESELECT_IRQ      |
+			                     IRQSELECT_PHASE_CHANGE_IRQ  |
+			                     IRQSELECT_AUTO_SCSI_SEQ_IRQ |
+			                     //IRQSELECT_BMCNTERR_IRQ    |
+			                     IRQSELECT_TARGET_ABORT_IRQ  |
+			                     IRQSELECT_MASTER_ABORT_IRQ );
+	nsp32_write2(base, IRQ_CONTROL, 0);
+
+	/* PCI LED off */
+	nsp32_index_write1(base, EXT_PORT_DDR, LED_OFF);
+	nsp32_index_write1(base, EXT_PORT, LED_OFF);
+
+	return TRUE;
+}
+
+
+/* interrupt routine */
+static void do_nsp32_isr(int irq, void *dev_id, struct pt_regs *regs)
+{
+	nsp32_hw_data *data = dev_id;
+	unsigned int base = data->BaseAddress;
+	Scsi_Cmnd *SCpnt = data->CurrentSC;
+	unsigned short auto_stat, irq_stat, trans_stat;
+	unsigned char busmon, busphase;
+	unsigned long flags;
+	int ret;
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0)
+	struct Scsi_Host *host = data->Host;
+	spin_lock_irqsave(host->host_lock, flags);
+#else
+	spin_lock_irqsave(&io_request_lock, flags);
+#endif
+
+	/*
+	 * IRQ check, then enable IRQ mask
+	 */
+	irq_stat = nsp32_read2(base, IRQ_STATUS);
+	nsp32_dbg(NSP32_DEBUG_INTR, 
+		  "enter IRQ: %d, IRQstatus: 0x%x", irq, irq_stat);
+	/* is this interrupt comes from Ninja asic? */
+	if ((irq_stat & IRQSTATUS_ANY_IRQ) == 0) {
+		nsp32_msg(KERN_INFO, "spurious interrupt: irq other 0x%x", irq_stat);
+		goto out2;
+	}
+	nsp32_write2(base, IRQ_CONTROL, IRQ_CONTROL_ALL_IRQ_MASK);
+
+	busmon = nsp32_read1(base, SCSI_BUS_MONITOR);
+	busphase = busmon & BUSMON_PHASE_MASK;
+
+	trans_stat = nsp32_read2(base, TRANSFER_STATUS);
+	if ((irq_stat == 0xffff) && (trans_stat == 0xffff)) {
+		nsp32_msg(KERN_INFO, "card disconnect");
+		if (data->CurrentSC != NULL) {
+			nsp32_msg(KERN_INFO, "clean up current SCSI command");
+			SCpnt->result = DID_BAD_TARGET << 16;
+			nsp32_scsi_done(data, SCpnt);
+		}
+		goto out;
+	}
+
+	/* Timer IRQ */
+	if (irq_stat & IRQSTATUS_TIMER_IRQ) {
+		DEBUG(0, "timer stop\n");
+		nsp32_write2(base, TIMER_SET, TIMER_STOP);
+		goto out;
+	}
+
+	/* SCSI reset */
+	if (irq_stat & IRQSTATUS_SCSIRESET_IRQ) {
+		nsp32_msg(KERN_INFO, "detected someone do bus reset");
+		nsp32_do_bus_reset(data);
+		if (SCpnt != NULL) {
+			SCpnt->result = DID_RESET << 16;
+			nsp32_scsi_done(data, SCpnt);
+		}
+		goto out;
+	}
+	
+	if (SCpnt == NULL) {
+		nsp32_msg(KERN_WARNING, "SCpnt==NULL this can't be happen\n");
+		nsp32_msg(KERN_WARNING, "irq_stat=0x%x trans_stat=0x%x\n", irq_stat, trans_stat);
+		goto out;
+	}
+
+	/*
+	 * AutoSCSI Interrupt.
+	 * Note: This interrupt is occured when AutoSCSI is finished.  Then
+	 * check SCSIEXECUTEPHASE, and do appropriate action.  Each phases are
+	 * recorded when AutoSCSI sequencer has been processed.
+	 */
+	if(irq_stat & IRQSTATUS_AUTOSCSI_IRQ) {
+		/* getting SCSI executed phase */
+		auto_stat = nsp32_read2(base, SCSI_EXECUTE_PHASE);
+		nsp32_write2(base, SCSI_EXECUTE_PHASE, 0);
+
+		/* Selection Timeout, go busfree phase. */
+		if (auto_stat & SELECTION_TIMEOUT) {
+			nsp32_dbg(NSP32_DEBUG_INTR,
+				  "selection timeout occured");
+
+			SCpnt->result = DID_TIME_OUT << 16;
+			nsp32_scsi_done(data, SCpnt);
+			goto out;
+		}
+
+		if (auto_stat & MSGOUT_PHASE) {
+			/*
+			 * MsgOut phase was processed.
+			 * If MSG_IN_OCCUER is not set, then MsgOut phase is
+			 * completed. Thus, msgoutlen must reset.  Otherwise,
+			 * nothing to do here. If MSG_OUT_OCCUER is occured,
+			 * then we will encounter the condition and check.
+			 */
+			if (!(auto_stat & MSG_IN_OCCUER) &&
+			     (data->msgoutlen <= 3)) {
+				/*
+				 * !MSG_IN_OCCUER && msgoutlen <=3
+				 *   ---> AutoSCSI with MSGOUTreg is processed.
+				 */
+				data->msgoutlen = 0;
+			};
+
+			nsp32_dbg(NSP32_DEBUG_INTR, "MsgOut phase processed");
+		}
+
+		if ((auto_stat & DATA_IN_PHASE) &&
+		    (SCpnt->resid > 0) &&
+		    ((nsp32_read2(base, FIFO_REST_CNT) & FIFO_REST_MASK) != 0)) {
+			printk( "auto+fifo\n");
+			//nsp32_pio_read(SCpnt);
+		}
+
+		if (auto_stat & (DATA_IN_PHASE | DATA_OUT_PHASE)) {
+			/* DATA_IN_PHASE/DATA_OUT_PHASE was processed. */
+			nsp32_dbg(NSP32_DEBUG_INTR,
+				  "Data in/out phase processed");
+
+			/* read BMCNT, SGT pointer addr */
+			nsp32_dbg(NSP32_DEBUG_INTR, "BMCNT=0x%lx", 
+				    nsp32_read4(base, BM_CNT));
+			nsp32_dbg(NSP32_DEBUG_INTR, "addr=0x%lx", 
+				    nsp32_read4(base, SGT_ADR));
+			nsp32_dbg(NSP32_DEBUG_INTR, "SACK=0x%lx", 
+				    nsp32_read4(base, SACK_CNT));
+			nsp32_dbg(NSP32_DEBUG_INTR, "SSACK=0x%lx", 
+				    nsp32_read4(base, SAVED_SACK_CNT));
+			
+		}
+
+		/*
+		 * MsgIn Occur
+		 */
+		if (auto_stat & MSG_IN_OCCUER) {
+			nsp32_msgin_occur(data, irq_stat, auto_stat);
+		}
+
+		/*
+		 * MsgOut Occur
+		 */
+		if (auto_stat & MSG_OUT_OCCUER) {
+			nsp32_msgout_occur(data);
+		}
+
+		/*
+		 * Bus Free Occur
+		 */
+		if (auto_stat & BUS_FREE_OCCUER) {
+			ret = nsp32_busfree_occur(data, auto_stat);
+			if (ret == TRUE) {
+				goto out;
+			}
+		}
+
+		if (auto_stat & STATUS_PHASE) {
+			/*
+			 * Read CSB and substitute CSB for SCpnt->result
+			 * to save status phase stutas byte.
+			 * scsi error handler checks host_byte (DID_*:
+			 * low level driver to indicate status), then checks 
+			 * status_byte (SCSI status byte).
+			 */
+			SCpnt->result =	(int)nsp32_read1(base, SCSI_CSB_IN);
+		}
+
+		if (auto_stat & ILLEGAL_PHASE) {
+			/* Illegal phase is detected. SACK is not back. */
+			nsp32_msg(KERN_WARNING, 
+				  "AUTO SCSI ILLEGAL PHASE OCCUR!!!!");
+
+			/* TODO: currently we don't have any action... bus reset? */
+
+			/*
+			 * To send back SACK, assert, wait, and negate.
+			 */
+			nsp32_sack_assert(data);
+			nsp32_wait_req(data, NEGATE);
+			nsp32_sack_negate(data);
+
+		}
+
+		if (auto_stat & COMMAND_PHASE) {
+			/* nothing to do */
+			nsp32_dbg(NSP32_DEBUG_INTR, "Command phase processed");
+		}
+
+		if (auto_stat & AUTOSCSI_BUSY) {
+			/* AutoSCSI is running */
+		}
+
+		show_autophase(auto_stat);
+	}
+
+	/* FIFO_SHLD_IRQ */
+	if (irq_stat & IRQSTATUS_FIFO_SHLD_IRQ) {
+		nsp32_dbg(NSP32_DEBUG_INTR, "FIFO IRQ");
+
+		switch(busphase) {
+		case BUSPHASE_DATA_OUT:
+			printk( "write\n");
+
+			//nsp32_pio_write(SCpnt);
+
+			break;
+
+		case BUSPHASE_DATA_IN:
+			printk( "read\n");
+
+			//nsp32_pio_read(SCpnt);
+
+			break;
+
+		case BUSPHASE_STATUS:
+			//DEBUG(0, "fifo/status\n");
+
+			SCpnt->SCp.Status = nsp32_read1(base, SCSI_CSB_IN);
+
+			break;
+		default:
+			printk("fifo/other phase?\n");
+			printk("irq_stat=0x%x trans_stat=0x%x\n", irq_stat, trans_stat);
+			show_busphase(busphase);
+			break;
+		}
+
+		goto out;
+	}
+
+	/* Phase Change IRQ */
+	if (irq_stat & IRQSTATUS_PHASE_CHANGE_IRQ) {
+		nsp32_dbg(NSP32_DEBUG_INTR, "phase change IRQ");
+
+		switch(busphase) {
+		case BUSPHASE_MESSAGE_IN:
+			nsp32_dbg(NSP32_DEBUG_INTR, "phase chg/msg in");
+			nsp32_msgin_occur(data, irq_stat, 0);
+			break;
+		default:
+			nsp32_msg(KERN_WARNING, "phase chg/other phase?");
+			nsp32_msg(KERN_WARNING, "irq_stat=0x%x trans_stat=0x%x\n",
+				  irq_stat, trans_stat);
+			show_busphase(busphase);
+			break;
+		}
+		goto out;
+	}
+
+	/* PCI_IRQ */
+	if (irq_stat & IRQSTATUS_PCI_IRQ) {
+		nsp32_dbg(NSP32_DEBUG_INTR, "PCI IRQ occured");
+		/* Do nothing */
+	}
+
+	/* BMCNTERR_IRQ */
+	if (irq_stat & IRQSTATUS_BMCNTERR_IRQ) {
+		nsp32_msg(KERN_ERR, "Received unexpected BMCNTERR IRQ! ");
+		/*
+		 * TODO: To be implemented improving bus master
+		 * transfer reliablity when BMCNTERR is occured in
+		 * AutoSCSI phase described in specification.
+		 */
+	}
+
+#if 0
+	printk("irq_stat=0x%x trans_stat=0x%x\n", irq_stat, trans_stat);
+	show_busphase(busphase);
+#endif
+
+ out:
+	/* disable IRQ mask */
+	nsp32_write2(base, IRQ_CONTROL, 0);
+
+ out2:
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+	spin_unlock_irqrestore(&io_request_lock, flags);
+#else
+	spin_unlock_irqrestore(host->host_lock, flags);
+#endif
+
+	nsp32_dbg(NSP32_DEBUG_INTR, "exit");
+
+	return;
+}
+
+#undef SPRINTF
+#define SPRINTF(args...) \
+        do { if(pos < buffer + length) pos += sprintf(pos, ## args); } while(0)
+static int nsp32_proc_info(char  *buffer,
+			   char **start,
+			   off_t  offset,
+			   int    length,
+			   int    hostno,
+			   int    inout)
+{
+	char *pos = buffer;
+	int thislength;
+	unsigned long flags;
+	nsp32_hw_data *data;
+	struct Scsi_Host *host = NULL;
+	unsigned int base;
+	unsigned char mode_reg;
+
+	/* Write is not supported, just return. */
+	if (inout == TRUE) {
+		return -EINVAL;
+	}
+
+	/* search this HBA host */
+	for (host=scsi_hostlist; host; host=host->next) {
+		if (host->host_no == hostno) {
+			break;
+		}
+	}
+	if (host == NULL) {
+		return -ESRCH;
+	}
+	data = (nsp32_hw_data *)host->hostdata;
+	base = host->io_port;
+
+	SPRINTF("NinjaSCSI-32 status\n\n");
+	SPRINTF("Driver version:        %s\n",		nsp32_release_version);
+	SPRINTF("SCSI host No.:         %d\n",		hostno);
+	SPRINTF("IRQ:                   %d\n",		host->irq);
+	SPRINTF("IO:                    0x%lx-0x%lx\n", host->io_port, host->io_port + host->n_io_port - 1);
+	SPRINTF("MMIO(virtual address): 0x%lx\n",	host->base);
+	SPRINTF("sg_tablesize:          %d\n",		host->sg_tablesize);
+	SPRINTF("Chip revision:         %d\n",		(nsp32_read2(base, INDEX_REG) >> 8) - 0x4f);
+
+	mode_reg = nsp32_index_read1(base, CHIP_MODE);
+
+#ifdef CONFIG_PM
+	//SPRINTF("Power Management:      %s\n",          (mode_reg & OPTF) ? "yes" : "no");
+#endif
+	SPRINTF("OEM:                   %s\n",          nsp32_model[mode_reg & (OEM0|OEM1)]);
+
+	spin_lock_irqsave(&(data->Lock), flags);
+	SPRINTF("CurrentSC:             0x%p\n\n",      data->CurrentSC);
+	spin_unlock_irqrestore(&(data->Lock), flags);
+
+	thislength = pos - (buffer + offset);
+
+	if(thislength < 0) {
+		*start = 0;
+                return 0;
+        }
+
+
+	thislength = MIN(thislength, length);
+	*start = buffer + offset;
+
+	return thislength;
+}
+#undef SPRINTF
+
+/*
+ * Note: n_io_port is defined as 0x7f because I/O register port is
+ *	 assigned as:
+ *	0x800-0x8ff: memory mapped I/O port
+ *	0x900-0xbff: (map same 0x800-0x8ff I/O port image repeatedly)
+ *	0xc00-0xfff: CardBus status registers
+ */
+static int nsp32_detect(Scsi_Host_Template *sht)
+{
+	struct Scsi_Host *host;	/* registered host structure */
+	int ret;
+	nsp32_hw_data *data;
+	int i, j;
+
+	nsp32_dbg(NSP32_DEBUG_REGISTER, "enter");
+
+	/*
+	 * register this HBA as SCSI device
+	 */
+	host = scsi_register(sht, sizeof(nsp32_hw_data));
+	if (host == NULL) {
+		nsp32_msg (KERN_ERR, "failed to scsi register");
+		goto err;
+	}
+
+	/*
+	 * set nsp32_hw_data
+	 */
+	data = (nsp32_hw_data *)host->hostdata;
+	memset(data, 0, sizeof(nsp32_hw_data));
+
+	data->IrqNumber   = nsp32_data_base.IrqNumber;
+	data->BaseAddress = nsp32_data_base.BaseAddress;
+	data->NumAddress  = nsp32_data_base.NumAddress; 
+	data->MmioAddress = nsp32_data_base.MmioAddress;
+	data->Pci         = nsp32_data_base.Pci;
+	data->pci_devid   = nsp32_data_base.pci_devid;
+
+	host->irq         = data->IrqNumber;
+	host->io_port	  = data->BaseAddress;
+	host->unique_id	  = data->BaseAddress;
+	host->n_io_port	  = data->NumAddress;
+	host->base        = data->MmioAddress;
+	scsi_set_pci_device(host, data->Pci);
+
+	data->Host        = host;
+	spin_lock_init(&(data->Lock));
+
+	data->curlunt     = NULL;
+	data->curtarget   = NULL;
+
+	/*
+	 * Bus master transfer mode is supported currently.
+	 */
+	data->trans_method	= NSP32_TRANSFER_BUSMASTER;
+
+	/*
+	 * Set clock div, CLOCK_4 (HBA has external clock, and
+	 * dividing * 100ns/4).
+	 * Currently CLOCK_4 has only tested, not for CLOCK_2/PCICLK yet.
+	 */
+	data->clock = CLOCK_4;
+
+	/*
+	 * Select appropriate nsp32_sync_table and set I_CLOCKDIV.
+	 */
+	switch (data->clock) {
+	case CLOCK_4:
+		/* If data->clock is CLOCK_4, then select 40M sync table. */
+		data->synct = nsp32_sync_table_40M;
+		data->syncnum = nsp32_table_40M_num;
+		break;
+	case CLOCK_2:
+		/* If data->clock is CLOCK_2, then select 20M sync table. */
+		data->synct = nsp32_sync_table_20M;
+		data->syncnum = nsp32_table_20M_num;
+		break;
+	case PCICLK:
+		/* If data->clock is PCICLK, then select pci sync table. */
+		data->synct = nsp32_sync_table_pci;
+		data->syncnum = nsp32_table_pci_num;
+		break;
+	default:
+		nsp32_msg(KERN_WARNING,
+			  "Invalid clock div is selected, set CLOCK_4.");
+		/* Use default value CLOCK_4 */
+		data->clock = CLOCK_4;
+		data->synct = nsp32_sync_table_40M;
+		data->syncnum = nsp32_table_40M_num;
+	}
+
+	/*
+	 * setup nsp32_lunt
+	 */
+	data->lunt_list = (struct nsp32_lunt *)
+		kmalloc(sizeof(struct nsp32_lunt) * MAX_TARGET * MAX_LUN,
+			GFP_KERNEL);
+	if (data->lunt_list == NULL) {
+		nsp32_msg(KERN_ERR, "cannot allocate LUN memory");
+		goto scsi_unregister;
+	}
+	nsp32_dbg(NSP32_DEBUG_REGISTER, "0x%x 0x%x",
+		  data->lunt_list, sizeof(struct nsp32_lunt)*MAX_TARGET*MAX_LUN);
+
+	/*
+	 * setup DMA 
+	 */
+	if (pci_set_dma_mask(data->Pci, 0xffffffffUL)) {
+		nsp32_msg (KERN_ERR, "failed to set PCI DMA mask");
+		goto kfree_lunt;
+	}
+
+	/*
+	 * allocate autoparam DMA resource.
+	 */
+	data->autoparam = pci_alloc_consistent(data->Pci, AUTOPARAM_SIZE, &data->apaddr);
+	if (data->autoparam == NULL) {
+		nsp32_msg(KERN_ERR, "failed to allocate DMA memory");
+		goto kfree_lunt;
+	}
+
+	/*
+	 * allocate scatter-gather DMA resource.
+	 */
+	data->sg_list = pci_alloc_consistent(data->Pci, 
+			(sizeof(struct nsp32_sgtable) * NSP_SG_SIZE * MAX_TARGET * MAX_LUN),
+			 &data->sgaddr);
+	if (data->sg_list == NULL) {
+		nsp32_msg(KERN_ERR, "failed to allocate DMA memory");
+		goto free_autoparam;
+	}
+
+	for (i=0; i<MAX_TARGET; i++) {
+		for (j=0; j<MAX_LUN; j++) {
+			data->lunt[i][j] = data->lunt_list + (i * MAX_LUN + j);
+		}
+	}
+
+	for (i=0; i<MAX_TARGET; i++) {
+		for (j=0; j<MAX_LUN; j++) {
+			struct nsp32_lunt *lp = data->lunt[i][j];
+			lp->sglun = (struct nsp32_sglun *)
+				(data->sg_list + (i * MAX_LUN + j));
+			lp->sglun_paddr = data->sgaddr +
+				(long)((i * MAX_LUN + j) 
+				       * sizeof(struct nsp32_sglun));
+			lp->SCpnt = NULL;
+			lp->save_datp = 0;
+			lp->msgin03 = FALSE;
+			lp->sg_num = 0;
+			lp->cur_entry = 0;
+		}
+	}
+
+	/*
+	 * setup target
+	 */
+	for (i=0; i<MAX_TARGET; i++) {
+		struct nsp32_target *target = &data->target[i];
+
+		target->limit_entry  = 0;
+		target->sync_flag    = 0;
+		nsp32_set_async(data, target);
+	}
+
+	/*
+	 * EEPROM check
+	 */
+	ret = nsp32_getprom_param(data);
+	if (ret == FALSE) {
+		data->resettime = 3;	/* default 3 */
+	}
+
+	/*
+	 * setup HBA
+	 */
+	nsp32hw_init(host);
+
+	snprintf(data->info_str, sizeof(data->info_str),
+		 "NinjaSCSI-32Bi/UDE: irq %d, io 0x%lx+0x%x",
+		 host->irq, host->io_port, host->n_io_port);
+
+	sht->name = data->info_str;
+
+	/*
+	 * SCSI bus reset
+	 *
+	 * Note: It's important to reset SCSI bus in initialization phase.
+	 *     NinjaSCSI-32Bi/UDE HBA EEPROM seems to exchange SDTR when system is
+	 *     coming up, so SCSI devices connected to HBA is set as
+	 *     un-asynchronous mode.  It brings the merit that this HBA is
+	 *     ready to start synchronous transfer without any preparation,
+	 *     but we are difficult to control transfer speed.  In addition,
+	 *     it prevents device transfer speed from effecting EEPROM start-up
+	 *     SDTR.  NinjaSCSI-32Bi/UDE has the feature if EEPROM is set as Auto
+	 *     Mode, then FAST-10M is selected when SCSI devices are connected
+	 *     same or more than 4 devices.  It should be avoided depending on
+	 *     this specification Thus, resetting the SCSI bus restores all
+	 *     connected SCSI devices to asynchronous mode, then this driver
+	 *     put SDTR safely later, and we can control all SCSI device
+	 *     transfer mode.
+	 */
+	nsp32_do_bus_reset(data);
+
+	ret = request_irq(host->irq, do_nsp32_isr, SA_SHIRQ, "nsp32", data);
+	if (ret < 0) {
+		nsp32_msg(KERN_ERR, "Unable to allocate IRQ for NSP32 "
+			  "SCSI PCI controller. Interrupt: %d\n", host->irq);
+		goto free_sg_list;
+	}
+
+        /*
+         * PCI IO register
+         */
+	if(!request_region(host->io_port, host->n_io_port, "nsp32")) {
+		nsp32_msg(KERN_ERR, 
+			  "I/O region 0x%lx+0x%lx is already used",
+			  data->BaseAddress, data->length);
+		goto free_irq;
+        }
+
+	return 1;
+
+ free_irq:
+	free_irq(host->irq, data);
+
+ free_autoparam:
+	pci_free_consistent(data->Pci, AUTOPARAM_SIZE, data->autoparam, data->apaddr);
+	
+ free_sg_list:
+	pci_free_consistent(data->Pci,
+		(sizeof(struct nsp32_sgtable) * NSP_SG_SIZE * MAX_TARGET * MAX_LUN),
+		data->sg_list, data->sgaddr);
+	
+ kfree_lunt:
+	kfree(data->lunt_list);
+
+ scsi_unregister:
+	scsi_unregister(host);
+
+ err:
+	return 0;
+}
+
+static int nsp32_release(struct Scsi_Host *shpnt)
+{
+	nsp32_hw_data *data = (nsp32_hw_data *)shpnt->hostdata;
+
+	if (data->lunt_list) {
+		kfree(data->lunt_list);
+	}
+
+	if (data->autoparam) {
+		pci_free_consistent(data->Pci, AUTOPARAM_SIZE,
+					data->autoparam, data->apaddr);
+	}
+
+	if (data->sg_list) {
+		pci_free_consistent(data->Pci, 
+			(sizeof(struct nsp32_sgtable) * NSP_SG_SIZE * MAX_TARGET * MAX_LUN),
+			data->sg_list, data->sgaddr);
+	}
+
+	DEBUG(0, "free irq\n");
+	if (shpnt->irq) {
+		free_irq(shpnt->irq, data);
+	}
+
+	DEBUG(0, "free io\n");
+	if (shpnt->io_port && shpnt->n_io_port) {
+		release_region(shpnt->io_port, shpnt->n_io_port);
+	}
+
+	if (data->MmioAddress != 0) {
+		iounmap((void *)(data->MmioAddress));
+	}
+
+	return 0;
+}
+
+static const char *nsp32_info(struct Scsi_Host *shpnt)
+{
+	nsp32_hw_data *data = (nsp32_hw_data *)shpnt->hostdata;
+
+	return data->info_str;
+}
+
+
+/*
+ * error handler
+ */
+static int nsp32_reset(Scsi_Cmnd *SCpnt, unsigned int reset_flags)
+{
+	nsp32_dbg(NSP32_DEBUG_BUSRESET, "SCpnt=0x%p why=%d\n", SCpnt, reset_flags);
+
+	nsp32_eh_bus_reset(SCpnt);
+
+	return SCSI_RESET_SUCCESS | SCSI_RESET_BUS_RESET;
+}
+
+static int nsp32_eh_abort(Scsi_Cmnd *SCpnt)
+{
+	nsp32_hw_data *data = (nsp32_hw_data *)SCpnt->host->hostdata;
+	unsigned int base = data->BaseAddress;
+
+	nsp32_msg(KERN_WARNING, "abort");
+
+	if (data->curlunt->SCpnt == NULL) {
+		return (FAILED);
+	}
+
+	if (data->curtarget->sync_flag & (SDTR_INITIATOR | SDTR_TARGET)) {
+		/* reset SDTR negotiation */
+		data->curtarget->sync_flag = 0;
+	}
+
+	nsp32_write2(base, TRANSFER_CONTROL, 0);
+	nsp32_write2(base, BM_CNT, 0);
+
+	return (FAILED);
+}
+
+static int nsp32_eh_bus_reset(Scsi_Cmnd *SCpnt)
+{
+	nsp32_hw_data *data = (nsp32_hw_data *)SCpnt->host->hostdata;
+	unsigned int base = data->BaseAddress;
+
+	nsp32_msg(KERN_INFO, "Bus Reset");	
+	nsp32_dbg(NSP32_DEBUG_BUSRESET, "SCpnt=0x%x", SCpnt);
+
+	nsp32_write2(base, IRQ_CONTROL, IRQ_CONTROL_ALL_IRQ_MASK);
+	nsp32_do_bus_reset(data);
+	nsp32_write2(base, IRQ_CONTROL, 0);
+
+	return SUCCESS;	/* SCSI bus reset is succeeded at any time. */
+}
+
+static void nsp32_do_bus_reset(nsp32_hw_data *data)
+{
+	unsigned int base = data->BaseAddress;
+	unsigned short intrdat;
+	int i;
+
+	/*
+	 * stop all transfer
+	 * clear TRANSFERCONTROL_BM_START
+	 * clear counter
+	 */
+	nsp32_write2(base, TRANSFER_CONTROL, 0);
+	nsp32_write4(base, BM_CNT,           0);
+	nsp32_write4(base, CLR_COUNTER, CLRCOUNTER_ALLMASK);
+
+	/*
+	 * fall back to asynchronous transfer mode
+	 * initialize SDTR negotiation flag
+	 */
+	for (i=0; i<MAX_TARGET; i++) {
+		struct nsp32_target *target = &data->target[i];
+
+		target->sync_flag = 0;
+		nsp32_set_async(data, target);
+	}
+
+	/*
+	 * reset SCSI bus
+	 */
+	nsp32_write1(base, SCSI_BUS_CONTROL, BUSCTL_RST);
+	udelay(RESET_HOLD_TIME);
+	nsp32_write1(base, SCSI_BUS_CONTROL, 0);
+	for(i = 0; i < 5; i++) {
+		intrdat = nsp32_read2(base, IRQ_STATUS); /* dummy read */
+		nsp32_dbg(NSP32_DEBUG_BUSRESET, "irq:1: 0x%x", intrdat);
+        }
+
+	data->CurrentSC = NULL;
+}
+
+static int nsp32_eh_host_reset(Scsi_Cmnd *SCpnt)
+{
+	struct Scsi_Host *host = SCpnt->host;
+	nsp32_hw_data *data = (nsp32_hw_data *)host->hostdata;
+	unsigned int base = data->BaseAddress;
+
+	nsp32_msg(KERN_INFO, "Host Reset");	
+	nsp32_dbg(NSP32_DEBUG_BUSRESET, "SCpnt=0x%x", SCpnt);
+
+	nsp32hw_init(host);
+	nsp32_write2(base, IRQ_CONTROL, IRQ_CONTROL_ALL_IRQ_MASK);
+	nsp32_do_bus_reset(data);
+	nsp32_write2(base, IRQ_CONTROL, 0);
+
+	return SUCCESS;	/* Host reset is succeeded at any time. */
+}
+
+/*
+ * PCI/Cardbus probe/remove routine
+ */
+static int __devinit nsp32_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+	int ret;
+	nsp32_hw_data *data = &nsp32_data_base;
+
+	nsp32_dbg(NSP32_DEBUG_REGISTER, "enter");
+
+        ret = pci_enable_device(pdev);
+	if (ret) {
+		nsp32_msg(KERN_ERR, "failed to enable pci device");
+		return ret;
+	}
+
+	data->Pci = pdev;
+	data->pci_devid = id;
+	data->IrqNumber = pdev->irq;
+	data->BaseAddress = pci_resource_start(pdev, 0);
+	data->NumAddress = pci_resource_len(pdev, 0);
+	data->MmioAddress = (unsigned long)ioremap_nocache(
+		pci_resource_start(pdev, 1), pci_resource_len(pdev, 1));
+
+	pci_set_master(pdev);
+
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,2))
+	scsi_register_host(&driver_template);
+#else
+	scsi_register_module(MODULE_SCSI_HA, &driver_template);
+#endif
+
+	nsp32_msg(KERN_INFO, "nsp32 irq: %i mmio: 0x%lx slot: %s model: %s",
+		  pdev->irq, data->MmioAddress, pdev->slot_name,
+		  nsp32_model[id->driver_data]);
+
+	nsp32_dbg(NSP32_DEBUG_REGISTER, "exit");
+
+	return 0;
+}
+
+static void __devexit nsp32_remove(struct pci_dev *pdev)
+{
+	nsp32_dbg(NSP32_DEBUG_REGISTER, "enter");
+	
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,2))
+	scsi_unregister_host(&driver_template);
+#else
+	scsi_unregister_module(MODULE_SCSI_HA, &driver_template);
+#endif
+}
+
+static struct pci_device_id nsp32_pci_table[] __devinitdata = {
+	{
+		vendor:      PCI_VENDOR_ID_IODATA,
+		device:      PCI_DEVICE_ID_NINJASCSI_32BI_CBSC_II,
+		subvendor:   PCI_ANY_ID,
+		subdevice:   PCI_ANY_ID,
+		driver_data: MODEL_IODATA,
+	},
+	{
+		vendor:      PCI_VENDOR_ID_WORKBIT,
+		device:      PCI_DEVICE_ID_NINJASCSI_32BI_KME,
+		subvendor:   PCI_ANY_ID,
+		subdevice:   PCI_ANY_ID,
+		driver_data: MODEL_KME,
+	},
+	{
+		vendor:      PCI_VENDOR_ID_WORKBIT,
+		device:      PCI_DEVICE_ID_NINJASCSI_32BI_WBT,
+		subvendor:   PCI_ANY_ID,
+		subdevice:   PCI_ANY_ID,
+		driver_data: MODEL_WORKBIT,
+	},
+	{
+		vendor:      PCI_VENDOR_ID_WORKBIT,
+		device:      PCI_DEVICE_ID_WORKBIT_STANDARD,
+		subvendor:   PCI_ANY_ID,
+		subdevice:   PCI_ANY_ID,
+		driver_data: MODEL_PCI_WORKBIT,
+	},
+	{
+		vendor:      PCI_VENDOR_ID_WORKBIT,
+		device:      PCI_DEVICE_ID_NINJASCSI_32BI_LOGITEC,
+		subvendor:   PCI_ANY_ID,
+		subdevice:   PCI_ANY_ID,
+		driver_data: MODEL_EXT_ROM,
+	},
+	{
+		vendor:      PCI_VENDOR_ID_WORKBIT,
+		device:      PCI_DEVICE_ID_NINJASCSI_32BIB_LOGITEC,
+		subvendor:   PCI_ANY_ID,
+		subdevice:   PCI_ANY_ID,
+		driver_data: MODEL_PCI_LOGITEC,
+	},
+	{
+		vendor:      PCI_VENDOR_ID_WORKBIT,
+		device:      PCI_DEVICE_ID_NINJASCSI_32UDE_MELCO,
+		subvendor:   PCI_ANY_ID,
+		subdevice:   PCI_ANY_ID,
+		driver_data: MODEL_PCI_MELCO,
+	},
+	{0,0,},
+};
+MODULE_DEVICE_TABLE(pci, nsp32_pci_table);
+
+static struct pci_driver nsp32_driver = {
+	.name =		"nsp32",
+	.id_table =	nsp32_pci_table,
+	.probe =	nsp32_probe,
+	.remove =	nsp32_remove,
+#ifdef CONFIG_PM
+/*	.suspend =	nsp32_suspend,*/
+/*	.resume =	nsp32_resume,*/
+#endif
+};
+
+static int __init init_nsp32(void) {
+	return pci_module_init(&nsp32_driver);
+}
+
+static void __exit exit_nsp32(void) {
+	pci_unregister_driver(&nsp32_driver);
+}
+
+module_init(init_nsp32);
+module_exit(exit_nsp32);
+
+
+/*
+ * Reset parameters and call scsi_done for data->curlunt.
+ * Be careful setting SCpnt->result = DID_* before calling this function.
+ */
+static void nsp32_scsi_done(nsp32_hw_data *data, Scsi_Cmnd *SCpnt)
+{
+	unsigned int base = data->BaseAddress;
+
+	/*
+	 * unmap pci
+	 */
+	if (SCpnt->request_bufflen == 0) {
+		goto skip;
+	}
+
+	if (SCpnt->use_sg) {
+		pci_unmap_sg(data->Pci,
+			(struct scatterlist *)SCpnt->buffer,
+			SCpnt->use_sg,
+			scsi_to_pci_dma_dir(SCpnt->sc_data_direction));
+	} else {
+		pci_unmap_single(data->Pci,
+			(u32)SCpnt->SCp.have_data_in,
+			SCpnt->request_bufflen,
+			scsi_to_pci_dma_dir(SCpnt->sc_data_direction));
+	}
+
+ skip:
+	/*
+	 * clear TRANSFERCONTROL_BM_START
+	 */
+	nsp32_write2(base, TRANSFER_CONTROL, 0);
+	nsp32_write4(base, BM_CNT, 0);
+
+	/*
+	 * call scsi_done
+	 */
+	(*SCpnt->scsi_done)(SCpnt);
+
+	/*
+	 * reset parameters
+	 */
+	data->curlunt->SCpnt = NULL;
+	data->curlunt = NULL;
+	data->curtarget = NULL;
+	data->CurrentSC = NULL;
+}
+
+
+/*
+ * Bus Free Occur
+ *
+ * Current Phase is BUSFREE. AutoSCSI is automatically execute BUSFREE phase
+ * with ACK reply when below condition is matched:
+ *	MsgIn 00: Command Complete.
+ *	MsgIn 02: Save Data Pointer.
+ *	MsgIn 04: Diconnect.
+ * In other case, unexpected BUSFREE is detected.
+ */
+static int nsp32_busfree_occur(nsp32_hw_data *data, unsigned short execph)
+{
+	Scsi_Cmnd *SCpnt = data->curlunt->SCpnt;
+	unsigned int base = data->BaseAddress;
+
+	nsp32_dbg(NSP32_DEBUG_BUSFREE, "enter");
+
+	nsp32_write4(base, BM_CNT, 0);
+	nsp32_write2(base, TRANSFER_CONTROL, 0);
+
+	/*
+	 * MsgIn 02: Save Data Pointer
+	 *
+	 * VALID:
+	 *   Save Data Pointer is received. Adjust pointer.
+	 *   
+	 * NO-VALID:
+	 *   SCSI-3 says if Save Data Pointer is not received, then we restart
+	 *   processing and we can't adjust any SCSI data pointer in next data
+	 *   phase.
+	 */
+	if (execph & MSGIN_02_VALID) {
+		nsp32_dbg(NSP32_DEBUG_BUSFREE, "MsgIn02_Valid");
+
+		/*
+		 * Check sack_cnt/saved_sack_cnt, then adjust sg table if
+		 * needed.
+		 */
+		if (!(execph & MSGIN_00_VALID) && 
+		    ((execph & DATA_IN_PHASE) || (execph & DATA_OUT_PHASE))) {
+			unsigned int sacklen, s_sacklen;
+
+			/*
+			 * Read SACK count and SAVEDSACK count, then compare.
+			 */
+			sacklen   = nsp32_read4(base, SACK_CNT);
+			s_sacklen = nsp32_read4(base, SAVED_SACK_CNT);
+
+			/*
+			 * If SAVEDSACKCNT == 0, it means SavedDataPointer is
+			 * come after data transfering.
+			 */
+			if (s_sacklen > 0) {
+				/*
+				 * Comparing between sack and savedsack to
+				 * check the condition of AutoMsgIn03.
+				 *
+				 * If they are same, set msgin03 == TRUE,
+				 * COMMANDCONTROL_AUTO_MSGIN_03 is enabled at
+				 * reselection.  On the other hand, if they
+				 * aren't same, set msgin03 == FALSE, and
+				 * COMMANDCONTROL_AUTO_MSGIN_03 is disabled at
+				 * reselection.
+				 */
+				if (sacklen != s_sacklen) {
+					data->curlunt->msgin03 = FALSE;
+				} else {
+					data->curlunt->msgin03 = TRUE;
+				}
+
+				nsp32_adjust_busfree(data, s_sacklen);
+			}
+		}
+
+		/* This value has not substitude with valid value yet... */
+		//data->curlunt->save_datp = data->cur_datp;
+	} else {
+		/*
+		 * no processing.
+		 */
+	}
+	
+	if (execph & MSGIN_03_VALID) {
+		/* MsgIn03 was valid to be processed. No need processing. */
+	}
+
+	/*
+	 * target SDTR check
+	 */
+	if (data->curtarget->sync_flag & SDTR_INITIATOR) {
+		/*
+		 * SDTR negotiation pulled by the initiator has not
+		 * finished yet. Fall back to ASYNC mode.
+		 */
+		nsp32_set_async(data, data->curtarget);
+		data->curtarget->sync_flag &= ~SDTR_INITIATOR;
+		data->curtarget->sync_flag |= SDTR_DONE;
+	} else if (data->curtarget->sync_flag & SDTR_TARGET) {
+		/*
+		 * SDTR negotiation pulled by the target has been
+		 * negotiating.
+		 */
+		if (execph & (MSGIN_00_VALID | MSGIN_04_VALID)) {
+			/* 
+			 * If valid message is received, then
+			 * negotiation is succeeded.
+			 */
+		} else {
+			/*
+			 * On the contrary, if unexpected bus free is
+			 * occured, then negotiation is failed. Fall
+			 * back to ASYNC mode.
+			 */
+			nsp32_set_async(data, data->curtarget);
+		}
+		data->curtarget->sync_flag &= ~SDTR_TARGET;
+		data->curtarget->sync_flag |= SDTR_DONE;
+	}
+	
+	/*
+	 * It is always ensured by SCSI standard that initiator
+	 * switches into Bus Free Phase after
+	 * receiving message 00 (Command Complete), 04 (Disconnect).
+	 * It's the reason that processing here is valid.
+	 */
+	if (execph & MSGIN_00_VALID) {
+		/* MsgIn 00: Command Complete */
+		nsp32_dbg(NSP32_DEBUG_BUSFREE, "command complete");
+
+		SCpnt->SCp.Status  = nsp32_read1(base, SCSI_CSB_IN);
+		SCpnt->SCp.Message = 0;
+		nsp32_dbg(NSP32_DEBUG_BUSFREE, 
+			  "normal end stat=0x%x resid=0x%x\n",
+			  SCpnt->SCp.Status, SCpnt->resid);
+		SCpnt->result = 
+			(DID_OK << 16) | (SCpnt->SCp.Message << 8) | (SCpnt->SCp.Status << 0);
+		nsp32_scsi_done(data, SCpnt);
+		/* All operation is done */
+		return (TRUE);
+	} else if (execph & MSGIN_04_VALID) {
+		/* MsgIn 04: Disconnect */
+		SCpnt->SCp.Status  = nsp32_read1(base, SCSI_CSB_IN);
+		SCpnt->SCp.Message = 4;
+		
+		nsp32_dbg(NSP32_DEBUG_BUSFREE, "disconnect");
+		return (TRUE);
+	} else {
+		/* Unexpected bus free */
+		nsp32_msg(KERN_WARNING, "unexpected bus free occured");
+
+		/* DID_ERROR? */
+		//SCpnt->result   = (DID_OK << 16) | (SCpnt->SCp.Message << 8) | (SCpnt->SCp.Status << 0);
+		SCpnt->result = DID_ERROR << 16;
+		nsp32_scsi_done(data, SCpnt);
+		return (TRUE);
+	}
+	return (FALSE);
+}
+
+
+/*
+ * nsp32_adjust_busfree - adjusting SG table
+ *
+ * Note: This driver adjust the SG table using SCSI ACK
+ *       counter instead of BMCNT counter!
+ */
+static void nsp32_adjust_busfree(nsp32_hw_data *data, unsigned int s_sacklen)
+{
+	int old_entry = data->cur_entry;
+	int new_entry;
+	struct nsp32_sgtable *sgt = data->curlunt->sglun->sgt;
+	unsigned int restlen, sentlen;
+	int sg_num = data->curlunt->sg_num;
+
+	/* adjust saved SACK count with 4 byte start address boundary */
+	s_sacklen -= sgt[old_entry].addr & 3;
+
+	/*
+	 * calculate new_entry from sack count and each sgt[].len 
+	 * calculate the byte which is intent to send
+	 */
+	sentlen = 0;
+	for (new_entry = old_entry; new_entry < sg_num; new_entry++) {
+		sentlen += (sgt[new_entry].len & ~NSP32_SG_END_SGT);
+		if (sentlen > s_sacklen) {
+			break;
+		}
+	}
+
+	/* all sgt is processed */
+	if (new_entry == sg_num) {
+		goto last;
+	}
+
+	if (sentlen == s_sacklen) {
+		/* XXX: confirm it's ok or not */
+		/* In this case, it's ok because we are at 
+		   the head element of the sg. restlen is correctly calculated. */
+	}
+
+	/* calculate the rest length for transfering */
+	restlen = sentlen - s_sacklen;
+	
+	/* update adjusting current SG table entry */
+	sgt[new_entry].addr += (sgt[new_entry].len - restlen);
+	sgt[new_entry].len = restlen;
+
+	/* set cur_entry with new_entry */
+	data->cur_entry = new_entry;
+	
+	return;
+
+ last:
+	/* update hostdata and lun */
+
+	return;
+}
+
+
+/*
+ * It's called MsgOut phase occur.
+ * NinjaSCSI-32Bi/UDE automatically processes up to 3 messages in
+ * message out phase. It, however, has more than 3 messages,
+ * HBA creates the interrupt and we have to process by hand.
+ */
+static void nsp32_msgout_occur(nsp32_hw_data *data)
+{
+	unsigned int base = data->BaseAddress;
+	unsigned short command;
+	long new_sgtp;
+	int i;
+	
+	nsp32_dbg(NSP32_DEBUG_MSGOUTOCCUR,
+		  "enter: msgoutlen: 0x%x", data->msgoutlen);
+
+	/*
+	 * If MsgOut phase is occured without having any
+	 * message, then No_Operation is sent (SCSI-2).
+	 */
+	if (data->msgoutlen == 0) {
+		nsp32_build_nop(data);
+	}
+
+	/*
+	 * Set SGTP ADDR current entry for restarting AUTOSCSI, 
+	 * because SGTP is incremented next point.
+	 * There is few statement in the specification...
+	 */
+ 	new_sgtp = data->curlunt->sglun_paddr
+		+ data->curlunt->cur_entry * sizeof(struct nsp32_sgtable);
+
+	/*
+	 * send messages
+	 */
+	for (i=0; i<data->msgoutlen; i++) {
+		nsp32_dbg(NSP32_DEBUG_MSGOUTOCCUR,
+			  "%d : 0x%x", i, data->msgoutbuf[i]);
+
+		/*
+		 * Check REQ is asserted.
+		 */
+		nsp32_wait_req(data, ASSERT);
+
+		if (i == (data->msgoutlen - 1)) {
+			/*
+			 * If the last message, set the AutoSCSI restart
+			 * before send back the ack message. AutoSCSI
+			 * restart automatically negate ATN signal.
+			 */
+			//command = (AUTO_MSGIN_00_OR_04 | AUTO_MSGIN_02);
+			//nsp32_restart_autoscsi(data, command);
+			nsp32_write2(base, COMMAND_CONTROL,
+					 (CLEAR_CDB_FIFO_POINTER |
+					  AUTO_COMMAND_PHASE |
+					  AUTOSCSI_RESTART |
+					  AUTO_MSGIN_00_OR_04 |
+					  AUTO_MSGIN_02 ));
+		}
+		/*
+		 * Write data with SACK, then wait sack is
+		 * automatically negated.
+		 */
+		nsp32_write1(base, SCSI_DATA_WITH_ACK, data->msgoutbuf[i]);
+		nsp32_wait_sack(data, NEGATE);
+
+		nsp32_dbg(NSP32_DEBUG_MSGOUTOCCUR, "bus: 0x%x\n",
+			  nsp32_read1(base, SCSI_BUS_MONITOR));
+	};
+
+	data->msgoutlen = 0;
+
+	nsp32_dbg(NSP32_DEBUG_MSGOUTOCCUR, "exit");
+}
+
+/*
+ * Restart AutoSCSI
+ *
+ * Note: Restarting AutoSCSI needs set:
+ *		SYNC_REG, ACK_WIDTH, SGT_ADR, TRANSFER_CONTROL
+ */
+static void nsp32_restart_autoscsi(nsp32_hw_data *data, unsigned short command)
+{
+	unsigned int base = data->BaseAddress;
+	unsigned short transfer = 0;
+	Scsi_Cmnd *SCpnt = data->curlunt->SCpnt;
+
+	nsp32_dbg(NSP32_DEBUG_RESTART, "enter");
+
+	if (data->curtarget == NULL || data->curlunt == NULL) {
+		nsp32_msg(KERN_ERR, "Target or Lun is invalid");
+	}
+
+	/*
+	 * set SYNC_REG
+	 * Don't set BM_START_ADR before setting this register.
+	 */
+	nsp32_write1(base, SYNC_REG, data->curtarget->syncreg);
+
+	/*
+	 * set ACKWIDTH
+	 */
+	nsp32_write1(base, ACK_WIDTH, data->curtarget->ackwidth);
+
+	/*
+	 * set SGT ADDR (physical address)
+	 */
+	nsp32_write4(base, SGT_ADR, data->curlunt->sglun_paddr);
+
+	/*
+	 * set TRANSFER CONTROL REG
+	 */
+	transfer = 0;
+	transfer |= (TRANSFER_GO | ALL_COUNTER_CLR);
+	if (data->trans_method & NSP32_TRANSFER_BUSMASTER) {
+		if (SCpnt->request_bufflen > 0) {
+			transfer |= BM_START;
+		}
+	} else if (data->trans_method & NSP32_TRANSFER_MMIO) {
+		transfer |= CB_MMIO_MODE;
+	} else if (data->trans_method & NSP32_TRANSFER_PIO) {
+		transfer |= CB_IO_MODE;
+	}
+	nsp32_write2(base, TRANSFER_CONTROL, transfer);
+
+	/*
+	 * restart AutoSCSI
+	 *
+	 * TODO: COMMANDCONTROL_AUTO_COMMAND_PHASE is needed ?
+	 */
+	command |= (CLEAR_CDB_FIFO_POINTER |
+		    AUTO_COMMAND_PHASE |
+		    AUTOSCSI_RESTART);
+	nsp32_write2(base, COMMAND_CONTROL, command);
+
+	nsp32_dbg(NSP32_DEBUG_RESTART, "exit");
+}
+
+
+/*
+ * cannot run automatically message in occur
+ */
+static void nsp32_msgin_occur(nsp32_hw_data *data, unsigned long irq_status,
+			unsigned short execph)
+{
+	unsigned int base = data->BaseAddress;
+	unsigned char msg;
+	unsigned char msgtype;
+	unsigned char newlun;
+	unsigned short command = 0;
+	int msgclear = TRUE;
+	long new_sgtp;
+	int ret;
+
+	/*
+	 * read first message
+	 *    Use SCSIDATA_W_ACK instead of SCSIDATAIN, because the procedure
+	 *    of Message-In have to be processed before sending back SCSI ACK.
+	 */
+	msg = nsp32_read1(base, SCSI_DATA_IN);
+	data->msginbuf[(unsigned char)data->msginlen] = msg;
+	msgtype = data->msginbuf[0];
+	nsp32_dbg(NSP32_DEBUG_MSGINOCCUR,
+		  "enter: msglen: 0x%x msgin: 0x%x msgtype: 0x%x",
+		  data->msginlen, msg, msgtype);
+
+	/*
+	 * TODO: We need checking whether bus phase is message in?
+	 */
+
+	/*
+	 * assert SCSI ACK
+	 */
+	nsp32_sack_assert(data);
+
+	/*
+	 * processing IDENTIFY
+	 */
+	if (msgtype & 0x80) {
+		if (!(irq_status & IRQSTATUS_RESELECT_OCCUER)) {
+			/* Invalid (non reselect) phase */
+			goto reject;
+		}
+
+		newlun = msgtype & 0x1f; /* TODO: SPI-3 compliant? */
+		ret = nsp32_reselection(data, newlun);
+		if (ret == TRUE) {
+			goto restart;
+		} else {
+			goto reject;
+		}
+	}
+	
+	/*
+	 * processing messages except for IDENTIFY
+	 *
+	 * TODO: Messages are all SCSI-2 terminology. SCSI-3 compliance is TODO.
+	 */
+	switch (msgtype) {
+	/*
+	 * 1-byte message
+	 */
+	case COMMAND_COMPLETE:
+	case DISCONNECT:
+		/*
+		 * These messages should not be occured.
+		 * They should be processed on AutoSCSI sequencer.
+		 */
+		nsp32_msg(KERN_WARNING, 
+			   "unexpected message of AutoSCSI MsgIn: 0x%x", msg);
+		break;
+		
+	case RESTORE_POINTERS:
+		/*
+		 * AutoMsgIn03 is disabled, and HBA gets this message.
+		 */
+
+		if ((execph & DATA_IN_PHASE) || (execph & DATA_OUT_PHASE)) {
+			unsigned int s_sacklen;
+
+			s_sacklen = nsp32_read4(base, SAVED_SACK_CNT);
+			if ((execph & MSGIN_02_VALID) && (s_sacklen > 0)) {
+				nsp32_adjust_busfree(data, s_sacklen);
+			} else {
+				/* No need to rewrite SGT */
+			}
+		}
+		data->curlunt->msgin03 = FALSE;
+
+		/* Update with the new value */
+
+		/* reset SACK/SavedACK counter (or ALL clear?) */
+		nsp32_write4(base, CLR_COUNTER, CLRCOUNTER_ALLMASK);
+
+		/*
+		 * set new sg pointer
+		 */
+		new_sgtp = data->curlunt->sglun_paddr + 
+			data->curlunt->cur_entry * sizeof(struct nsp32_sgtable);
+		nsp32_write4(base, SGT_ADR, new_sgtp);
+
+		break;
+
+	case SAVE_POINTERS:
+		/*
+		 * These messages should not be occured.
+		 * They should be processed on AutoSCSI sequencer.
+		 */
+		nsp32_msg (KERN_WARNING, 
+			   "unexpected message of AutoSCSI MsgIn: SAVE_POINTERS");
+		
+		break;
+		
+	case MESSAGE_REJECT:
+		/* If previous message_out is sending SDTR, and get 
+		   message_reject from target, SDTR negotiation is failed */
+		if (data->curtarget->sync_flag &
+				(SDTR_INITIATOR | SDTR_TARGET)) {
+			/*
+			 * Current target is negotiating SDTR, but it's
+			 * failed.  Fall back to async transfer mode, and set
+			 * SDTR_DONE.
+			 */
+			nsp32_set_async(data, data->curtarget);
+			data->curtarget->sync_flag &= ~SDTR_INITIATOR;
+			data->curtarget->sync_flag |= SDTR_DONE;
+
+		}
+		break;
+
+	case LINKED_CMD_COMPLETE:
+	case LINKED_FLG_CMD_COMPLETE:
+		/* queue tag is not supported currently */
+		nsp32_msg (KERN_WARNING, 
+			   "unsupported message: 0x%x", msgtype);
+		break;
+
+	case INITIATE_RECOVERY:
+		/* staring ECA (Extended Contingent Allegiance) state. */
+		/* This message is declined in SPI2 or later. */
+
+		goto reject;
+
+	/*
+	 * 2-byte message
+	 */
+	case SIMPLE_QUEUE_TAG:
+	case 0x23:
+		/*
+		 * 0x23: Ignore_Wide_Residue is not declared in scsi.h.
+		 * No support is needed.
+		 */
+		if (data->msginlen >= 1) {
+			goto reject;
+		}
+
+		/* current position is 1-byte of 2 byte */
+		msgclear = FALSE;
+
+		break;
+
+	/*
+	 * extended message
+	 */
+	case EXTENDED_MESSAGE:
+		if (data->msginlen < 1) {
+			/*
+			 * Current position does not reach 2-byte
+			 * (2-byte is extended message length).
+			 */
+			msgclear = FALSE;
+			break;
+		}
+
+		if ((data->msginbuf[1] + 1) > data->msginlen) {
+			/*
+			 * Current extended message has msginbuf[1] + 2
+			 * (msginlen starts counting from 0, so buf[1] + 1).
+			 * If current message position is not finished,
+			 * continue receiving message.
+			 */
+			msgclear = FALSE;
+			break;
+		}
+
+		/*
+		 * Reach here means regular length of each type of 
+		 * extended messages.
+		 */
+		switch (data->msginbuf[2]) {
+		case EXTENDED_MODIFY_DATA_POINTER:
+			/* TODO */
+			goto reject; /* not implemented yet */
+			break;
+
+		case EXTENDED_SDTR:
+			/*
+			 * Exchange this message between initiator and target.
+			 */
+			if (data->msginlen != EXTENDED_SDTR_LEN + 1) {
+				/*
+				 * received inappropriate message.
+				 */
+				goto reject;
+				break;
+			}
+
+			nsp32_analyze_sdtr(data);
+
+			break;
+
+		case EXTENDED_EXTENDED_IDENTIFY:
+			/* SCSI-I only, not supported. */
+			goto reject; /* not implemented yet */
+
+			break;
+
+		case EXTENDED_WDTR:
+			goto reject; /* not implemented yet */
+
+			break;
+			
+		default:
+			goto reject;
+		}
+		break;
+		
+	default:
+		goto reject;
+	}
+
+ restart:
+	if (msgclear == TRUE) {
+		data->msginlen = 0;
+
+		/*
+		 * If restarting AutoSCSI, but there are some message to out
+		 * (msgoutlen > 0), set AutoATN, and set SCSIMSGOUT as 0
+		 * (MV_VALID = 0). When commandcontrol is written with
+		 * AutoSCSI restart, at the same time MsgOutOccur should be
+		 * happened (however, such situation is really possible...?).
+		 */
+		if (data->msgoutlen > 0) {	
+			nsp32_write4(base, SCSI_MSG_OUT, 0);
+			command |= AUTO_ATN;
+		}
+
+		/*
+		 * restart AutoSCSI
+		 * If it's failed, COMMANDCONTROL_AUTO_COMMAND_PHASE is needed.
+		 */
+		command |= (AUTO_MSGIN_00_OR_04 | AUTO_MSGIN_02);
+
+		/*
+		 * If current msgin03 is TRUE, then flag on.
+		 */
+		if (data->curlunt->msgin03 == TRUE) {
+			command |= AUTO_MSGIN_03;
+		}
+		data->curlunt->msgin03 = FALSE;
+	} else {
+		data->msginlen++;
+	}
+
+	/*
+	 * restart AutoSCSI
+	 */
+	nsp32_restart_autoscsi(data, command);
+
+	/*
+	 * wait SCSI REQ negate for REQ-ACK handshake
+	 */
+	nsp32_wait_req(data, NEGATE);
+
+	/*
+	 * negate SCSI ACK
+	 */
+	nsp32_sack_negate(data);
+
+	nsp32_dbg(NSP32_DEBUG_MSGINOCCUR, "exit");
+
+	return;
+
+ reject:
+	nsp32_msg(KERN_WARNING, 
+		  "invalid or unsupported MessageIn, rejected. "
+		  "current msg: 0x%x (len: 0x%x), processing msg: 0x%x",
+		  msg, data->msginlen, msgtype);
+	nsp32_build_reject(data);
+	data->msginlen = 0;
+
+	goto restart;
+}
+
+/*
+ * 
+ */
+static void nsp32_analyze_sdtr(nsp32_hw_data *data)
+{
+	struct nsp32_target *target = data->curtarget;
+	struct nsp32_sync_table *synct;
+	unsigned char get_period = data->msginbuf[3];
+	unsigned char get_offset = data->msginbuf[4];
+	int entry;
+	int syncnum;
+
+	nsp32_dbg(NSP32_DEBUG_MSGINOCCUR, "enter");
+
+	synct = data->synct;
+	syncnum = data->syncnum;
+
+	/*
+	 * If this inititor sent the SDTR message, then target responds SDTR,
+	 * initiator SYNCREG, ACKWIDTH from SDTR parameter.
+	 * Messages are not appropriate, then send back reject message.
+	 * If initiator did not send the SDTR, but target sends SDTR, 
+	 * initiator calculator the appropriate parameter and send back SDTR.
+	 */	
+	if (target->sync_flag & SDTR_INITIATOR) {
+		/*
+		 * Initiator sent SDTR, the target responds and
+		 * send back negotiation SDTR.
+		 */
+		nsp32_dbg(NSP32_DEBUG_MSGINOCCUR, "target responds SDTR");
+	
+		target->sync_flag &= ~SDTR_INITIATOR;
+		target->sync_flag |= SDTR_DONE;
+
+		/*
+		 * offset:
+		 */
+		if (get_offset > SYNC_OFFSET) {
+			/*
+			 * Negotiation is failed, the target send back
+			 * unexpected offset value.
+			 */
+			goto reject;
+		}
+		
+		if (get_offset == ASYNC_OFFSET) {
+			/*
+			 * Negotiation is succeeded, the target want
+			 * to fall back into asynchronous transfer mode.
+			 */
+			goto async;
+		}
+
+		/*
+		 * period:
+		 *    Check whether sync period is too short. If too short,
+		 *    fall back to async mode. If it's ok, then investigate
+		 *    the received sync period. If sync period is acceptable
+		 *    between sync table start_period and end_period, then
+		 *    set this I_T nexus as sent offset and period.
+		 *    If it's not acceptable, send back reject and fall back
+		 *    to async mode.
+		 */
+		if (get_period < data->synct[0].period_num) {
+			/*
+			 * Negotiation is failed, the target send back
+			 * unexpected period value.
+			 */
+			goto reject;
+		}
+
+		entry = nsp32_search_period_entry(data, target, get_period);
+
+		if (entry < 0) {
+			/*
+			 * Target want to use long period which is not 
+			 * acceptable NinjaSCSI-32Bi/UDE.
+			 */
+			goto reject;
+		}
+
+		/*
+		 * Set new sync table and offset in this I_T nexus.
+		 */
+		nsp32_set_sync_entry(data, target, entry, get_offset);
+	} else {
+		/* Target send SDTR to initiator. */
+		nsp32_dbg(NSP32_DEBUG_MSGINOCCUR, "target send SDTR");
+	
+		target->sync_flag |= SDTR_INITIATOR;
+
+		/* offset: */
+		if (get_offset > SYNC_OFFSET) {
+			/* send back as SYNC_OFFSET */
+			get_offset = SYNC_OFFSET;
+		}
+
+		/* period: */
+		if (get_period < data->synct[0].period_num) {
+			get_period = data->synct[0].period_num;
+		}
+
+		entry = nsp32_search_period_entry(data, target, get_period);
+
+		if (get_offset == ASYNC_OFFSET || entry < 0) {
+			nsp32_set_async(data, target);
+			nsp32_build_sdtr(data, 0, ASYNC_OFFSET);
+		} else {
+			nsp32_set_sync_entry(data, target, entry, get_offset);
+			nsp32_build_sdtr(data, get_period, get_offset);
+		}
+	}
+	
+	nsp32_dbg(NSP32_DEBUG_MSGINOCCUR, "exit");
+	return;
+
+ reject:
+	/*
+	 * If the current message is unacceptable, send back to the target
+	 * with reject message.
+	 */
+	nsp32_build_reject(data);
+
+ async:
+	nsp32_set_async(data, target);	/* set as ASYNC transfer mode */
+
+	nsp32_dbg(NSP32_DEBUG_MSGINOCCUR, "exit: set async");
+	return;
+}
+
+
+/*
+ * Search config entry number matched in sync_table from given
+ * target and speed period value. If failed to search, return negative value.
+ */
+static int nsp32_search_period_entry(nsp32_hw_data *data,
+			      struct nsp32_target *target,
+			      unsigned char period)
+{
+	int i;
+
+	if (target->limit_entry >= data->syncnum) {
+		nsp32_msg(KERN_ERR, "limit_entry exceeds syncnum!");
+		target->limit_entry = 0;
+	}
+
+	for (i=target->limit_entry; i<data->syncnum; i++) {
+		if (period >= data->synct[i].start_period &&
+		    period <= data->synct[i].end_period) {
+				break;
+		}
+	}
+
+	/*
+	 * Check given period value is over the sync_table value.
+	 * If so, return max value.
+	 */
+	if (i == data->syncnum) {
+		i = -1;
+	}
+
+	return i;
+}
+
+
+/*
+ * target <-> initiator use ASYNC transfer
+ */
+static void nsp32_set_async(nsp32_hw_data *data, struct nsp32_target *target)
+{
+	unsigned char period = data->synct[target->limit_entry].period_num;
+
+	target->offset   = ASYNC_OFFSET;
+	target->syncreg  = TO_SYNCREG(period, ASYNC_OFFSET);
+	target->ackwidth = 0;
+
+	nsp32_dbg(NSP32_DEBUG_SYNC, "set async");
+}
+
+
+/*
+ * target <-> initiator use maximum SYNC transfer
+ */
+static void nsp32_set_max_sync(nsp32_hw_data *data,
+			       struct nsp32_target *target,
+			       unsigned char *period, unsigned char *offset)
+{
+	unsigned char period_num, ackwidth;
+
+	period_num = data->synct[target->limit_entry].period_num;
+	*period    = data->synct[target->limit_entry].start_period;
+	ackwidth   = data->synct[target->limit_entry].ackwidth;
+	*offset    = SYNC_OFFSET;
+
+	target->syncreg  = TO_SYNCREG(period_num, *offset);
+	target->ackwidth = ackwidth;
+	target->offset   = *offset;
+}
+
+
+/*
+ * target <-> initiator use entry number speed
+ */
+static void nsp32_set_sync_entry(nsp32_hw_data *data,
+				 struct nsp32_target *target,
+				 int entry, unsigned char offset)
+{
+	unsigned char period, ackwidth;
+
+	period   = data->synct[entry].period_num;
+	ackwidth = data->synct[entry].ackwidth;
+	offset  = offset;
+
+	target->syncreg  = TO_SYNCREG(period, offset);
+	target->ackwidth = ackwidth;
+	target->offset   = offset;
+
+	nsp32_dbg(NSP32_DEBUG_SYNC, "set sync");
+}
+
+
+/*
+ * It waits until SCSI REQ becomes assertion or negation state.
+ *
+ * Note: If nsp32_msgin_occur is called, we asserts SCSI ACK. Then
+ *     connected target responds SCSI REQ negation.  We have to wait
+ *     SCSI REQ becomes negation in order to negate SCSI ACK signal for
+ *     REQ-ACK handshake.
+ */
+static void nsp32_wait_req(nsp32_hw_data *data, int state)
+{
+	unsigned int base = data->BaseAddress;
+	int wait_time = 0;
+	unsigned char bus;
+
+	if (!((state == ASSERT) || (state == NEGATE))) {
+		nsp32_msg(KERN_ERR, "unknown state designation");
+	}
+	state <<= 5; /* REQ is BIT(5) */
+
+	do {
+		bus = nsp32_read1(base, SCSI_BUS_MONITOR);
+		if ((bus & BUSMON_REQ) == state) {
+			nsp32_dbg(NSP32_DEBUG_WAIT, 
+				  "wait_time: %d", wait_time);
+			return;
+		}
+		udelay(1);
+		wait_time++;
+	} while (wait_time < REQSACK_TIMEOUT_TIME);
+
+	nsp32_msg(KERN_WARNING, "wait REQ timeout, state: %d", state);
+}
+
+/*
+ * It waits until SCSI SACK becomes assertion or negation state.
+ */
+static void nsp32_wait_sack(nsp32_hw_data *data, int state)
+{
+	unsigned int base = data->BaseAddress;
+	int wait_time = 0;
+	unsigned char bus;
+
+	if (!((state == ASSERT) || (state == NEGATE))) {
+		nsp32_msg(KERN_ERR, "unknown state designation");
+	}
+	state <<= 4; /* ACK is BIT(4) */
+
+	do {
+		bus = nsp32_read1(base, SCSI_BUS_MONITOR);
+		if ((bus & BUSMON_ACK) == state) {
+			nsp32_dbg(NSP32_DEBUG_WAIT,
+				  "wait_time: %d", wait_time);
+			return;
+		}
+		udelay(1);
+		wait_time++;
+	} while (wait_time < REQSACK_TIMEOUT_TIME);
+
+	nsp32_msg(KERN_WARNING, "wait SACK timeout, state: %d", state);
+}
+
+/*
+ * assert SCSI ACK
+ *
+ * Note: SCSI ACK assertion needs with ACKENB=1, AUTODIRECTION=1.
+ */
+static void nsp32_sack_assert(nsp32_hw_data *data)
+{
+	unsigned char busctrl;
+	unsigned int base = data->BaseAddress;
+
+	busctrl  = nsp32_read1(base, SCSI_BUS_CONTROL);
+	busctrl	|= (BUSCTL_ACK | AUTODIRECTION | ACKENB);
+	nsp32_write1(base, SCSI_BUS_CONTROL,busctrl);
+}
+
+/*
+ * negate SCSI ACK
+ */
+static void nsp32_sack_negate(nsp32_hw_data *data)
+{
+	unsigned char busctrl;
+	unsigned int base = data->BaseAddress;
+
+	busctrl  = nsp32_read1(base, SCSI_BUS_CONTROL);
+	busctrl	&= ~BUSCTL_ACK;
+	nsp32_write1(base, SCSI_BUS_CONTROL, busctrl);
+}
+
+
+
+/*
+ * getting EEPROM parameter
+ */
+static int nsp32_getprom_param(nsp32_hw_data *data)
+{
+	int vendor = data->pci_devid->vendor;
+	int device = data->pci_devid->device;
+	int ret, val, i;
+
+	/*
+	 * EEPROM checking.
+	 */
+	ret = nsp32_prom_read(data, 0x7e);
+	if (ret != 0x55) {
+		nsp32_msg(KERN_INFO, "No EEPROM detected: 0x%x", ret);
+		return (FALSE);
+	}
+	ret = nsp32_prom_read(data, 0x7f);
+	if (ret != 0xaa) {
+		nsp32_msg(KERN_INFO, "Invalid number: 0x%x", ret);
+		return (FALSE);
+	}
+
+	/*
+	 * check EEPROM type
+	 */
+	if (vendor == PCI_VENDOR_ID_WORKBIT &&
+	    device == PCI_DEVICE_ID_WORKBIT_STANDARD) {
+		ret = nsp32_getprom_standard(data);
+	} else if (vendor == PCI_VENDOR_ID_WORKBIT &&
+		   device == PCI_DEVICE_ID_NINJASCSI_32BIB_LOGITEC) {
+		ret = nsp32_getprom_new(data);
+	} else if (vendor == PCI_VENDOR_ID_WORKBIT &&
+		   device == PCI_DEVICE_ID_NINJASCSI_32UDE_MELCO ) {
+		ret = nsp32_getprom_new(data);
+	} else {
+		nsp32_msg(KERN_WARNING, "Unknown EEPROM");
+		ret = FALSE;
+	}
+
+	/* for debug : SPROM data full checking */
+	for (i=0; i<=0x1f; i++) {
+		val = nsp32_prom_read(data, i);
+		nsp32_dbg(NSP32_DEBUG_EEPROM,
+			  "rom address 0x%x : 0x%x", i, val);
+	}
+
+	return (ret);
+}
+
+
+/*
+ * AT24C01A (Logitec: LHA-600S), AT24C02 (Melco Buffalo: IFC-USLP) data map:
+ *
+ *   ROMADDR
+ *   0x00 - 0x06 :  Device Synchronous Transfer Period (SCSI ID 0 - 6) 
+ *			Value 0x0: ASYNC, 0x0c: Ultra-20M, 0x19: Fast-10M
+ *   0x07        :  HBA Synchronous Transfer Period
+ *			Value 0: AutoSync, 1: Manual Setting
+ *   0x08 - 0x0f :  Not Used? (0x0)
+ *   0x10        :  Bus Termination
+ * 			Value 0: Auto[ON], 1: ON, 2: OFF
+ *   0x11        :  Not Used? (0)
+ *   0x12        :  Bus Reset Delay Time (0x03)
+ *   0x13        :  Bootable CD Support
+ *			Value 0: Disable, 1: Enable
+ *   0x14        :  Device Scan
+ *			Bit   7  6  5  4  3  2  1  0
+ *			      |  <----------------->
+ * 			      |    SCSI ID: Value 0: Skip, 1: YES
+ *			      |->  Value 0: ALL scan,  Value 1: Manual
+ *   0x15 - 0x1b :  Not Used? (0)
+ *   0x1c        :  Constant? (0x01) (clock div?)
+ *   0x1d - 0x7c :  Not Used (0xff)
+ *   0x7d	 :  Not Used? (0xff)
+ *   0x7e        :  Constant (0x55), HBA chip revision
+ *   0x7f        :  Constant (0xaa), HBA value
+ */
+static int nsp32_getprom_new(nsp32_hw_data *data)
+{
+	int ret, i;
+	int auto_sync;
+	struct nsp32_target *target;
+	int entry;
+
+	/*
+	 * Reset time which is designated by EEPROM.
+	 *
+	 * TODO: Not used yet.
+	 */
+	data->resettime = nsp32_prom_read(data, 0x12);
+
+	/*
+	 * HBA Synchronous Transfer Period
+	 *
+	 * Note: auto_sync = 0: auto, 1: manual.  Ninja SCSI HBA spec says
+	 *	that if auto_sync is 0 (auto), and connected SCSI devices are
+	 *	same or lower than 3, then transfer speed is set as ULTRA-20M.
+	 *	On the contrary if connected SCSI devices are same or higher
+	 *	than 4, then transfer speed is set as FAST-10M.
+	 *
+	 *	I break this rule. The number of connected SCSI devices are
+	 *	only ignored. If auto_sync is 0 (auto), then transfer speed is
+	 *	forced as ULTRA-20M.
+	 */
+	ret = nsp32_prom_read(data, 0x07);
+	switch (ret) {
+	case 0:
+		auto_sync = TRUE;
+		break;
+	case 1:
+		auto_sync = FALSE;
+		break;
+	default:
+		nsp32_msg(KERN_WARNING,
+			  "Unsupported Auto Sync mode."
+			  "Fall back to manual mode.");
+		auto_sync = TRUE;
+	}
+
+	if (trans_mode == ULTRA20M_MODE) {
+		auto_sync = TRUE;
+	}
+
+	/*
+	 * each device Synchronous Transfer Period
+	 */
+	for (i=0; i<NSP32_HOST_SCSIID; i++) {
+		target = &data->target[i];
+		if (auto_sync == TRUE) {
+			target->limit_entry = 0;   /* set as ULTRA20M */
+		} else {
+			ret = nsp32_prom_read(data, i);
+			entry = nsp32_search_period_entry(data, target, ret);
+			if (entry < 0) {
+				/* search failed... set maximum speed */
+				entry = 0;
+			}
+			target->limit_entry = entry;
+		}
+	}
+
+	return (TRUE);
+}
+
+
+/*
+ * ? (I-O Data: SC-NBD) data map:
+ *
+ *   ROMADDR
+ *   0x00 - 0x06 :  Device Synchronous Transfer Period (SCSI ID 0 - 6) 
+ *			Value 0x0: 20MB/S, 0x1: 10MB/S, 0x2: 5MB/S, 0x3: ASYNC
+ *   0x07        :  0 (HBA Synchronous Transfer Period: Auto Sync)
+ *   0x08 - 0x0f :  Not Used? (0x0)
+ *   0x10        :  Transfer Mode
+ *			Value 0: PIO, 1: Busmater
+ *   0x11        :  Bus Reset Delay Time (0x00-0x20)
+ *   0x12        :  Bus Termination
+ * 			Value 0: Disable, 1: Enable
+ *   0x13 - 0x19 :  Disconnection
+ *			Value 0: Disable, 1: Enable
+ *   0x1a - 0x7c :  Not Used? (0)
+ *   0x7d	 :  Not Used? (0xf8)
+ *   0x7e        :  Constant (0x55), HBA chip revision
+ *   0x7f        :  Constant (0xaa), HBA value
+ */
+static int nsp32_getprom_standard(nsp32_hw_data *data)
+{
+	int ret, i;
+	struct nsp32_target *target;
+	int entry, val;
+
+	/*
+	 * Reset time which is designated by EEPROM.
+	 *
+	 * TODO: Not used yet.
+	 */
+	data->resettime = nsp32_prom_read(data, 0x11);
+
+	/*
+	 * each device Synchronous Transfer Period
+	 */
+	for (i=0; i<NSP32_HOST_SCSIID; i++) {
+		target = &data->target[i];
+		ret = nsp32_prom_read(data, i);
+		switch (ret) {
+		case 0:		/* 20MB/s */
+			val = 0x0c;
+			break;
+		case 1:		/* 10MB/s */
+			val = 0x19;
+			break;
+		case 2:		/* 5MB/s */
+			val = 0x32;
+			break;
+		case 3:		/* ASYNC */
+			val = 0x0;
+			break;
+		default:	/* default 20MB/s */
+			val = 0x0c;
+		}
+		entry = nsp32_search_period_entry(data, target, val);
+		if (entry < 0 || trans_mode == ULTRA20M_MODE) {
+			/* search failed... set maximum speed */
+			entry = 0;
+		}
+		target->limit_entry = entry;
+	}
+
+	return (TRUE);
+}
+
+
+/*
+ * Atmel AT24C01A (drived in 5V) serial EEPROM routines
+ */
+static int nsp32_prom_read(nsp32_hw_data *data, int romaddr)
+{
+	int i, val;
+
+	/* start condition */
+	nsp32_prom_start(data);
+
+	/* device address */
+	nsp32_prom_write(data, 1);	/* 1 */
+	nsp32_prom_write(data, 0);	/* 0 */
+	nsp32_prom_write(data, 1);	/* 1 */
+	nsp32_prom_write(data, 0);	/* 0 */
+	nsp32_prom_write(data, 0);	/* A2: 0 (GND) */
+	nsp32_prom_write(data, 0);	/* A1: 0 (GND) */
+	nsp32_prom_write(data, 0);	/* A0: 0 (GND) */
+
+	/* R/W: W for dummy write */
+	nsp32_prom_write(data, 0);
+
+	/* ack */
+	nsp32_prom_write(data, 0);
+
+	/* word address */
+	for (i=7; i>=0; i--) {
+		nsp32_prom_write(data, ((romaddr >> i) & 1));
+	}
+
+	/* ack */
+	nsp32_prom_write(data, 0);
+
+	/* start condition */
+	nsp32_prom_start(data);
+
+	/* device address */
+	nsp32_prom_write(data, 1);	/* 1 */
+	nsp32_prom_write(data, 0);	/* 0 */
+	nsp32_prom_write(data, 1);	/* 1 */
+	nsp32_prom_write(data, 0);	/* 0 */
+	nsp32_prom_write(data, 0);	/* A2: 0 (GND) */
+	nsp32_prom_write(data, 0);	/* A1: 0 (GND) */
+	nsp32_prom_write(data, 0);	/* A0: 0 (GND) */
+
+	/* R/W: R */
+	nsp32_prom_write(data, 1);
+
+	/* ack */
+	nsp32_prom_write(data, 0);
+
+	/* data... */
+	val = 0;
+	for (i=7; i>=0; i--) {
+		val += (nsp32_prom_fetch(data) << i);
+	}
+	
+	/* no ack */
+	nsp32_prom_write(data, 1);
+
+	/* stop condition */
+	nsp32_prom_stop(data);
+
+	return (val);
+}
+
+static void nsp32_prom_start (nsp32_hw_data *data)
+{
+	/* start condition */
+	nsp32_prom_set(data, SCL, 1);
+	nsp32_prom_set(data, SDA, 1);
+	nsp32_prom_set(data, ENA, 1);	/* output mode */
+	nsp32_prom_set(data, SDA, 0);	/* keeping SCL=1 and transiting
+					 * SDA 1->0 is start condition */
+	nsp32_prom_set(data, SCL, 0);
+}
+
+static void nsp32_prom_stop (nsp32_hw_data *data)
+{
+	/* stop condition */
+	nsp32_prom_set(data, SCL, 1);
+	nsp32_prom_set(data, SDA, 0);
+	nsp32_prom_set(data, ENA, 1);	/* output mode */
+	nsp32_prom_set(data, SDA, 1);
+	nsp32_prom_set(data, SCL, 0);
+}
+
+static void nsp32_prom_write (nsp32_hw_data *data, int val)
+{
+	/* write */
+	nsp32_prom_set(data, SDA, val);
+	nsp32_prom_set(data, SCL, 1);
+	nsp32_prom_set(data, SCL, 0);
+}
+
+static int nsp32_prom_fetch (nsp32_hw_data *data)
+{
+	int val;
+
+	/* read */
+	nsp32_prom_set(data, ENA, 0);	/* input mode */
+	nsp32_prom_set(data, SCL, 1);
+	val = nsp32_prom_get(data, SDA);
+	nsp32_prom_set(data, SCL, 0);
+	nsp32_prom_set(data, ENA, 1);	/* output mode */
+	return (val);
+}
+
+static inline void nsp32_prom_set(nsp32_hw_data *data, int bit, int val)
+{
+	int cur;
+	int base = data->BaseAddress;
+
+	switch(val) {
+	case 0:
+		cur = nsp32_index_read1(base, SERIAL_ROM_CTL);
+		nsp32_index_write1(base, SERIAL_ROM_CTL, cur & ~bit);
+		break;
+	case 1:
+		cur = nsp32_index_read1(base, SERIAL_ROM_CTL);
+		nsp32_index_write1(base, SERIAL_ROM_CTL, cur | bit);
+		break;
+	default:
+		nsp32_msg(KERN_ERR, "val must be 0 or 1");
+		return;
+	}
+
+	udelay(10);
+}
+
+static inline int nsp32_prom_get(nsp32_hw_data *data, int bit)
+{
+	int ret;
+	int base = data->BaseAddress;
+
+	ret = nsp32_index_read1(base, SERIAL_ROM_CTL) & bit;
+	switch (ret) {
+	case 0:
+		ret = 0;
+		break;
+	case SDA:
+		ret = 1;
+		break;
+	default:
+		nsp32_msg(KERN_ERR, "return value is not appropriate");
+	}
+
+	udelay(10);
+
+	return (ret);
+}
+
+/* end */
diff -Nru a/drivers/scsi/nsp32.h b/drivers/scsi/nsp32.h
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/scsi/nsp32.h	Mon Oct  7 11:25:40 2002
@@ -0,0 +1,429 @@
+/*
+ * Workbit NinjaSCSI-32Bi/UDE PCI/Cardbus SCSI Host Bus Adapter driver
+ * Basic data header
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+*/
+
+#ifndef _NSP32_H
+#define _NSP32_H
+
+
+//#define NSP32_DEBUG 9
+
+
+/*
+ * VENDOR/DEVICE ID
+ */
+#define PCI_VENDOR_ID_IODATA  0x10fc
+#define PCI_VENDOR_ID_WORKBIT 0x1145
+
+#define PCI_DEVICE_ID_NINJASCSI_32BI_CBSC_II  0x0005
+#define PCI_DEVICE_ID_NINJASCSI_32BI_KME      0xf007
+#define PCI_DEVICE_ID_NINJASCSI_32BI_WBT      0x8007
+#define PCI_DEVICE_ID_WORKBIT_STANDARD        0xf010
+#define PCI_DEVICE_ID_WORKBIT_DUALEDGE        0xf011
+#define PCI_DEVICE_ID_NINJASCSI_32BI_LOGITEC  0xf012
+#define PCI_DEVICE_ID_NINJASCSI_32BIB_LOGITEC 0xf013
+#define PCI_DEVICE_ID_NINJASCSI_32UDE_MELCO   0xf015
+
+/*
+ * MODEL
+ */
+enum {
+	MODEL_IODATA        = 0,
+	MODEL_KME           = 1,
+	MODEL_WORKBIT       = 2,
+	MODEL_EXT_ROM       = 3,
+	MODEL_PCI_WORKBIT   = 4,
+	MODEL_PCI_LOGITEC   = 5,
+	MODEL_PCI_MELCO     = 6,
+};
+
+static char * nsp32_model[] = {
+	"I-O DATA CBSC-II",
+	"KME SCSI card",
+	"Workbit duo SCSI card",
+	"External ROM",
+	"Workbit Standard/IO Data PCI card",
+	"Logitec PCI card",
+	"Melco PCI card",
+};
+
+
+/*
+ * SCSI Generic Definitions
+ */
+#define EXTENDED_SDTR_LEN	0x03
+
+
+/*
+ * MACRO
+ */
+#define BIT(x)    (1UL << (x))
+#ifndef MIN
+# define MIN(a,b)  ((a) > (b) ? (b) : (a))
+#endif
+
+/*
+ * BASIC Definitions
+ */
+#ifndef TRUE
+# define TRUE  1
+#endif
+#ifndef FALSE
+# define FALSE 0
+#endif
+#define ASSERT 1
+#define NEGATE 0
+
+
+/*******************/
+/* normal register */
+/*******************/
+/*
+ * Don't access below register with Double Word:
+ * +00, +04, +08, +0c, +64, +80, +84, +88, +90, +c4, +c8, +cc, +d0.
+ */
+#define IRQ_CONTROL 0x00	/* BASE+00, W, W */
+#define IRQ_STATUS  0x00	/* BASE+00, W, R */
+# define IRQSTATUS_LATCHED_MSG      BIT(0)
+# define IRQSTATUS_LATCHED_IO       BIT(1)
+# define IRQSTATUS_LATCHED_CD       BIT(2)
+# define IRQSTATUS_LATCHED_BUS_FREE BIT(3)
+# define IRQSTATUS_RESELECT_OCCUER  BIT(4)
+# define IRQSTATUS_PHASE_CHANGE_IRQ BIT(5)
+# define IRQSTATUS_SCSIRESET_IRQ    BIT(6)
+# define IRQSTATUS_TIMER_IRQ        BIT(7)
+# define IRQSTATUS_FIFO_SHLD_IRQ    BIT(8)
+# define IRQSTATUS_PCI_IRQ	    BIT(9)
+# define IRQSTATUS_BMCNTERR_IRQ     BIT(10)
+# define IRQSTATUS_AUTOSCSI_IRQ     BIT(11)
+# define PCI_IRQ_MASK               BIT(12)
+# define TIMER_IRQ_MASK             BIT(13)
+# define FIFO_IRQ_MASK              BIT(14)
+# define SCSI_IRQ_MASK              BIT(15)
+# define IRQ_CONTROL_ALL_IRQ_MASK   0xf000
+# define IRQSTATUS_ANY_IRQ          (IRQSTATUS_RESELECT_OCCUER	| \
+				     IRQSTATUS_PHASE_CHANGE_IRQ	| \
+				     IRQSTATUS_SCSIRESET_IRQ	| \
+				     IRQSTATUS_TIMER_IRQ	| \
+				     IRQSTATUS_FIFO_SHLD_IRQ	| \
+				     IRQSTATUS_PCI_IRQ		| \
+				     IRQSTATUS_BMCNTERR_IRQ	| \
+				     IRQSTATUS_AUTOSCSI_IRQ	)
+
+#define TRANSFER_CONTROL 0x02	/* BASE+02, W, W */
+#define TRANSFER_STATUS  0x02	/* BASE+02, W, R */
+# define CB_MMIO_MODE        BIT(0)
+# define CB_IO_MODE          BIT(1)
+# define BM_TEST             BIT(2)
+# define BM_TEST_DIR         BIT(3)
+# define DUAL_EDGE_ENABLE    BIT(4)
+# define NO_TRANSFER_TO_HOST BIT(5)
+# define TRANSFER_GO         BIT(7)
+# define BLIEND_MODE         BIT(8)
+# define BM_START            BIT(9)
+# define ADVANCED_BM_WRITE   BIT(10)
+# define BM_SINGLE_MODE      BIT(11)
+# define FIFO_TRUE_FULL      BIT(12)
+# define FIFO_TRUE_EMPTY     BIT(13)
+# define ALL_COUNTER_CLR     BIT(14)
+# define FIFOTEST            BIT(15)
+
+#define INDEX_REG 0x04		/* BASE+04, Byte(R/W), Word(R) */
+
+#define TIMER_SET 0x06		/* BASE+06, W, R/W */
+# define TIMER_CNT_MASK 0xff
+# define TIMER_STOP     BIT(8)
+
+#define DATA_REG_LOW 0x08	/* BASE+08, LowW, R/W */
+#define DATA_REG_HI  0x0a	/* BASE+0a, Hi-W, R/W */
+
+#define FIFO_REST_CNT 0x0c	/* BASE+0c, W, R/W */
+# define FIFO_REST_MASK       0x1ff
+# define FIFO_EMPTY_SHLD_FLAG BIT(14)
+# define FIFO_FULL_SHLD_FLAG  BIT(15)
+
+#define SREQ_SMPL_RATE 0x0f	/* BASE+0f, B, R/W */
+# define SREQSMPLRATE_RATE0 BIT(0)
+# define SREQSMPLRATE_RATE1 BIT(1)
+# define SAMPLING_ENABLE    BIT(2)
+
+#define SCSI_BUS_CONTROL 0x10	/* BASE+10, B, R/W */
+# define BUSCTL_SEL         BIT(0)
+# define BUSCTL_RST         BIT(1)
+# define BUSCTL_DATAOUT_ENB BIT(2)
+# define BUSCTL_ATN         BIT(3)
+# define BUSCTL_ACK         BIT(4)
+# define BUSCTL_BSY         BIT(5)
+# define AUTODIRECTION      BIT(6)
+# define ACKENB             BIT(7)
+
+#define CLR_COUNTER 0x12	/* BASE+12, B, W */
+# define ACK_COUNTER_CLR       BIT(0)
+# define SREQ_COUNTER_CLR      BIT(1)
+# define FIFO_HOST_POINTER_CLR BIT(2)
+# define FIFO_REST_COUNT_CLR   BIT(3)
+# define BM_COUNTER_CLR        BIT(4)
+# define SAVED_ACK_CLR         BIT(5)
+# define CLRCOUNTER_ALLMASK    (BIT(0) | BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5))
+
+#define SCSI_BUS_MONITOR 0x12	/* BASE+12, B, R */
+# define BUSMON_MSG BIT(0)
+# define BUSMON_IO  BIT(1)
+# define BUSMON_CD  BIT(2)
+# define BUSMON_BSY BIT(3)
+# define BUSMON_ACK BIT(4)
+# define BUSMON_REQ BIT(5)
+# define BUSMON_SEL BIT(6)
+# define BUSMON_ATN BIT(7)
+
+#define COMMAND_DATA 0x14	/* BASE+14, B, R/W */
+
+#define PARITY_CONTROL 0x16	/* BASE+16, B, R/W */
+# define PARITY_CHECK_ENABLE BIT(0)
+# define PARITY_ERROR_CLEAR  BIT(1)
+#define PARITY_STATUS  0x16
+//# define PARITY_CHECK_ENABLE BIT(0)
+# define PARITY_ERROR_NORMAL BIT(1)
+# define PARITY_ERROR_LSB    BIT(1)
+# define PARITY_ERROR_MSB    BIT(2)
+
+#define RESELECT_ID 0x18	/* BASE+18, B, R */
+
+#define COMMAND_CONTROL 0x18	/* BASE+18, W, W */
+# define CLEAR_CDB_FIFO_POINTER BIT(0)
+# define AUTO_COMMAND_PHASE     BIT(1)
+# define AUTOSCSI_START         BIT(2)
+# define AUTOSCSI_RESTART       BIT(3)
+# define AUTO_PARAMETER         BIT(4)
+# define AUTO_ATN               BIT(5)
+# define AUTO_MSGIN_00_OR_04    BIT(6)
+# define AUTO_MSGIN_02          BIT(7)
+# define AUTO_MSGIN_03          BIT(8)
+
+#define SET_ARBIT 0x1a		/* BASE+1a, B, W */
+# define ARBIT_GO    BIT(0)
+# define ARBIT_CLEAR BIT(1)
+
+#define ARBIT_STATUS 0x1a	/* BASE+1a, B, R */
+//# define ARBIT_GO             BIT(0)
+# define ARBIT_WIN            BIT(1)
+# define ARBIT_FAIL           BIT(2)
+# define AUTO_PARAMETER_VALID BIT(3)
+# define SGT_VALID            BIT(4)
+
+#define SYNC_REG 0x1c	/* BASE+1c, B, R/W */
+
+#define ACK_WIDTH 0x1d	/* BASE+1d, B, R/W */
+
+#define SCSI_DATA_WITH_ACK 0x20	/* BASE+20, B, R/W */
+#define SCSI_OUT_LATCH_TARGET_ID 0x22	/* BASE+22, B, W */
+#define SCSI_DATA_IN 0x22	/* BASE+22, B, R */
+
+#define SCAM_CONTROL 0x24	/* BASE+24, B, W */
+#define SCAM_STATUS  0x24	/* BASE+24, B, R */
+# define SCAM_MSG    BIT(0)
+# define SCAM_IO     BIT(1)
+# define SCAM_CD     BIT(2)
+# define SCAM_BSY    BIT(3)
+# define SCAM_SEL    BIT(4)
+# define SCAM_XFEROK BIT(5)
+
+#define SCAM_DATA 0x26	/* BASE+26, B, R/W */
+# define SD0					BIT(0)
+# define SD1					BIT(1)
+# define SD2					BIT(2)
+# define SD3					BIT(3)
+# define SD4					BIT(4)
+# define SD5					BIT(5)
+# define SD6					BIT(6)
+# define SD7					BIT(7)
+
+#define SACK_CNT 0x28	/* BASE+28, DW, R/W */
+#define SREQ_CNT 0x2c	/* BASE+2c, DW, R/W */
+
+#define FIFO_DATA_LOW 0x30	/* BASE+30, B/W/DW, R/W */
+#define FIFO_DATA_HIGH 0x32	/* BASE+32, B/W, R/W */
+#define BM_START_ADR 0x34	/* BASE+34, DW, R/W */
+
+#define BM_CNT 0x38	/* BASE+38, DW, R/W */
+# define BM_COUNT_MASK 0x0001ffff
+# define SGTEND        BIT(31)
+
+#define SGT_ADR 0x3c	/* BASE+3c, DW, R/W */
+#define WAIT_REG 0x40	/* Bi only */
+
+#define SCSI_EXECUTE_PHASE 0x40	/* BASE+40, W, R */
+# define COMMAND_PHASE     BIT(0)
+# define DATA_IN_PHASE     BIT(1)
+# define DATA_OUT_PHASE    BIT(2)
+# define MSGOUT_PHASE      BIT(3)
+# define STATUS_PHASE      BIT(4)
+# define ILLEGAL_PHASE     BIT(5)
+# define BUS_FREE_OCCUER   BIT(6)
+# define MSG_IN_OCCUER     BIT(7)
+# define MSG_OUT_OCCUER    BIT(8)
+# define SELECTION_TIMEOUT BIT(9)
+# define MSGIN_00_VALID    BIT(10)
+# define MSGIN_02_VALID    BIT(11)
+# define MSGIN_03_VALID    BIT(12)
+# define MSGIN_04_VALID    BIT(13)
+# define AUTOSCSI_BUSY     BIT(15)
+
+#define SCSI_CSB_IN 0x42	/* BASE+42, B, R */
+
+#define SCSI_MSG_OUT 0x44	/* BASE+44, DW, R/W */
+# define MSGOUT_COUNT_MASK (BIT(0)|BIT(1))
+# define MV_VALID	      BIT(7)
+
+#define SEL_TIME_OUT 0x48	/* BASE+48, W, R/W */
+#define SAVED_SACK_CNT 0x4c	/* BASE+4c, DW, R */
+
+#define HTOSDATADELAY		0x50	/* BASE+50, B, R/W */
+#define STOHDATADELAY		0x54	/* BASE+54, B, R/W */
+#define ACKSUMCHECKRD		0x58	/* BASE+58, W, R */
+#define REQSUMCHECKRD		0x5c	/* BASE+5c, W, R */
+
+
+/********************/
+/* indexed register */
+/********************/
+
+#define CLOCK_DIV 0x00	/* BASE+08, IDX+00, B, R/W */
+# define CLOCK_2		BIT(0)	/* MCLK/2 */
+# define CLOCK_4		BIT(1)	/* MCLK/4 */
+# define PCICLK			BIT(7)	/* PCICLK (33MHz) */
+
+#define TERM_PWR_CONTROL 0x01	/* BASE+08, IDX+01, B, R/W */
+# define BPWR  BIT(0)
+# define SENSE BIT(1)	/* Read Only */
+
+#define EXT_PORT_DDR 0x02	/* BASE+08, IDX+02, B, R/W */
+#define EXT_PORT     0x03	/* BASE+08, IDX+03, B, R/W */
+# define LED_ON	 0
+# define LED_OFF 1
+
+#define IRQ_SELECT 0x04	/* BASE+08, IDX+04, W, R/W */
+# define IRQSELECT_RESELECT_IRQ      BIT(0)
+# define IRQSELECT_PHASE_CHANGE_IRQ  BIT(1)
+# define IRQSELECT_SCSIRESET_IRQ     BIT(2)
+# define IRQSELECT_TIMER_IRQ         BIT(3)
+# define IRQSELECT_FIFO_SHLD_IRQ     BIT(4)
+# define IRQSELECT_TARGET_ABORT_IRQ  BIT(5)
+# define IRQSELECT_MASTER_ABORT_IRQ  BIT(6)
+# define IRQSELECT_SERR_IRQ          BIT(7)
+# define IRQSELECT_PERR_IRQ          BIT(8)
+# define IRQSELECT_BMCNTERR_IRQ      BIT(9)
+# define IRQSELECT_AUTO_SCSI_SEQ_IRQ BIT(10)
+
+#define OLD_SCSI_PHASE 0x05	/* BASE+08, IDX+05, B, R */
+# define OLD_MSG  BIT(0)
+# define OLD_IO   BIT(1)
+# define OLD_CD   BIT(2)
+# define OLD_BUSY BIT(3)
+
+#define FIFO_FULL_SHLD_COUNT 0x06	/* BASE+08, IDX+06, B, R/W */
+#define FIFO_EMPTY_SHLD_COUNT 0x07	/* BASE+08, IDX+07, B, R/W */
+
+#define EXP_ROM_CONTROL 0x08	/* BASE+08, IDX+08, B, R/W */
+
+#define EXP_ROM_ADRL		0x09	/* BASE+08, IDX+09, W, R/W */
+
+#define EXP_ROM_DATA		0x0a	/* BASE+08, IDX+0a, B, R/W */
+
+#define CHIP_MODE 0x0b	/* Bi only */
+# define OEM0 BIT(1)
+# define OEM1 BIT(2)
+# define OPTB BIT(3)
+# define OPTC BIT(4)
+# define OPTD BIT(5)
+# define OPTE BIT(6)
+# define OPTF BIT(7)
+
+#define MISC_WR 0x0c	/* BASE+08, IDX+0c, W, R/W */
+#define MISC_RD 0x0c
+# define SCSI_DIRECTION_DETECTOR_SELECT BIT(0)
+# define SCSI2_HOST_DIRECTION_VALID	BIT(1)	/* Read only */
+# define HOST2_SCSI_DIRECTION_VALID	BIT(2)	/* Read only */
+# define DELAYED_BMSTART                BIT(3)
+# define MASTER_TERMINATION_SELECT      BIT(4)
+# define BMREQ_NEGATE_TIMING_SEL        BIT(5)
+# define AUTOSEL_TIMING_SEL             BIT(6)
+# define MISC_MABORT_MASK		BIT(7)
+# define BMSTOP_CHANGE2_NONDATA_PHASE	BIT(8)
+
+#define BM_CYCLE 0x0d	/* BASE+08, IDX+0d, B, R/W */
+# define BM_CYCLE0		 BIT(0)
+# define BM_CYCLE1		 BIT(1)
+# define BM_FRAME_ASSERT_TIMING	 BIT(2)
+# define BM_IRDY_ASSERT_TIMING	 BIT(3)
+# define BM_SINGLE_BUS_MASTER	 BIT(4)
+# define MEMRD_CMD0              BIT(5)
+# define SGT_AUTO_PARA_MEMED_CMD BIT(6)
+# define MEMRD_CMD1              BIT(7)
+
+
+#define SREQ_EDGH 0x0e	/* BASE+08, IDX+0e, B, W */
+# define SREQ_EDGH_SELECT BIT(0)
+
+#define UP_CNT		0x0f	/* BASE+08, IDX+0f, B, W */
+#define CFG_CMD_STR      0x10	/* BASE+08, IDX+10, W, R */
+#define CFG_LATE_CACHE   0x11	/* BASE+08, IDX+11, W, R/W */
+#define CFG_BASE_ADR_1   0x12	/* BASE+08, IDX+12, W, R */
+#define CFG_BASE_ADR_2   0x13	/* BASE+08, IDX+13, W, R */
+#define CFG_INLINE       0x14	/* BASE+08, IDX+14, W, R */
+
+#define SERIAL_ROM_CTL	0x15	/* BASE+08, IDX+15, B, R */
+# define SCL 					BIT(0)
+# define ENA					BIT(1)
+# define SDA					BIT(2)
+
+#define FIFO_HST_POINTER	0x16	/* BASE+08, IDX+16, B, R/W */
+#define SREQ_DELAY		0x17	/* BASE+08, IDX+17, B, R/W */
+#define SACK_DELAY		0x18	/* BASE+08, IDX+18, B, R/W */
+#define SREQ_NOISE_CANCEL	0x19	/* BASE+08, IDX+19, B, R/W */
+#define SDP_NOISE_CANCEL	0x1a	/* BASE+08, IDX+1a, B, R/W */
+#define DELAY_TEST		0x1b	/* BASE+08, IDX+1b, B, R/W */
+#define SD0_NOISE_CANCEL	0x20	/* BASE+08, IDX+20, B, R/W */
+#define SD1_NOISE_CANCEL	0x21	/* BASE+08, IDX+21, B, R/W */
+#define SD2_NOISE_CANCEL	0x22	/* BASE+08, IDX+22, B, R/W */
+#define SD3_NOISE_CANCEL	0x23	/* BASE+08, IDX+23, B, R/W */
+#define SD4_NOISE_CANCEL	0x24	/* BASE+08, IDX+24, B, R/W */
+#define SD5_NOISE_CANCEL	0x25	/* BASE+08, IDX+25, B, R/W */
+#define SD6_NOISE_CANCEL	0x26	/* BASE+08, IDX+26, B, R/W */
+#define SD7_NOISE_CANCEL	0x27	/* BASE+08, IDX+27, B, R/W */
+
+
+/*
+ * Useful Bus Monitor status combinations.
+ */
+#define BUSMON_BUS_FREE    0
+#define BUSMON_COMMAND     ( BUSMON_BSY |                          BUSMON_CD | BUSMON_REQ )
+#define BUSMON_MESSAGE_IN  ( BUSMON_BSY | BUSMON_MSG | BUSMON_IO | BUSMON_CD | BUSMON_REQ )
+#define BUSMON_MESSAGE_OUT ( BUSMON_BSY | BUSMON_MSG |             BUSMON_CD | BUSMON_REQ )
+#define BUSMON_DATA_IN     ( BUSMON_BSY |              BUSMON_IO |             BUSMON_REQ )
+#define BUSMON_DATA_OUT    ( BUSMON_BSY |                                      BUSMON_REQ )
+#define BUSMON_STATUS      ( BUSMON_BSY |              BUSMON_IO | BUSMON_CD | BUSMON_REQ )
+#define BUSMON_RESELECT    (                           BUSMON_IO                          | BUSMON_SEL)
+#define BUSMON_PHASE_MASK  (              BUSMON_MSG | BUSMON_IO | BUSMON_CD              | BUSMON_SEL)
+
+#define BUSPHASE_COMMAND     ( BUSMON_COMMAND     & BUSMON_PHASE_MASK )
+#define BUSPHASE_MESSAGE_IN  ( BUSMON_MESSAGE_IN  & BUSMON_PHASE_MASK )
+#define BUSPHASE_MESSAGE_OUT ( BUSMON_MESSAGE_OUT & BUSMON_PHASE_MASK )
+#define BUSPHASE_DATA_IN     ( BUSMON_DATA_IN     & BUSMON_PHASE_MASK )
+#define BUSPHASE_DATA_OUT    ( BUSMON_DATA_OUT    & BUSMON_PHASE_MASK )
+#define BUSPHASE_STATUS      ( BUSMON_STATUS      & BUSMON_PHASE_MASK )
+#define BUSPHASE_SELECT      ( BUSMON_SEL | BUSMON_IO )
+
+#endif _NSP32_H
+/* end */
diff -Nru a/drivers/scsi/nsp32_debug.c b/drivers/scsi/nsp32_debug.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/scsi/nsp32_debug.c	Mon Oct  7 11:26:06 2002
@@ -0,0 +1,262 @@
+/*
+ * Workbit NinjaSCSI-32Bi/UDE PCI/Cardbus SCSI Host Bus Adapter driver
+ * Debug routine
+ *
+ * This software may be used and distributed according to the terms of
+ * the GNU General Public License.
+ */
+
+/*
+ * Show the command data of a command
+ */
+static const char unknown[] = "UNKNOWN";
+
+static const char * group_0_commands[] = {
+/* 00-03 */ "Test Unit Ready", "Rezero Unit", unknown, "Request Sense",
+/* 04-07 */ "Format Unit", "Read Block Limits", unknown, "Reasssign Blocks",
+/* 08-0d */ "Read (6)", unknown, "Write (6)", "Seek (6)", unknown, unknown,
+/* 0e-12 */ unknown, "Read Reverse", "Write Filemarks", "Space", "Inquiry",  
+/* 13-16 */ unknown, "Recover Buffered Data", "Mode Select", "Reserve",
+/* 17-1b */ "Release", "Copy", "Erase", "Mode Sense", "Start/Stop Unit",
+/* 1c-1d */ "Receive Diagnostic", "Send Diagnostic", 
+/* 1e-1f */ "Prevent/Allow Medium Removal", unknown,
+};
+
+
+static const char *group_1_commands[] = {
+/* 20-22 */  unknown, unknown, unknown,
+/* 23-28 */ unknown, unknown, "Read Capacity", unknown, unknown, "Read (10)",
+/* 29-2d */ unknown, "Write (10)", "Seek (10)", unknown, unknown,
+/* 2e-31 */ "Write Verify","Verify", "Search High", "Search Equal",
+/* 32-34 */ "Search Low", "Set Limits", "Prefetch or Read Position", 
+/* 35-37 */ "Synchronize Cache","Lock/Unlock Cache", "Read Defect Data",
+/* 38-3c */ "Medium Scan", "Compare","Copy Verify", "Write Buffer", "Read Buffer",
+/* 3d-3f */ "Update Block", "Read Long",  "Write Long",
+};
+
+
+static const char *group_2_commands[] = {
+/* 40-41 */ "Change Definition", "Write Same", 
+/* 42-48 */ "Read Sub-Ch(cd)", "Read TOC", "Read Header(cd)", "Play Audio(cd)", unknown, "Play Audio MSF(cd)", "Play Audio Track/Index(cd)", 
+/* 49-4f */ "Play Track Relative(10)(cd)", unknown, "Pause/Resume(cd)", "Log Select", "Log Sense", unknown, unknown,
+/* 50-55 */ unknown, unknown, unknown, unknown, unknown, "Mode Select (10)",
+/* 56-5b */ unknown, unknown, unknown, unknown, "Mode Sense (10)", unknown,
+/* 5c-5f */ unknown, unknown, unknown,
+};
+
+#define group(opcode) (((opcode) >> 5) & 7)
+
+#define RESERVED_GROUP  0
+#define VENDOR_GROUP    1
+#define NOTEXT_GROUP    2
+
+static const char **commands[] = {
+    group_0_commands, group_1_commands, group_2_commands, 
+    (const char **) RESERVED_GROUP, (const char **) RESERVED_GROUP, 
+    (const char **) NOTEXT_GROUP, (const char **) VENDOR_GROUP, 
+    (const char **) VENDOR_GROUP
+};
+
+static const char reserved[] = "RESERVED";
+static const char vendor[] = "VENDOR SPECIFIC";
+
+static void print_opcodek(unsigned char opcode)
+{
+	const char **table = commands[ group(opcode) ];
+
+	switch ((unsigned long) table) {
+	case RESERVED_GROUP:
+		printk("%s[%02x] ", reserved, opcode); 
+		break;
+	case NOTEXT_GROUP:
+		printk("%s(notext)[%02x] ", unknown, opcode); 
+		break;
+	case VENDOR_GROUP:
+		printk("%s[%02x] ", vendor, opcode); 
+		break;
+	default:
+		if (table[opcode & 0x1f] != unknown)
+			printk("%s[%02x] ", table[opcode & 0x1f], opcode);
+		else
+			printk("%s[%02x] ", unknown, opcode);
+		break;
+	}
+}
+
+static void print_commandk (unsigned char *command)
+{
+	int i,s;
+//	printk(KERN_DEBUG);
+	print_opcodek(command[0]);
+	/*printk(KERN_DEBUG __func__ " ");*/
+	if ((command[0] >> 5) == 6 ||
+	    (command[0] >> 5) == 7 ) {
+		s = 12; /* vender specific */
+	} else {
+		s = COMMAND_SIZE(command[0]);
+	}
+
+	for ( i = 1; i < s; ++i) {
+		printk("%02x ", command[i]);
+	}
+
+	switch (s) {
+	case 6:
+		printk("LBA=%d len=%d",
+		       (((unsigned int)command[1] & 0x0f) << 16) |
+		       ( (unsigned int)command[2]         <<  8) |
+		       ( (unsigned int)command[3]              ),
+		       (unsigned int)command[4]
+			);
+		break;
+	case 10:
+		printk("LBA=%d len=%d",
+		       ((unsigned int)command[2] << 24) |
+		       ((unsigned int)command[3] << 16) |
+		       ((unsigned int)command[4] <<  8) |
+		       ((unsigned int)command[5]      ),
+		       ((unsigned int)command[7] <<  8) |
+		       ((unsigned int)command[8]      )
+		       );
+		break;
+	case 12:
+		printk("LBA=%d len=%d",
+		       ((unsigned int)command[2] << 24) |
+		       ((unsigned int)command[3] << 16) |
+		       ((unsigned int)command[4] <<  8) |
+		       ((unsigned int)command[5]      ),
+		       ((unsigned int)command[6] << 24) |
+		       ((unsigned int)command[7] << 16) |
+		       ((unsigned int)command[8] <<  8) |
+		       ((unsigned int)command[9]      )
+		       );
+		break;
+	default:
+		break;
+	}
+	printk("\n");
+}
+
+static void show_command(Scsi_Cmnd *ptr)
+{
+	print_commandk(ptr->cmnd);
+}
+
+static void show_busphase(unsigned char stat)
+{
+	switch(stat) {
+	case BUSPHASE_COMMAND:
+		printk( "BUSPHASE_COMMAND\n");
+		break;
+	case BUSPHASE_MESSAGE_IN:
+		printk( "BUSPHASE_MESSAGE_IN\n");
+		break;
+	case BUSPHASE_MESSAGE_OUT:
+		printk( "BUSPHASE_MESSAGE_OUT\n");
+		break;
+	case BUSPHASE_DATA_IN:
+		printk( "BUSPHASE_DATA_IN\n");
+		break;
+	case BUSPHASE_DATA_OUT:
+		printk( "BUSPHASE_DATA_OUT\n");
+		break;
+	case BUSPHASE_STATUS:
+		printk( "BUSPHASE_STATUS\n");
+		break;
+	case BUSPHASE_SELECT:
+		printk( "BUSPHASE_SELECT\n");
+		break;
+	default:
+		printk( "BUSPHASE_other: 0x%x\n", stat);
+		break;
+	}
+}
+
+static void show_autophase(unsigned short i)
+{
+	printk("auto: 0x%x,", i);
+
+	if(i & COMMAND_PHASE) {
+		printk(" cmd");
+	}
+	if(i & DATA_IN_PHASE) {
+		printk(" din");
+	}
+	if(i & DATA_OUT_PHASE) {
+		printk(" dout");
+	}
+	if(i & MSGOUT_PHASE) {
+		printk(" mout");
+	}
+	if(i & STATUS_PHASE) {
+		printk(" stat");
+	}
+	if(i & ILLEGAL_PHASE) {
+		printk(" ill");
+	}
+	if(i & BUS_FREE_OCCUER) {
+		printk(" bfree-o");
+	}
+	if(i & MSG_IN_OCCUER) {
+		printk(" min-o");
+	}
+	if(i & MSG_OUT_OCCUER) {
+		printk(" mout-o");
+	}
+	if(i & SELECTION_TIMEOUT) {
+		printk(" sel");
+	}
+	if(i & MSGIN_00_VALID) {
+		printk(" m0");
+	}
+	if(i & MSGIN_02_VALID) {
+		printk(" m2");
+	}
+	if(i & MSGIN_03_VALID) {
+		printk(" m3");
+	}
+	if(i & MSGIN_04_VALID) {
+		printk(" m4");
+	}
+	if(i & AUTOSCSI_BUSY) {
+		printk(" busy");
+	}
+
+	printk("\n");
+}
+
+static void nsp32_print_register(int base)
+{
+	if (!(NSP32_DEBUG_MASK & NSP32_SPECIAL_PRINT_REGISTER))
+		return;
+
+	printk("Phase=0x%x, ", nsp32_read1(base, SCSI_BUS_MONITOR));
+	printk("OldPhase=0x%x, ", nsp32_index_read1(base, OLD_SCSI_PHASE));
+	printk("syncreg=0x%x, ", nsp32_read1(base, SYNC_REG));
+	printk("ackwidth=0x%x, ", nsp32_read1(base, ACK_WIDTH));
+	printk("sgtpaddr=0x%lx, ", nsp32_read4(base, SGT_ADR));
+	printk("scsioutlatch=0x%x, ", nsp32_read1(base, SCSI_OUT_LATCH_TARGET_ID));
+	printk("msgout=0x%lx, ", nsp32_read4(base, SCSI_MSG_OUT));
+	printk("miscrd=0x%x, ", nsp32_index_read2(base, MISC_WR));
+	printk("seltimeout=0x%x, ", nsp32_read2(base, SEL_TIME_OUT));
+	printk("sreqrate=0x%x, ", nsp32_read1(base, SREQ_SMPL_RATE));
+	printk("transStatus=0x%x, ", nsp32_read2(base, TRANSFER_STATUS));
+	printk("reselectid=0x%x, ", nsp32_read2(base, COMMAND_CONTROL));
+	printk("arbit=0x%x, ", nsp32_read1(base, ARBIT_STATUS));
+	printk("BmStart=0x%lx, ", nsp32_read4(base, BM_START_ADR));
+	printk("BmCount=0x%lx, ", nsp32_read4(base, BM_CNT));
+	printk("SackCnt=0x%lx, ", nsp32_read4(base, SACK_CNT));
+	printk("SReqCnt=0x%lx, ", nsp32_read4(base, SREQ_CNT));
+	printk("SavedSackCnt=0x%lx, ", nsp32_read4(base, SAVED_SACK_CNT));
+	printk("ScsiBusControl=0x%x, ", nsp32_read1(base, SCSI_BUS_CONTROL));
+	printk("FifoRestCnt=0x%x, ", nsp32_read2(base, FIFO_REST_CNT));
+	printk("CdbIn=0x%x, ", nsp32_read1(base, SCSI_CSB_IN));
+	printk("\n");
+
+	if (0) {
+		printk("execph=0x%x, ", nsp32_read2(base, SCSI_EXECUTE_PHASE));
+		printk("IrqStatus=0x%x, ", nsp32_read2(base, IRQ_STATUS));
+		printk("\n");
+	}
+}
+
diff -Nru a/drivers/scsi/nsp32_io.h b/drivers/scsi/nsp32_io.h
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/scsi/nsp32_io.h	Mon Oct  7 11:26:06 2002
@@ -0,0 +1,269 @@
+/*
+ * Workbit NinjaSCSI-32Bi/UDE PCI/Cardbus SCSI Host Bus Adapter driver
+ * I/O routine
+ *
+ * This software may be used and distributed according to the terms of
+ * the GNU General Public License.
+ */
+
+#ifndef _NSP32_IO_H
+#define _NSP32_IO_H
+
+static inline void nsp32_write1(unsigned int  base,
+				unsigned int  index,
+				unsigned char val)
+{
+	outb(val, (base + index));
+}
+
+static inline unsigned char nsp32_read1(unsigned int base,
+					unsigned int index)
+{
+	return inb(base + index);
+}
+
+static inline void nsp32_write2(unsigned int  base,
+				unsigned int  index,
+				unsigned short val)
+{
+	outw(cpu_to_le16(val), (base + index));
+}
+
+static inline unsigned short nsp32_read2(unsigned int base,
+					 unsigned int index)
+{
+	return le16_to_cpu(inw(base + index));
+}
+
+static inline void nsp32_write4(unsigned int  base,
+				unsigned int  index,
+				unsigned long val)
+{
+	outl(cpu_to_le32(val), (base + index));
+}
+
+static inline unsigned long nsp32_read4(unsigned int base,
+					unsigned int index)
+{
+	return le32_to_cpu(inl(base + index));
+}
+
+/*==============================================*/
+
+static inline void nsp32_mmio_write1(unsigned long base,
+				     unsigned int  index,
+				     unsigned char val)
+{
+	volatile unsigned char *ptr;
+
+	ptr = (unsigned char *)(base + NSP32_MMIO_OFFSET + index);
+
+	*ptr = val;
+}
+
+static inline unsigned char nsp32_mmio_read1(unsigned long base,
+					     unsigned int  index)
+{
+	volatile unsigned char *ptr;
+
+	ptr = (unsigned char *)(base + NSP32_MMIO_OFFSET + index);
+
+	return *ptr;
+}
+
+static inline void nsp32_mmio_write2(unsigned long base,
+				     unsigned int  index,
+				     unsigned short val)
+{
+	volatile unsigned short *ptr;
+
+	ptr = (unsigned short *)(base + NSP32_MMIO_OFFSET + index);
+
+	*ptr = cpu_to_le16(val);
+}
+
+static inline unsigned short nsp32_mmio_read2(unsigned long base,
+					      unsigned int  index)
+{
+	volatile unsigned short *ptr;
+
+	//printk(__FUNCTION__ "\n");
+
+	ptr = (unsigned short *)(base + NSP32_MMIO_OFFSET + index);
+
+	return le16_to_cpu(*ptr);
+}
+
+static inline void nsp32_mmio_write4(unsigned long base,
+				     unsigned int  index,
+				     unsigned long val)
+{
+	volatile unsigned long *ptr;
+
+	ptr = (unsigned long *)(base + NSP32_MMIO_OFFSET + index);
+
+	*ptr = cpu_to_le32(val);
+}
+
+static inline unsigned long nsp32_mmio_read4(unsigned long base,
+					     unsigned int  index)
+{
+	volatile unsigned long *ptr;
+
+	//printk(__FUNCTION__ "\n");
+
+	ptr = (unsigned long *)(base + NSP32_MMIO_OFFSET + index);
+
+	return le32_to_cpu(*ptr);
+}
+
+
+/*=============================================*/
+
+
+static inline unsigned char nsp32_index_read1(unsigned int base,
+					      unsigned int reg)
+{
+	outb(reg, base + INDEX_REG);
+	return inb(base + DATA_REG_LOW);
+}
+
+static inline void nsp32_index_write1(unsigned int  base,
+				      unsigned int  reg,
+				      unsigned char val)
+{
+	outb(reg, base + INDEX_REG);
+	outb(val, base + DATA_REG_LOW);
+}
+
+static inline unsigned short nsp32_index_read2(unsigned int base,
+					       unsigned int reg)
+{
+	outb(reg, base + INDEX_REG);
+	return le16_to_cpu(inw(base + DATA_REG_LOW));
+}
+
+static inline void nsp32_index_write2(unsigned int  base,
+				      unsigned int  reg,
+				      unsigned short val)
+{
+	outb(reg, base + INDEX_REG);
+	outw(cpu_to_le16(val), base + DATA_REG_LOW);
+}
+
+static inline unsigned long nsp32_index_read4(unsigned int base,
+					      unsigned int reg)
+{
+	unsigned long h,l;
+
+	outb(reg, base + INDEX_REG);
+	l = le16_to_cpu(inw(base + DATA_REG_LOW));
+	h = le16_to_cpu(inw(base + DATA_REG_HI ));
+
+	return ((h << 16) | l);
+}
+
+static inline void nsp32_index_write4(unsigned int  base,
+				      unsigned int  reg,
+				      unsigned long val)
+{
+	unsigned long h,l;
+
+	h = (val & 0xffff0000) >> 16;
+	l = (val & 0x0000ffff) >> 0;
+
+	outb(reg, base + INDEX_REG);
+	outw(cpu_to_le16(l), base + DATA_REG_LOW);
+	outw(cpu_to_le16(h), base + DATA_REG_HI);
+}
+
+
+/* ===================================*/
+
+static inline unsigned char nsp32_mmio_index_read1(unsigned int base,
+						   unsigned int reg)
+{
+	volatile unsigned short *index_ptr, *data_ptr;
+
+	index_ptr = (unsigned short *)(base + NSP32_MMIO_OFFSET + INDEX_REG);
+	data_ptr  = (unsigned short *)(base + NSP32_MMIO_OFFSET + DATA_REG_LOW);
+
+	*index_ptr = reg;
+
+	return ((*data_ptr) & 0xff);
+}
+
+static inline void nsp32_mmio_index_write1(unsigned int base,
+					   unsigned int reg,
+					   unsigned char val)
+{
+	volatile unsigned short *index_ptr, *data_ptr;
+
+	index_ptr = (unsigned short *)(base + NSP32_MMIO_OFFSET + INDEX_REG);
+	data_ptr  = (unsigned short *)(base + NSP32_MMIO_OFFSET + DATA_REG_LOW);
+
+	*index_ptr = reg;
+	*data_ptr  = (unsigned short)val;
+}
+
+static inline unsigned short nsp32_mmio_index_read2(unsigned int base,
+						    unsigned int reg)
+{
+	volatile unsigned short *index_ptr, *data_ptr;
+
+	index_ptr = (unsigned short *)(base + NSP32_MMIO_OFFSET + INDEX_REG);
+	data_ptr  = (unsigned short *)(base + NSP32_MMIO_OFFSET + DATA_REG_LOW);
+
+	*index_ptr = reg;
+
+	return le16_to_cpu(*data_ptr);
+}
+
+static inline void nsp32_mmio_index_write2(unsigned int base,
+					   unsigned int reg,
+					   unsigned short val)
+{
+	volatile unsigned short *index_ptr, *data_ptr;
+
+	index_ptr = (unsigned short *)(base + NSP32_MMIO_OFFSET + INDEX_REG);
+	data_ptr  = (unsigned short *)(base + NSP32_MMIO_OFFSET + DATA_REG_LOW);
+
+	*index_ptr = reg;
+	*data_ptr  = val;
+}
+
+/*-------------------------------------------------------------------*/
+
+static inline void nsp32_multi_read4(unsigned int   BaseAddr,
+				     unsigned int   Register,
+				     void          *buf,
+				     unsigned long  count)
+{
+	insl(BaseAddr + Register, buf, count);
+}
+
+static inline void nsp32_fifo_read(unsigned int   base,
+				   void          *buf,
+				   unsigned long  count)
+{
+	//DEBUG(0, __FUNCTION__ "() buf=0x%p, count=0x%lx*4\n", buf, count);
+	nsp32_multi_read4(base, FIFO_DATA_LOW, buf, count);
+}
+
+static inline void nsp32_multi_write4(unsigned int   BaseAddr,
+				      unsigned int   Register,
+				      void          *buf,
+				      unsigned long  count)
+{
+	outsl(BaseAddr + Register, buf, count);
+}
+
+static inline void nsp32_fifo_write(unsigned int   base,
+				    void          *buf,
+				    unsigned long  count)
+{
+	nsp32_multi_write4(base, FIFO_DATA_LOW, buf, count);
+}
+
+#endif _NSP32_IO_H
+/* end */
diff -Nru a/drivers/scsi/oktagon_esp.c b/drivers/scsi/oktagon_esp.c
--- a/drivers/scsi/oktagon_esp.c	Mon Oct  7 11:25:25 2002
+++ b/drivers/scsi/oktagon_esp.c	Mon Oct  7 11:25:25 2002
@@ -40,7 +40,7 @@
 #include <asm/amigahw.h>
 
 #ifdef USE_BOTTOM_HALF
-#include <linux/tqueue.h>
+#include <linux/workqueue.h>
 #include <linux/interrupt.h>
 #endif
 
@@ -77,9 +77,7 @@
 long oktag_to_io(long *paddr, long *addr, long len);
 long oktag_from_io(long *addr, long *paddr, long len);
 
-static struct tq_struct tq_fake_dma = {
-    routine:	dma_commit,
-};
+static DECLARE_WORK(tq_fake_dma, dma_commit, NULL);
 
 #define DMA_MAXTRANSFER 0x8000
 
@@ -515,9 +513,7 @@
 #ifdef USE_BOTTOM_HALF
 	if(dma_on)
 	 {
-	  tq_fake_dma.sync = 0;
-	  queue_task(&tq_fake_dma,&tq_immediate);
-	  mark_bh(IMMEDIATE_BH);
+	  schedule_work(&tq_fake_dma);
 	 }
 #else
 	while(len && !dma_irq_p(esp))
diff -Nru a/drivers/scsi/ppa.c b/drivers/scsi/ppa.c
--- a/drivers/scsi/ppa.c	Mon Oct  7 11:25:25 2002
+++ b/drivers/scsi/ppa.c	Mon Oct  7 11:25:25 2002
@@ -29,7 +29,7 @@
     int mode;			/* Transfer mode                */
     int host;			/* Host number (for proc)       */
     Scsi_Cmnd *cur_cmd;		/* Current queued command       */
-    struct tq_struct ppa_tq;	/* Polling interrupt stuff       */
+    struct work_struct ppa_tq;	/* Polling interrupt stuff       */
     unsigned long jstart;	/* Jiffies at start             */
     unsigned long recon_tmo;    /* How many usecs to wait for reconnection (6th bit) */
     unsigned int failed:1;	/* Failure flag                 */
@@ -801,7 +801,7 @@
     if (ppa_engine(tmp, cmd)) {
 	tmp->ppa_tq.data = (void *) tmp;
 	tmp->ppa_tq.sync = 0;
-	queue_task(&tmp->ppa_tq, &tq_timer);
+	schedule_delayed_work(&tmp->ppa_tq, 1);
 	return;
     }
     /* Command must of completed hence it is safe to let go... */
@@ -986,8 +986,7 @@
 
     ppa_hosts[host_no].ppa_tq.data = ppa_hosts + host_no;
     ppa_hosts[host_no].ppa_tq.sync = 0;
-    queue_task(&ppa_hosts[host_no].ppa_tq, &tq_immediate);
-    mark_bh(IMMEDIATE_BH);
+    schedule_work(&ppa_hosts[host_no].ppa_tq);
 
     return 0;
 }
diff -Nru a/drivers/scsi/qla1280.c b/drivers/scsi/qla1280.c
--- a/drivers/scsi/qla1280.c	Mon Oct  7 11:25:40 2002
+++ b/drivers/scsi/qla1280.c	Mon Oct  7 11:25:40 2002
@@ -131,14 +131,14 @@
         - Added check of device_id when handling non
           QLA12160s during detect().
     Rev  3.22 Beta January 5, 2001 BN Qlogic
-        - Changed queue_task() to schedule_task()
+        - Changed queue_task() to schedule_work()
           for kernels 2.4.0 and higher.
           Note: 2.4.0-testxx kernels released prior to
                 the actual 2.4.0 kernel release on January 2001
-                will get compile/link errors with schedule_task().
+                will get compile/link errors with schedule_work().
                 Please update your kernel to released 2.4.0 level,
                 or comment lines in this file flagged with  3.22
-                to resolve compile/link error of schedule_task().
+                to resolve compile/link error of schedule_work().
         - Added -DCONFIG_SMP in addition to -D__SMP__
           in Makefile for 2.4.0 builds of driver as module.
     Rev  3.21 Beta January 4, 2001 BN Qlogic
@@ -253,7 +253,7 @@
 #include <linux/pci.h>
 #include <linux/proc_fs.h>
 #include <linux/blk.h>
-#include <linux/tqueue.h>
+#include <linux/workqueue.h>
 #include <linux/stat.h>
 #include <linux/slab.h>
 
@@ -1190,9 +1190,9 @@
 					   &ha->done_q_last);
 /* 3.22 */
 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)	/* 3.22 */
-			queue_task(&ha->run_qla_bh, &tq_scheduler);
+			schedule_work(&ha->run_qla_bh);
 #else				/* 3.22 */
-			schedule_task(&ha->run_qla_bh);	/* 3.22 */
+			schedule_work(&ha->run_qla_bh);	/* 3.22 */
 #endif				/* 3.22 */
 			return 0;
 		}
diff -Nru a/drivers/scsi/qla1280.h b/drivers/scsi/qla1280.h
--- a/drivers/scsi/qla1280.h	Mon Oct  7 11:25:39 2002
+++ b/drivers/scsi/qla1280.h	Mon Oct  7 11:25:39 2002
@@ -1234,7 +1234,7 @@
 	struct scsi_lu *dev[MAX_EQ];	/* Logical unit queues */
 
 	/* bottom half run queue */
-	struct tq_struct run_qla_bh;
+	struct work_struct run_qla_bh;
 
 	/* Received ISP mailbox data. */
 	volatile uint16_t mailbox_out[MAILBOX_REGISTER_COUNT];
diff -Nru a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c
--- a/drivers/scsi/scsi.c	Mon Oct  7 11:25:35 2002
+++ b/drivers/scsi/scsi.c	Mon Oct  7 11:25:35 2002
@@ -616,25 +616,17 @@
 	 * If the host/device isn't busy, assume that something actually
 	 * completed, and that we should be able to queue a command now.
 	 *
-	 * Note that there is an implicit assumption that every host can
-	 * always queue at least one command.  If a host is inactive and
-	 * cannot queue any commands, I don't see how things could
-	 * possibly work anyways.
+	 * Note that the prior mid-layer assumption that any host could
+	 * always queue at least one command is now broken.  The mid-layer
+	 * will implement a user specifiable stall (see
+	 * scsi_host.max_host_blocked and scsi_device.max_device_blocked)
+	 * if a command is requeued with no other commands outstanding
+	 * either for the device or for the host.
 	 */
 	if (reason == SCSI_MLQUEUE_HOST_BUSY) {
-		if (host->host_busy == 0) {
-			if (scsi_retry_command(cmd) == 0) {
-				return 0;
-			}
-		}
-		host->host_blocked = TRUE;
+		host->host_blocked = host->max_host_blocked;
 	} else {
-		if (cmd->device->device_busy == 0) {
-			if (scsi_retry_command(cmd) == 0) {
-				return 0;
-			}
-		}
-		cmd->device->device_blocked = TRUE;
+                cmd->device->device_blocked = cmd->device->max_device_blocked;
 	}
 
 	/*
@@ -793,7 +785,7 @@
 			spin_unlock_irqrestore(host->host_lock, flags);
 			if (rtn != 0) {
 				scsi_delete_timer(SCpnt);
-				scsi_mlqueue_insert(SCpnt, SCSI_MLQUEUE_HOST_BUSY);
+				scsi_mlqueue_insert(SCpnt, rtn == SCSI_MLQUEUE_DEVICE_BUSY ? rtn : SCSI_MLQUEUE_HOST_BUSY);
 				SCSI_LOG_MLQUEUE(3,
 				   printk("queuecommand : request rejected\n"));                                
 			}
@@ -1396,8 +1388,8 @@
          * for both the queue full condition on a device, and for a
          * host full condition on the host.
          */
-        host->host_blocked = FALSE;
-        device->device_blocked = FALSE;
+        host->host_blocked = 0;
+        device->device_blocked = 0;
 
 	/*
 	 * If we have valid sense information, then some kind of recovery
diff -Nru a/drivers/scsi/scsi.h b/drivers/scsi/scsi.h
--- a/drivers/scsi/scsi.h	Mon Oct  7 11:25:28 2002
+++ b/drivers/scsi/scsi.h	Mon Oct  7 11:25:28 2002
@@ -611,11 +611,17 @@
 				 * this device */
 	unsigned expecting_cc_ua:1;	/* Expecting a CHECK_CONDITION/UNIT_ATTN
 					 * because we did a bus reset. */
-	unsigned device_blocked:1;	/* Device returned QUEUE_FULL. */
 	unsigned ten:1;		/* support ten byte read / write */
 	unsigned remap:1;	/* support remapping  */
 	unsigned starved:1;	/* unable to process commands because
 				   host busy */
+
+	unsigned int device_blocked;	/* Device returned QUEUE_FULL. */
+
+	unsigned int max_device_blocked; /* what device_blocked counts down from  */
+  	/* default value if the device doesn't override */
+	#define SCSI_DEFAULT_DEVICE_BLOCKED	3
+
 
 	// Flag to allow revalidate to succeed in sd_open
 	int allow_revalidate;
diff -Nru a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c
--- a/drivers/scsi/scsi_error.c	Mon Oct  7 11:25:25 2002
+++ b/drivers/scsi/scsi_error.c	Mon Oct  7 11:25:25 2002
@@ -1341,6 +1341,13 @@
 		 * the case of trying to send too many commands to a
 		 * tagged queueing device.
 		 */
+	case BUSY:
+		/*
+		 * device can't talk to us at the moment.  Should only
+		 * occur (SAM-3) when the task queue is empty, so will cause
+		 * the empty queue handling to trigger a stall in the
+		 * device.
+		 */
 		return ADD_TO_MLQUEUE;
 	case GOOD:
 	case COMMAND_TERMINATED:
@@ -1358,8 +1365,6 @@
 		 * who knows?  FIXME(eric)
 		 */
 		return SUCCESS;
-	case BUSY:
-		goto maybe_retry;
 
 	case RESERVATION_CONFLICT:
 		printk("scsi%d (%d,%d,%d) : reservation conflict\n", 
diff -Nru a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c
--- a/drivers/scsi/scsi_lib.c	Mon Oct  7 11:25:35 2002
+++ b/drivers/scsi/scsi_lib.c	Mon Oct  7 11:25:35 2002
@@ -754,6 +754,24 @@
 		if (SHpnt->in_recovery || blk_queue_plugged(q))
 			return;
 
+		if(SHpnt->host_busy == 0 && SHpnt->host_blocked) {
+			/* unblock after host_blocked iterates to zero */
+			if(--SHpnt->host_blocked == 0) {
+				SCSI_LOG_MLQUEUE(3, printk("scsi%d unblocking host at zero depth\n", SHpnt->host_no));
+			} else {
+				blk_plug_device(q);
+				break;
+			}
+		}
+		if(SDpnt->device_busy == 0 && SDpnt->device_blocked) {
+			/* unblock after device_blocked iterates to zero */
+			if(--SDpnt->device_blocked == 0) {
+				SCSI_LOG_MLQUEUE(3, printk("scsi%d (%d:%d) unblocking device at zero depth\n", SHpnt->host_no, SDpnt->id, SDpnt->lun));
+			} else {
+				blk_plug_device(q);
+				break;
+			}
+		}
 		/*
 		 * If the device cannot accept another request, then quit.
 		 */
diff -Nru a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c
--- a/drivers/scsi/scsi_scan.c	Mon Oct  7 11:25:36 2002
+++ b/drivers/scsi/scsi_scan.c	Mon Oct  7 11:25:36 2002
@@ -1461,6 +1461,10 @@
 	if (*bflags & BLIST_SINGLELUN)
 		sdev->single_lun = 1;
 
+	/* if the device needs this changing, it may do so in the detect
+	 * function */
+	sdev->max_device_blocked = SCSI_DEFAULT_DEVICE_BLOCKED;
+
 	for (sdt = scsi_devicelist; sdt; sdt = sdt->next)
 		if (sdt->detect)
 			sdev->attached += (*sdt->detect) (sdev);
diff -Nru a/drivers/scsi/sd.c b/drivers/scsi/sd.c
--- a/drivers/scsi/sd.c	Mon Oct  7 11:25:36 2002
+++ b/drivers/scsi/sd.c	Mon Oct  7 11:25:36 2002
@@ -42,6 +42,7 @@
 #include <linux/errno.h>
 #include <linux/interrupt.h>
 #include <linux/init.h>
+#include <linux/reboot.h>
 #include <linux/vmalloc.h>
 #include <linux/smp.h>
 
@@ -98,6 +99,10 @@
 static int sd_detect(Scsi_Device *);
 static void sd_detach(Scsi_Device *);
 static int sd_init_command(Scsi_Cmnd *);
+static int sd_synchronize_cache(int, int);
+static int sd_notifier(struct notifier_block *, unsigned long, void *);
+
+static struct notifier_block sd_notifier_block = {sd_notifier, NULL, 0}; 
 
 static struct Scsi_Device_Template sd_template = {
 	module:THIS_MODULE,
@@ -574,6 +579,7 @@
 		__MOD_DEC_USE_COUNT(sdp->host->hostt->module);
 	if (sd_template.module)
 		__MOD_DEC_USE_COUNT(sd_template.module);
+
 	return 0;
 }
 
@@ -881,6 +887,74 @@
 }
 
 /*
+ * sd_read_cache_type - called only from sd_init_onedisk()
+ */
+static void
+sd_read_cache_type(Scsi_Disk *sdkp, char *diskname,
+		   Scsi_Request *SRpnt, unsigned char *buffer) {
+
+	unsigned char cmd[10];
+	Scsi_Device *sdp = sdkp->device;
+	int the_result, retries;
+
+	retries = 3;
+	do {
+
+		memset((void *) &cmd[0], 0, 10);
+		cmd[0] = MODE_SENSE;
+		cmd[1] = (sdp->scsi_level <= SCSI_2) ?
+			 ((sdp->lun << 5) & 0xe0) : 0;
+		cmd[1] |= 0x08;	/* DBD */
+		cmd[2] = 0x08;	/* current values, cache page */
+		cmd[4] = 128;	/* allocation length */
+
+
+		memset((void *) buffer, 0, 24);
+		SRpnt->sr_cmd_len = 0;
+		SRpnt->sr_sense_buffer[0] = 0;
+		SRpnt->sr_sense_buffer[2] = 0;
+
+		SRpnt->sr_data_direction = SCSI_DATA_READ;
+		scsi_wait_req(SRpnt, (void *) cmd, (void *) buffer,
+			    24, SD_TIMEOUT, MAX_RETRIES);
+
+		the_result = SRpnt->sr_result;
+		retries--;
+
+	} while (the_result && retries);
+
+	if (the_result) {
+		printk(KERN_ERR "%s : MODE SENSE failed.\n"
+		       "%s : status = %x, message = %02x, host = %d, driver = %02x \n",
+		       diskname, diskname,
+		       status_byte(the_result),
+		       msg_byte(the_result),
+		       host_byte(the_result),
+		       driver_byte(the_result)
+		    );
+		if (driver_byte(the_result) & DRIVER_SENSE)
+			print_req_sense("sd", SRpnt);
+		else
+			printk(KERN_ERR "%s : sense not available. \n", diskname);
+
+		printk(KERN_ERR "%s : assuming drive cache: write through\n", diskname);
+		sdkp->WCE = 0;
+		sdkp->RCD = 0;
+	} else {
+		const char *types[] = { "write through", "none", "write back", "write back, no read (daft)" };
+		int ct = 0;
+		int offset = buffer[3] + 4; /* offset to start of mode page */
+
+		sdkp->WCE = (buffer[offset + 2] & 0x04) == 0x04;
+		sdkp->RCD = (buffer[offset + 2] & 0x01) == 0x01;
+
+		ct =  sdkp->RCD + 2*sdkp->WCE;
+
+		printk(KERN_NOTICE "SCSI device %s: drive cache: %s\n", diskname, types[ct]);
+	}
+}
+
+/*
  * read disk capacity - called only in sd_init_onedisk()
  */
 static void
@@ -1118,6 +1192,7 @@
 	sdkp->write_prot = 0;
 
 	sd_spinup_disk(sdkp, diskname, SRpnt, buffer);
+	sd_read_cache_type(sdkp, diskname, SRpnt, buffer);
 
 	if (sdkp->media_present)
 		sd_read_capacity(sdkp, diskname, SRpnt, buffer);
@@ -1395,9 +1470,6 @@
 	for (dsk_nr = 0; dsk_nr < sd_template.dev_max; dsk_nr++) {
 		sdkp = sd_dsk_arr[dsk_nr];
 		if (sdkp->device == sdp) {
-			sdkp->device = NULL;
-			sdkp->capacity = 0;
-			/* sdkp->detaching = 1; */
 			break;
 		}
 	}
@@ -1405,6 +1477,16 @@
 	if (dsk_nr >= sd_template.dev_max)
 		return;
 
+	/* check that we actually have a write back cache to synchronize */
+	if(sdkp->WCE) {
+		printk(KERN_NOTICE "Synchronizing SCSI cache: ");
+		sd_synchronize_cache(dsk_nr, 1);
+		printk("\n");
+	}
+	sdkp->device = NULL;
+	sdkp->capacity = 0;
+	/* sdkp->detaching = 1; */
+
 	if (sdkp->has_been_registered) {
 		sdkp->has_been_registered = 0;
 		dev = MKDEV_SD(dsk_nr);
@@ -1433,6 +1515,7 @@
 		sd_template.scsi_driverfs_driver.name = (char *)sd_template.tag;
 		sd_template.scsi_driverfs_driver.bus = &scsi_driverfs_bus_type;
 		driver_register(&sd_template.scsi_driverfs_driver);
+		register_reboot_notifier(&sd_notifier_block);
 	}
 	return rc;
 }
@@ -1461,6 +1544,92 @@
 		blk_dev[SD_MAJOR(k)].queue = NULL;
 	sd_template.dev_max = 0;
 	remove_driver(&sd_template.scsi_driverfs_driver);
+
+	unregister_reboot_notifier(&sd_notifier_block);
+}
+
+static int sd_notifier(struct notifier_block *nbt, unsigned long event, void *buf)
+{
+	int i;
+	char *msg = "Synchronizing SCSI caches: ";
+
+	if (!(event == SYS_RESTART || event == SYS_HALT 
+	      || event == SYS_POWER_OFF))
+		return NOTIFY_DONE;
+	for (i = 0; i < sd_template.dev_max; i++) {
+		Scsi_Disk *sdkp = sd_get_sdisk(i);
+
+		if (!sdkp || !sdkp->device)
+			continue;
+		if (sdkp->WCE) {
+			if(msg) {
+				printk(KERN_NOTICE "%s", msg);
+				msg = NULL;
+			}
+			sd_synchronize_cache(i, 1);
+		}
+	}
+	if(!msg)
+		printk("\n");
+
+	return NOTIFY_OK;
+}
+
+/* send a SYNCHRONIZE CACHE instruction down to the device through the
+ * normal SCSI command structure.  Wait for the command to complete (must
+ * have user context) */
+static int sd_synchronize_cache(int index, int verbose)
+{
+	Scsi_Request *SRpnt;
+	Scsi_Disk *sdkp = sd_get_sdisk(index);
+	Scsi_Device *SDpnt = sdkp->device;
+	int retries, the_result;
+
+	if(verbose) {
+		char buf[16];
+
+		sd_dskname(index, buf);
+
+		printk("%s ", buf);
+	}
+
+	SRpnt = scsi_allocate_request(SDpnt);
+	if(!SRpnt) {
+		if(verbose)
+			printk("FAILED\n  No memory for request\n");
+		return 0;
+	}
+		
+
+	for(retries = 3; retries > 0; --retries) {
+		unsigned char cmd[10] = { 0 };
+
+		cmd[0] = SYNCHRONIZE_CACHE;
+		cmd[1] = SDpnt->scsi_level <= SCSI_2 ? (SDpnt->lun << 5) & 0xe0 : 0;
+		/* leave the rest of the command zero to indicate 
+		 * flush everything */
+		scsi_wait_req(SRpnt, (void *)cmd, NULL, 0,
+			      SD_TIMEOUT, MAX_RETRIES);
+
+		if(SRpnt->sr_result == 0)
+			break;
+	}
+
+	the_result = SRpnt->sr_result;
+	scsi_release_request(SRpnt);
+	if(verbose) {
+		if(the_result != 0) {
+			printk("FAILED\n  status = %x, message = %02x, host = %d, driver = %02x\n  ",
+			       status_byte(the_result),
+			       msg_byte(the_result),
+			       host_byte(the_result),
+			       driver_byte(the_result));
+			if (driver_byte(the_result) & DRIVER_SENSE)
+				print_req_sense("sd", SRpnt);
+
+		}
+	}
+	return (the_result == 0);
 }
 
 static Scsi_Disk * sd_get_sdisk(int index)
diff -Nru a/drivers/scsi/sd.h b/drivers/scsi/sd.h
--- a/drivers/scsi/sd.h	Mon Oct  7 11:25:35 2002
+++ b/drivers/scsi/sd.h	Mon Oct  7 11:25:35 2002
@@ -26,6 +26,8 @@
 	unsigned char media_present;
 	unsigned char write_prot;
 	unsigned has_been_registered:1;
+	unsigned WCE:1;         /* state of disk WCE bit */
+	unsigned RCD:1;         /* state of disk RCD bit */
 } Scsi_Disk;
 
 extern int revalidate_scsidisk(kdev_t dev, int maxusage);
diff -Nru a/drivers/scsi/sg.c b/drivers/scsi/sg.c
--- a/drivers/scsi/sg.c	Mon Oct  7 11:25:35 2002
+++ b/drivers/scsi/sg.c	Mon Oct  7 11:25:35 2002
@@ -1354,32 +1354,42 @@
 {
 	static int sg_registered = 0;
 	unsigned long iflags;
+	int tmp_dev_max;
+	Sg_device **tmp_da;
 
 	if ((sg_template.dev_noticed == 0) || sg_dev_arr)
 		return 0;
 
+	SCSI_LOG_TIMEOUT(3, printk("sg_init\n"));
+
+	write_lock_irqsave(&sg_dev_arr_lock, iflags);
+	tmp_dev_max = sg_template.dev_noticed + SG_DEV_ARR_LUMP;
+	write_unlock_irqrestore(&sg_dev_arr_lock, iflags);
+
+	tmp_da = (Sg_device **)vmalloc(
+				tmp_dev_max * sizeof(Sg_device *));
+	if (NULL == tmp_da) {
+		printk(KERN_ERR "sg_init: no space for sg_dev_arr\n");
+		return 1;
+	}
 	write_lock_irqsave(&sg_dev_arr_lock, iflags);
+
 	if (!sg_registered) {
 		if (register_chrdev(SCSI_GENERIC_MAJOR, "sg", &sg_fops)) {
 			printk(KERN_ERR
 			       "Unable to get major %d for generic SCSI device\n",
 			       SCSI_GENERIC_MAJOR);
 			write_unlock_irqrestore(&sg_dev_arr_lock, iflags);
+			vfree((char *) tmp_da);
 			return 1;
 		}
 		sg_registered++;
 	}
 
-	SCSI_LOG_TIMEOUT(3, printk("sg_init\n"));
-	sg_template.dev_max = sg_template.dev_noticed + SG_DEV_ARR_LUMP;
-	sg_dev_arr = (Sg_device **)vmalloc(
-			sg_template.dev_max * sizeof(Sg_device *));
-	if (NULL == sg_dev_arr) {
-		printk(KERN_ERR "sg_init: no space for sg_dev_arr\n");
-		write_unlock_irqrestore(&sg_dev_arr_lock, iflags);
-		return 1;
-	}
-	memset(sg_dev_arr, 0, sg_template.dev_max * sizeof (Sg_device *));
+	memset(tmp_da, 0, tmp_dev_max * sizeof (Sg_device *));
+	sg_template.dev_max = tmp_dev_max;
+	sg_dev_arr = tmp_da;
+
 	write_unlock_irqrestore(&sg_dev_arr_lock, iflags);
 #ifdef CONFIG_PROC_FS
 	sg_proc_init();
@@ -1430,7 +1440,7 @@
 static int
 sg_attach(Scsi_Device * scsidp)
 {
-	Sg_device *sdp;
+	Sg_device *sdp = NULL;
 	unsigned long iflags;
 	int k;
 
@@ -1439,15 +1449,16 @@
 		Sg_device **tmp_da;
 		int tmp_dev_max = sg_template.nr_dev + SG_DEV_ARR_LUMP;
 
+		write_unlock_irqrestore(&sg_dev_arr_lock, iflags);
 		tmp_da = (Sg_device **)vmalloc(
 				tmp_dev_max * sizeof(Sg_device *));
 		if (NULL == tmp_da) {
 			scsidp->attached--;
-			write_unlock_irqrestore(&sg_dev_arr_lock, iflags);
 			printk(KERN_ERR
 			       "sg_attach: device array cannot be resized\n");
 			return 1;
 		}
+		write_lock_irqsave(&sg_dev_arr_lock, iflags);
 		memset(tmp_da, 0, tmp_dev_max * sizeof (Sg_device *));
 		memcpy(tmp_da, sg_dev_arr,
 		       sg_template.dev_max * sizeof (Sg_device *));
@@ -1456,6 +1467,7 @@
 		sg_template.dev_max = tmp_dev_max;
 	}
 
+find_empty_slot:
 	for (k = 0; k < sg_template.dev_max; k++)
 		if (!sg_dev_arr[k])
 			break;
@@ -1467,11 +1479,19 @@
 		       " type=%d, minor number exceed %d\n",
 		       scsidp->host->host_no, scsidp->channel, scsidp->id,
 		       scsidp->lun, scsidp->type, SG_MAX_DEVS_MASK);
+		if (NULL != sdp)
+			vfree((char *) sdp);
 		return 1;
 	}
-	if (k < sg_template.dev_max)
-		sdp = (Sg_device *)vmalloc(sizeof(Sg_device));
-	else
+	if (k < sg_template.dev_max) {
+		if (NULL == sdp) {
+			write_unlock_irqrestore(&sg_dev_arr_lock, iflags);
+			sdp = (Sg_device *)vmalloc(sizeof(Sg_device));
+			write_lock_irqsave(&sg_dev_arr_lock, iflags);
+			if (!sg_dev_arr[k])
+				goto find_empty_slot;
+		}
+	} else
 		sdp = NULL;
 	if (NULL == sdp) {
 		scsidp->attached--;
@@ -1498,17 +1518,18 @@
 		scsidp->sdev_driverfs_dev.name);
 	sdp->sg_driverfs_dev.parent = &scsidp->sdev_driverfs_dev;
 	sdp->sg_driverfs_dev.bus = &scsi_driverfs_bus_type;
+
+	sg_template.nr_dev++;
+	sg_dev_arr[k] = sdp;
+	write_unlock_irqrestore(&sg_dev_arr_lock, iflags);
+
 	device_register(&sdp->sg_driverfs_dev);
 	device_create_file(&sdp->sg_driverfs_dev, &dev_attr_type);
 	device_create_file(&sdp->sg_driverfs_dev, &dev_attr_kdev);
-
 	sdp->de = devfs_register(scsidp->de, "generic", DEVFS_FL_DEFAULT,
 				 SCSI_GENERIC_MAJOR, k,
 				 S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP,
 				 &sg_fops, sdp);
-	sg_template.nr_dev++;
-	sg_dev_arr[k] = sdp;
-	write_unlock_irqrestore(&sg_dev_arr_lock, iflags);
 	switch (scsidp->type) {
 	case TYPE_DISK:
 	case TYPE_MOD:
@@ -1529,7 +1550,7 @@
 static void
 sg_detach(Scsi_Device * scsidp)
 {
-	Sg_device *sdp;
+	Sg_device *sdp = NULL;
 	unsigned long iflags;
 	Sg_fd *sfp;
 	Sg_fd *tsfp;
@@ -1571,22 +1592,11 @@
 				}
 			}
 			SCSI_LOG_TIMEOUT(3, printk("sg_detach: dev=%d, dirty\n", k));
-			devfs_unregister(sdp->de);
-			device_remove_file(&sdp->sg_driverfs_dev,
-					   &dev_attr_type);
-			device_remove_file(&sdp->sg_driverfs_dev,
-					   &dev_attr_kdev);
-			put_device(&sdp->sg_driverfs_dev);
-			sdp->de = NULL;
 			if (NULL == sdp->headfp) {
-				vfree((char *) sdp);
 				sg_dev_arr[k] = NULL;
 			}
 		} else {	/* nothing active, simple case */
 			SCSI_LOG_TIMEOUT(3, printk("sg_detach: dev=%d\n", k));
-			devfs_unregister(sdp->de);
-			put_device(&sdp->sg_driverfs_dev);
-			vfree((char *) sdp);
 			sg_dev_arr[k] = NULL;
 		}
 		scsidp->attached--;
@@ -1595,6 +1605,17 @@
 		break;
 	}
 	write_unlock_irqrestore(&sg_dev_arr_lock, iflags);
+
+	if (sdp) {
+		devfs_unregister(sdp->de);
+		sdp->de = NULL;
+		device_remove_file(&sdp->sg_driverfs_dev, &dev_attr_type);
+		device_remove_file(&sdp->sg_driverfs_dev, &dev_attr_kdev);
+		put_device(&sdp->sg_driverfs_dev);
+		if (NULL == sdp->headfp)
+			vfree((char *) sdp);
+	}
+
 	if (delay)
 		scsi_sleep(2);	/* dirty detach so delay device destruction */
 }
diff -Nru a/drivers/scsi/st.c b/drivers/scsi/st.c
--- a/drivers/scsi/st.c	Mon Oct  7 11:25:36 2002
+++ b/drivers/scsi/st.c	Mon Oct  7 11:25:36 2002
@@ -3832,14 +3832,14 @@
 	    tpnt->driverfs_dev_r[mode].parent = &SDp->sdev_driverfs_dev;
 	    tpnt->driverfs_dev_r[mode].bus = &scsi_driverfs_bus_type;
 	    tpnt->driverfs_dev_r[mode].driver_data =
-			(void *)(long)__mkdev(MAJOR_NR, i + (mode << 5));
+			(void *)(long)__mkdev(MAJOR_NR, dev_num + (mode << 5));
 	    device_register(&tpnt->driverfs_dev_r[mode]);
 	    device_create_file(&tpnt->driverfs_dev_r[mode], 
 			       &dev_attr_type);
 	    device_create_file(&tpnt->driverfs_dev_r[mode], &dev_attr_kdev);
 	    tpnt->de_r[mode] =
 		devfs_register (SDp->de, name, DEVFS_FL_DEFAULT,
-				MAJOR_NR, i + (mode << 5),
+				MAJOR_NR, dev_num + (mode << 5),
 				S_IFCHR | S_IRUGO | S_IWUGO,
 				&st_fops, NULL);
 	    /*  No-rewind entry  */
@@ -3851,7 +3851,7 @@
 	    tpnt->driverfs_dev_n[mode].parent= &SDp->sdev_driverfs_dev;
 	    tpnt->driverfs_dev_n[mode].bus = &scsi_driverfs_bus_type;
 	    tpnt->driverfs_dev_n[mode].driver_data =
-			(void *)(long)__mkdev(MAJOR_NR, i + (mode << 5) + 128);
+			(void *)(long)__mkdev(MAJOR_NR, dev_num + (mode << 5) + 128);
 	    device_register(&tpnt->driverfs_dev_n[mode]);
 	    device_create_file(&tpnt->driverfs_dev_n[mode], 
 			       &dev_attr_type);
@@ -3859,7 +3859,7 @@
 			       &dev_attr_kdev);
 	    tpnt->de_n[mode] =
 		devfs_register (SDp->de, name, DEVFS_FL_DEFAULT,
-				MAJOR_NR, i + (mode << 5) + 128,
+				MAJOR_NR, dev_num + (mode << 5) + 128,
 				S_IFCHR | S_IRUGO | S_IWUGO,
 				&st_fops, NULL);
 	}
diff -Nru a/drivers/scsi/sun3_NCR5380.c b/drivers/scsi/sun3_NCR5380.c
--- a/drivers/scsi/sun3_NCR5380.c	Mon Oct  7 11:25:25 2002
+++ b/drivers/scsi/sun3_NCR5380.c	Mon Oct  7 11:25:25 2002
@@ -642,16 +642,11 @@
  * interrupt or bottom half.
  */
 
-#include <linux/tqueue.h>
+#include <linux/workqueue.h>
 #include <linux/interrupt.h>
 
 static volatile int main_running = 0;
-static struct tq_struct NCR5380_tqueue = {
-//    NULL,		/* next */
-    sync: 0,			/* sync */
-    routine: (void (*)(void*))NCR5380_main,  /* routine, must have (void *) arg... */
-    data: NULL		/* data */
-};
+static DECLARE_WORK(NCR5380_tqueue, (void (*)(void*))NCR5380_main, NULL);
 
 static __inline__ void queue_main(void)
 {
@@ -660,8 +655,7 @@
 	   queue it on the 'immediate' task queue, to be processed
 	   immediately after the current interrupt processing has
 	   finished. */
-	queue_task(&NCR5380_tqueue, &tq_immediate);
-	mark_bh(IMMEDIATE_BH);
+	schedule_work(&NCR5380_tqueue);
     }
     /* else: nothing to do: the running NCR5380_main() will pick up
        any newly queued command. */
diff -Nru a/drivers/scsi/sym53c8xx_2/Makefile b/drivers/scsi/sym53c8xx_2/Makefile
--- a/drivers/scsi/sym53c8xx_2/Makefile	Mon Oct  7 11:25:36 2002
+++ b/drivers/scsi/sym53c8xx_2/Makefile	Mon Oct  7 11:25:36 2002
@@ -3,7 +3,5 @@
 sym53c8xx-objs := sym_fw.o sym_glue.o sym_hipd.o sym_malloc.o sym_misc.o sym_nvram.o
 obj-$(CONFIG_SCSI_SYM53C8XX_2) := sym53c8xx.o
 
-EXTRA_CFLAGS += -I.
-
 include $(TOPDIR)/Rules.make
 
diff -Nru a/drivers/scsi/wd7000.c b/drivers/scsi/wd7000.c
--- a/drivers/scsi/wd7000.c	Mon Oct  7 11:25:39 2002
+++ b/drivers/scsi/wd7000.c	Mon Oct  7 11:25:39 2002
@@ -145,6 +145,19 @@
  * 12/31/2001 - Arnaldo Carvalho de Melo <acme@conectiva.com.br>
  *
  * use host->host_lock, not io_request_lock, cleanups
+ *
+ * 2002/10/04 - Alan Cox <alan@redhat.com>
+ *
+ * Use dev_id for interrupts, kill __FUNCTION__ pasting
+ * Add a lock for the scb pool, clean up all other cli/sti usage stuff
+ * Use the adapter lock for the other places we had the cli's
+ *
+ * 2002/10/06 - Alan Cox <alan@redhat.com>
+ *
+ * Switch to new style error handling
+ * Clean up delay to udelay, and yielding sleeps
+ * Make host reset actually reset the card
+ * Make everything static
  */
 
 #include <linux/module.h>
@@ -254,19 +267,12 @@
 #define NUM_DMAS (sizeof(wd7000_dma)/sizeof(short))
 
 /*
- * possible irq range
+ * The following is set up by wd7000_detect, and used thereafter for
+ * proc and other global ookups
  */
-#define IRQ_MIN   3
-#define IRQ_MAX   15
-#define IRQS      (IRQ_MAX - IRQ_MIN + 1)
-
-/*
- * The following is set up by wd7000_detect, and used thereafter by
- * wd7000_intr_handle to map the irq level to the corresponding Adapter.
- * Note that if SA_INTERRUPT is not used, wd7000_intr_handle must be
- * changed to pick up the IRQ level correctly.
- */
-static struct Scsi_Host *wd7000_host[IRQS];
+ 
+#define UNITS	8
+static struct Scsi_Host *wd7000_host[UNITS];
 
 #define BUS_ON    64	/* x 125ns = 8000ns (BIOS default) */
 #define BUS_OFF   15	/* x 125ns = 1875ns (BIOS default) */
@@ -580,6 +586,7 @@
 static Scb scbs[MAX_SCBS];
 static Scb *scbfree;		/* free list         */
 static int freescbs = MAX_SCBS;	/* free list counter */
+static spinlock_t scbpool_lock; /* guards the scb free list and count */
 
 /*
  *  END of data/declarations - code follows.
@@ -621,16 +628,16 @@
 	(void)get_options(str, ARRAY_SIZE(ints), ints);
 
 	if (wd7000_card_num >= NUM_CONFIGS) {
-		printk(KERN_ERR __FUNCTION__
-			": Too many \"wd7000=\" configurations in "
-			"command line!\n");
+		printk(KERN_ERR
+			"%s: Too many \"wd7000=\" configurations in "
+			"command line!\n", __FUNCTION__);
 		return 0;
 	}
 
 	if ((ints[0] < 3) || (ints[0] > 5)) {
-		printk(KERN_ERR __FUNCTION__ ": Error in command line!  "
+		printk(KERN_ERR "%s: Error in command line!  "
 			"Usage: wd7000=<IRQ>,<DMA>,IO>[,<BUS_ON>"
-			"[,<BUS_OFF>]]\n");
+			"[,<BUS_OFF>]]\n", __FUNCTION__);
 	} else {
 		for (i = 0; i < NUM_IRQS; i++)
 			if (ints[1] == wd7000_irq[i])
@@ -812,14 +819,6 @@
 }
 
 
-static inline void delay (unsigned how_long)
-{
-    register unsigned long time = jiffies + how_long;
-
-    while (time_before(jiffies, time));
-}
-
-
 static inline int command_out (Adapter * host, unchar * cmd, int len)
 {
     if (!WAIT (host->iobase + ASC_STAT, ASC_STATMASK, CMD_RDY, 0)) {
@@ -853,43 +852,44 @@
  */
 static inline Scb *alloc_scbs(struct Scsi_Host *host, int needed)
 {
-    register Scb *scb, *p;
+    register Scb *scb, *p = NULL;
     register unsigned long flags;
     register unsigned long timeout = jiffies + WAITnexttimeout;
     register unsigned long now;
-    static int busy = 0;
     int i;
 
     if (needed <= 0)
 	return (NULL);		/* sanity check */
 
-    save_flags (flags);
-    cli ();
-    while (busy) {		/* someone else is allocating */
-	spin_unlock_irq(host->host_lock);
-	for (now = jiffies; now == jiffies; );	/* wait a jiffy */
-	spin_lock_irq(host->host_lock);
-    }
-    busy = 1;			/* not busy now; it's our turn */
-
+    spin_unlock_irq(host->host_lock);
+	
+retry:
     while (freescbs < needed) {
 	timeout = jiffies + WAITnexttimeout;
 	do {
-	    spin_unlock_irq(host->host_lock);
-	    for (now = jiffies; now == jiffies; );	/* wait a jiffy */
-	    spin_lock_irq(host->host_lock);
+	    /* FIXME: can we actually just yield here ?? */
+	    for (now = jiffies; now == jiffies; )
+	    	cpu_relax();	/* wait a jiffy */
 	} while (freescbs < needed && time_before_eq(jiffies, timeout));
 	/*
 	 *  If we get here with enough free Scbs, we can take them.
 	 *  Otherwise, we timed out and didn't get enough.
 	 */
 	if (freescbs < needed) {
-	    busy = 0;
 	    printk (KERN_ERR "wd7000: can't get enough free SCBs.\n");
-	    restore_flags (flags);
+	    spin_unlock_irq(host->host_lock);
 	    return (NULL);
 	}
     }
+
+    /* Take the lock, then check we didnt get beaten, if so try again */
+    spin_lock_irqsave(&scbpool_lock, flags);
+    if(freescbs < needed)
+    {
+    	spin_unlock_irqrestore(&scbpool_lock, flags);
+    	goto retry;
+    }
+    	
     scb = scbfree;
     freescbs -= needed;
     for (i = 0; i < needed; i++) {
@@ -897,10 +897,10 @@
 	scbfree = p->next;
     }
     p->next = NULL;
-    busy = 0;			/* we're done */
-
-    restore_flags (flags);
+   
+    spin_unlock_irqrestore(&scbpool_lock, flags);
 
+    spin_lock_irq(host->host_lock);
     return (scb);
 }
 
@@ -909,26 +909,25 @@
 {
     register unsigned long flags;
 
-    save_flags (flags);
-    cli ();
+    spin_lock_irqsave(&scbpool_lock, flags);
 
     memset (scb, 0, sizeof (Scb));
     scb->next = scbfree;
     scbfree = scb;
     freescbs++;
 
-    restore_flags (flags);
+    spin_unlock_irqrestore(&scbpool_lock, flags);
 }
 
 
 static inline void init_scbs (void)
 {
     int i;
-    unsigned long flags;
 
-    save_flags (flags);
-    cli ();
+    spin_lock_init(&scbpool_lock);
 
+    /* This is only ever called before the SCB pool is active */
+    
     scbfree = &(scbs[0]);
     memset (scbs, 0, sizeof (scbs));
     for (i = 0; i < MAX_SCBS - 1; i++) {
@@ -937,8 +936,6 @@
     }
     scbs[MAX_SCBS - 1].next = NULL;
     scbs[MAX_SCBS - 1].SCpnt = NULL;
-
-    restore_flags (flags);
 }
 
 
@@ -956,8 +953,7 @@
     dprintk("wd7000_mail_out: 0x%06lx", (long) scbptr);
 
     /* We first look for a free outgoing mailbox */
-    save_flags (flags);
-    cli ();
+    spin_lock_irqsave(host->sh->host_lock, flags);
     ogmb = *next_ogmb;
     for (i = 0; i < OGMB_CNT; i++) {
 	if (ogmbs[ogmb].status == 0) {
@@ -971,8 +967,8 @@
 	else
 	    ogmb = (ogmb + 1) % OGMB_CNT;
     }
-    restore_flags (flags);
-
+    spin_unlock_irqrestore(host->sh->host_lock, flags);
+    
     dprintk(", scb is 0x%06lx", (long) scbptr);
 
     if (i >= OGMB_CNT) {
@@ -999,7 +995,7 @@
 }
 
 
-int make_code (unsigned hosterr, unsigned scsierr)
+static int make_code (unsigned hosterr, unsigned scsierr)
 {
 #ifdef WD7000_DEBUG
     int in_error = hosterr;
@@ -1056,14 +1052,14 @@
 
 #define wd7000_intr_ack(host)   outb (0, host->iobase + ASC_INTR_ACK)
 
-void wd7000_intr_handle (int irq, void *dev_id, struct pt_regs *regs)
+static void wd7000_intr_handle (int irq, void *dev_id, struct pt_regs *regs)
 {
     register int flag, icmb, errstatus, icmb_status;
     register int host_error, scsi_error;
     register Scb *scb;		/* for SCSI commands */
     register IcbAny *icb;	/* for host commands */
     register Scsi_Cmnd *SCpnt;
-    Adapter *host = (Adapter *) wd7000_host[irq - IRQ_MIN]->hostdata;	/* This MUST be set!!! */
+    Adapter *host = (Adapter *)dev_id;
     Mailbox *icmbs = host->mb.icmb;
 
     host->int_counter++;
@@ -1139,7 +1135,7 @@
     dprintk("wd7000_intr_handle: return from interrupt handler\n");
 }
 
-void do_wd7000_intr_handle (int irq, void *dev_id, struct pt_regs *regs)
+static void do_wd7000_intr_handle (int irq, void *dev_id, struct pt_regs *regs)
 {
     unsigned long flags;
     struct Scsi_Host *host = dev_id;
@@ -1150,7 +1146,7 @@
 }
 
 
-int wd7000_queuecommand (Scsi_Cmnd *SCpnt, void (*done) (Scsi_Cmnd *))
+static int wd7000_queuecommand (Scsi_Cmnd *SCpnt, void (*done) (Scsi_Cmnd *))
 {
     register Scb *scb;
     register Sgb *sgb;
@@ -1197,25 +1193,31 @@
 	any2scsi (scb->dataptr, isa_virt_to_bus(SCpnt->request_buffer));
 	any2scsi (scb->maxlen, SCpnt->request_bufflen);
     }
+    
+    /* FIXME: drop lock and yield here ? */
 
-    while (!mail_out (host, scb));	/* keep trying */
+    while (!mail_out (host, scb))
+    	cpu_relax();	/* keep trying */
 
-    return (1);
+    return 0;
 }
 
 
-int wd7000_command (Scsi_Cmnd *SCpnt)
+static int wd7000_command (Scsi_Cmnd *SCpnt)
 {
     wd7000_queuecommand (SCpnt, wd7000_scsi_done);
 
     while (SCpnt->SCp.phase > 0)
-	barrier ();		/* phase counts scbs down to 0 */
+    {
+    	cpu_relax();
+	barrier();		/* phase counts scbs down to 0 */
+    }
 
     return (SCpnt->result);
 }
 
 
-int wd7000_diagnostics (Adapter *host, int code)
+static int wd7000_diagnostics (Adapter *host, int code)
 {
     static IcbDiag icb = {ICB_OP_DIAGNOSTICS};
     static unchar buf[256];
@@ -1233,7 +1235,10 @@
     mail_out (host, (struct scb *) &icb);
     timeout = jiffies + WAITnexttimeout;	/* wait up to 2 seconds */
     while (icb.phase && time_before(jiffies, timeout))
-	barrier ();		/* wait for completion */
+    {
+	cpu_relax();		/* wait for completion */
+	barrier();
+    }
 
     if (icb.phase) {
 	printk ("wd7000_diagnostics: timed out.\n");
@@ -1249,7 +1254,7 @@
 }
 
 
-int wd7000_init (Adapter *host)
+static int wd7000_adapter_reset(Adapter *host)
 {
     InitCmd init_cmd =
     {
@@ -1263,19 +1268,18 @@
 	ICMB_CNT
     };
     int diag;
-
     /*
      *  Reset the adapter - only.  The SCSI bus was initialized at power-up,
      *  and we need to do this just so we control the mailboxes, etc.
      */
     outb (ASC_RES, host->iobase + ASC_CONTROL);
-    delay (1);			/* reset pulse: this is 10ms, only need 25us */
+    udelay(40);			/* reset pulse: this is 40us, only need 25us */
     outb (0, host->iobase + ASC_CONTROL);
     host->control = 0;		/* this must always shadow ASC_CONTROL */
 
     if (WAIT (host->iobase + ASC_STAT, ASC_STATMASK, CMD_RDY, 0)) {
 	printk ("wd7000_init: WAIT timed out.\n");
-	return (0);		/* 0 = not ok */
+	return -1;		/* -1 = not ok */
     }
 
     if ((diag = inb (host->iobase + ASC_INTR_STAT)) != 1) {
@@ -1296,31 +1300,38 @@
 		     break;
 	    default: printk ("diagnostic code 0x%02Xh received.\n", diag);
 	}
-	return (0);
+	return -1;
     }
-
-    /* Clear mailboxes */
+   /* Clear mailboxes */
     memset (&(host->mb), 0, sizeof (host->mb));
 
     /* Execute init command */
     any2scsi ((unchar *) & (init_cmd.mailboxes), (int) &(host->mb));
     if (!command_out (host, (unchar *) &init_cmd, sizeof (init_cmd))) {
-	printk ("wd7000_init: adapter initialization failed.\n");
-	return (0);
+	printk (KERN_ERR "wd7000_adapter_reset: adapter initialization failed.\n");
+	return -1;
     }
 
     if (WAIT (host->iobase + ASC_STAT, ASC_STATMASK, ASC_INIT, 0)) {
-	printk ("wd7000_init: WAIT timed out.\n");
-	return (0);
+	printk ("wd7000_adapter_reset: WAIT timed out.\n");
+	return -1;
     }
+    return 0;
+}
 
-    if (request_irq (host->irq, do_wd7000_intr_handle, SA_INTERRUPT, "wd7000", NULL)) {
+static int wd7000_init (Adapter *host)
+{
+    if(wd7000_adapter_reset(host) == -1)
+    	return 0;
+
+ 
+    if (request_irq (host->irq, do_wd7000_intr_handle, SA_INTERRUPT, "wd7000", host)) {
 	printk ("wd7000_init: can't get IRQ %d.\n", host->irq);
 	return (0);
     }
     if (request_dma (host->dma, "wd7000")) {
 	printk ("wd7000_init: can't get DMA channel %d.\n", host->dma);
-	free_irq (host->irq, NULL);
+	free_irq (host->irq, host);
 	return (0);
     }
     wd7000_enable_dma (host);
@@ -1336,7 +1347,7 @@
 }
 
 
-void wd7000_revision (Adapter *host)
+static void wd7000_revision (Adapter *host)
 {
     static IcbRevLvl icb =
     {ICB_OP_GET_REVISION};
@@ -1350,7 +1361,10 @@
      */
     mail_out (host, (struct scb *) &icb);
     while (icb.phase)
-	barrier ();		/* wait for completion */
+    {
+	cpu_relax();		/* wait for completion */
+	barrier();
+    }
     host->rev1 = icb.primary;
     host->rev2 = icb.secondary;
 }
@@ -1359,27 +1373,19 @@
 #undef SPRINTF
 #define SPRINTF(args...) { if (pos < (buffer + length)) pos += sprintf (pos, ## args); }
 
-int wd7000_set_info (char *buffer, int length, struct Scsi_Host *host)
+static int wd7000_set_info (char *buffer, int length, struct Scsi_Host *host)
 {
-    unsigned long flags;
-
-    save_flags (flags);
-    cli ();
-
     dprintk("Buffer = <%.*s>, length = %d\n", length, buffer, length);
 
     /*
      * Currently this is a no-op
      */
     dprintk("Sorry, this function is currently out of order...\n");
-
-    restore_flags (flags);
-
     return (length);
 }
 
 
-int wd7000_proc_info (char *buffer, char **start, off_t offset, int length, int hostno, int inout)
+static int wd7000_proc_info (char *buffer, char **start, off_t offset, int length, int hostno, int inout)
 {
     struct Scsi_Host *host = NULL;
     Scsi_Device *scd;
@@ -1396,7 +1402,7 @@
     /*
      * Find the specified host board.
      */
-    for (i = 0; i < IRQS; i++)
+    for (i = 0; i < UNITS; i++)
 	if (wd7000_host[i] && (wd7000_host[i]->host_no == hostno)) {
 	    host = wd7000_host[i];
 
@@ -1417,9 +1423,7 @@
 
     adapter = (Adapter *) host->hostdata;
 
-    save_flags (flags);
-    cli ();
-
+    spin_lock_irqsave(host->host_lock, flags);
     SPRINTF ("Host scsi%d: Western Digital WD-7000 (rev %d.%d)\n", hostno, adapter->rev1, adapter->rev2);
     SPRINTF ("  IO base:      0x%x\n", adapter->iobase);
     SPRINTF ("  IRQ:          %d\n", adapter->irq);
@@ -1484,7 +1488,7 @@
 
     SPRINTF ("\n");
 
-    restore_flags (flags);
+    spin_unlock_irqrestore(host->host_lock, flags);
 
     /*
      * Calculate start of next buffer, and return value.
@@ -1510,13 +1514,15 @@
  *  calling scsi_unregister.
  *
  */
-int wd7000_detect (Scsi_Host_Template *tpnt)
+
+static int wd7000_detect (Scsi_Host_Template *tpnt)
 {
     short present = 0, biosaddr_ptr, sig_ptr, i, pass;
     short biosptr[NUM_CONFIGS];
     unsigned iobase;
     Adapter *host = NULL;
     struct Scsi_Host *sh;
+    int unit = 0;
 
     dprintk("wd7000_detect: started\n");
 
@@ -1525,7 +1531,7 @@
 		wd7000_setup(wd7000);     
 #endif
 
-    for (i = 0; i < IRQS; wd7000_host[i++] = NULL) ;
+    for (i = 0; i < UNITS; wd7000_host[i++] = NULL) ;
     for (i = 0; i < NUM_CONFIGS; biosptr[i++] = -1) ;
 
     tpnt->proc_name = "wd7000";
@@ -1579,6 +1585,9 @@
 
 	if (configs[pass].irq < 0)
 	    continue;
+	    
+	if (unit == UNITS)
+	    continue;
 
 	iobase = configs[pass].iobase;
 
@@ -1591,7 +1600,8 @@
 	     * ASC reset...
 	     */
 	    outb (ASC_RES, iobase + ASC_CONTROL);
-	    delay (1);
+	    set_current_state(TASK_UNINTERRUPTIBLE);
+	    schedule_timeout(HZ/100);
 	    outb (0, iobase + ASC_CONTROL);
 
 	    if (WAIT (iobase + ASC_STAT, ASC_STATMASK, CMD_RDY, 0)) {
@@ -1624,7 +1634,8 @@
 		host->int_counter = 0;
 		host->bus_on = configs[pass].bus_on;
 		host->bus_off = configs[pass].bus_off;
-		host->sh = wd7000_host[host->irq - IRQ_MIN] = sh;
+		host->sh = wd7000_host[unit] = sh;
+		unit++;
 
 		dprintk("wd7000_detect: Trying init WD-7000 card at IO "
 			"0x%x, IRQ %d, DMA %d...\n",
@@ -1649,7 +1660,7 @@
 		if (biosaddr_ptr != NUM_ADDRS)
 		    biosptr[pass] = biosaddr_ptr;
 
-		printk ("Western Digital WD-7000 (rev %d.%d) ",
+		printk (KERN_INFO "Western Digital WD-7000 (rev %d.%d) ",
 			host->rev1, host->rev2);
 		printk ("using IO 0x%x, IRQ %d, DMA %d.\n",
 			host->iobase, host->irq, host->dma);
@@ -1679,34 +1690,53 @@
 /*
  *  I have absolutely NO idea how to do an abort with the WD7000...
  */
-int wd7000_abort (Scsi_Cmnd *SCpnt)
+static int wd7000_abort (Scsi_Cmnd *SCpnt)
 {
     Adapter *host = (Adapter *) SCpnt->host->hostdata;
 
     if (inb (host->iobase + ASC_STAT) & INT_IM) {
 	printk ("wd7000_abort: lost interrupt\n");
 	wd7000_intr_handle (host->irq, NULL, NULL);
-
-	return (SCSI_ABORT_SUCCESS);
+	return FAILED;
     }
-
-    return (SCSI_ABORT_SNOOZE);
+    return FAILED;
 }
 
 
 /*
  *  I also have no idea how to do a reset...
  */
-int wd7000_reset (Scsi_Cmnd *SCpnt, unsigned int unused)
+
+static int wd7000_bus_reset (Scsi_Cmnd *SCpnt)
 {
-    return (SCSI_RESET_PUNT);
+    return FAILED;
+}
+
+static int wd7000_device_reset (Scsi_Cmnd *SCpnt)
+{
+    return FAILED;
+}
+
+/*
+ *  Last resort. Reinitialize the board.
+ */
+ 
+static int wd7000_host_reset (Scsi_Cmnd *SCpnt)
+{
+    Adapter *host = (Adapter *) SCpnt->host->hostdata;
+    
+    if(wd7000_adapter_reset(host)<0)
+	    return FAILED;
+    wd7000_enable_intr (host);
+    return SUCCESS;
 }
 
 
 /*
  *  This was borrowed directly from aha1542.c. (Zaga)
  */
-int wd7000_biosparam (Disk *disk, struct block_device *bdev, int *ip)
+
+static int wd7000_biosparam (Disk *disk, struct block_device *bdev, int *ip)
 {
     dprintk("wd7000_biosparam: dev=%s, size=%d, ", bdevname(bdev),
 	    disk->capacity);
@@ -1743,8 +1773,8 @@
 	    ip[2] = info[2];
 
 	    if (info[0] == 255)
-		printk(KERN_INFO __FUNCTION__ ": current partition table is "
-			"using extended translation.\n");
+		printk(KERN_INFO "%s: current partition table is "
+			"using extended translation.\n", __FUNCTION__);
 	}
     }
 
@@ -1754,6 +1784,8 @@
     return (0);
 }
 
+MODULE_AUTHOR("Thomas Wuensche, John Boyd, Miroslav Zagorac");
+MODULE_DESCRIPTION("Driver for the WD7000 series ISA controllers");
 MODULE_LICENSE("GPL");
 
 /* Eventually this will go into an include file, but this will be later */
diff -Nru a/drivers/scsi/wd7000.h b/drivers/scsi/wd7000.h
--- a/drivers/scsi/wd7000.h	Mon Oct  7 11:25:39 2002
+++ b/drivers/scsi/wd7000.h	Mon Oct  7 11:25:39 2002
@@ -13,14 +13,16 @@
 
 #include <linux/types.h>
 
-int wd7000_set_info (char *buffer, int length, struct Scsi_Host *host);
-int wd7000_proc_info (char *buffer, char **start, off_t offset, int length, int hostno, int inout);
-int wd7000_detect (Scsi_Host_Template *);
-int wd7000_command (Scsi_Cmnd *);
-int wd7000_queuecommand (Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
-int wd7000_abort (Scsi_Cmnd *);
-int wd7000_reset (Scsi_Cmnd *, unsigned int);
-int wd7000_biosparam (Disk *, struct block_device *, int *);
+static int wd7000_set_info (char *buffer, int length, struct Scsi_Host *host);
+static int wd7000_proc_info (char *buffer, char **start, off_t offset, int length, int hostno, int inout);
+static int wd7000_detect (Scsi_Host_Template *);
+static int wd7000_command (Scsi_Cmnd *);
+static int wd7000_queuecommand (Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
+static int wd7000_abort (Scsi_Cmnd *);
+static int wd7000_bus_reset (Scsi_Cmnd *);
+static int wd7000_host_reset (Scsi_Cmnd *);
+static int wd7000_device_reset (Scsi_Cmnd *);
+static int wd7000_biosparam (Disk *, struct block_device *, int *);
 
 #ifndef NULL
 #define NULL 0L
@@ -48,7 +50,9 @@
 	command:		wd7000_command,			\
 	queuecommand:		wd7000_queuecommand,		\
 	abort:			wd7000_abort,			\
-	reset:			wd7000_reset,			\
+	eh_bus_reset_handler:	wd7000_bus_reset,		\
+	eh_device_reset_handler:wd7000_device_reset,		\
+	eh_host_reset_handler:	wd7000_host_reset,		\
 	bios_param:		wd7000_biosparam,		\
 	can_queue:		WD7000_Q,			\
 	this_id:		7,				\
diff -Nru a/drivers/serial/21285.c b/drivers/serial/21285.c
--- a/drivers/serial/21285.c	Mon Oct  7 11:25:26 2002
+++ b/drivers/serial/21285.c	Mon Oct  7 11:25:26 2002
@@ -94,7 +94,7 @@
 	status = *CSR_UARTFLG;
 	while (!(status & 0x10) && max_count--) {
 		if (tty->flip.count >= TTY_FLIPBUF_SIZE) {
-			tty->flip.tqueue.routine((void *)tty);
+			tty->flip.work.func((void *)tty);
 			if (tty->flip.count >= TTY_FLIPBUF_SIZE) {
 				printk(KERN_WARNING "TTY_DONT_FLIP set\n");
 				return;
diff -Nru a/drivers/serial/8250.c b/drivers/serial/8250.c
--- a/drivers/serial/8250.c	Mon Oct  7 11:25:25 2002
+++ b/drivers/serial/8250.c	Mon Oct  7 11:25:25 2002
@@ -32,6 +32,7 @@
 #include <linux/sysrq.h>
 #include <linux/serial_reg.h>
 #include <linux/serialP.h>
+#include <linux/serial.h>
 #include <linux/delay.h>
 
 #include <asm/io.h>
@@ -735,7 +736,7 @@
 
 	do {
 		if (unlikely(tty->flip.count >= TTY_FLIPBUF_SIZE)) {
-			tty->flip.tqueue.routine((void *)tty);
+			tty->flip.work.func((void *)tty);
 			if (tty->flip.count >= TTY_FLIPBUF_SIZE)
 				return; // if TTY_DONT_FLIP is set
 		}
@@ -1560,21 +1561,22 @@
 {
 	struct uart_8250_port *up = (struct uart_8250_port *)port;
 	struct resource *res = NULL, *res_rsa = NULL;
-	int ret = -EBUSY;
+	int ret = 0;
 
-	if (up->port.type == PORT_RSA) {
-		ret = serial8250_request_rsa_resource(up, &res_rsa);
-		if (ret)
-			return ret;
-	}
+	if (up->port.flags & UPF_RESOURCES) {
+		if (up->port.type == PORT_RSA) {
+			ret = serial8250_request_rsa_resource(up, &res_rsa);
+			if (ret)
+				return ret;
+		}
 
-	ret = serial8250_request_std_resource(up, &res);
+		ret = serial8250_request_std_resource(up, &res);
+	}
 
 	/*
 	 * If we have a mapbase, then request that as well.
 	 */
-	if (res != NULL && up->port.iotype == SERIAL_IO_MEM &&
-	    up->port.mapbase) {
+	if (ret == 0 && up->port.flags & UPF_IOREMAP) {
 		int size = res->end - res->start + 1;
 
 		up->port.membase = ioremap(up->port.mapbase, size);
@@ -1610,13 +1612,17 @@
 	 * Find the region that we can probe for.  This in turn
 	 * tells us whether we can probe for the type of port.
 	 */
-	ret = serial8250_request_std_resource(up, &res_std);
-	if (ret)
-		return;
+	if (up->port.flags & UPF_RESOURCES) {
+		ret = serial8250_request_std_resource(up, &res_std);
+		if (ret)
+			return;
 
-	ret = serial8250_request_rsa_resource(up, &res_rsa);
-	if (ret)
+		ret = serial8250_request_rsa_resource(up, &res_rsa);
+		if (ret)
+			probeflags &= ~PROBE_RSA;
+	} else {
 		probeflags &= ~PROBE_RSA;
+	}
 
 	if (flags & UART_CONFIG_TYPE)
 		autoconfig(up, probeflags);
@@ -1678,6 +1684,7 @@
 
 static void __init serial8250_isa_init_ports(void)
 {
+	struct uart_8250_port *up;
 	static int first = 1;
 	int i;
 
@@ -1685,13 +1692,18 @@
 		return;
 	first = 0;
 
-	for (i = 0; i < ARRAY_SIZE(old_serial_port); i++) {
-		serial8250_ports[i].port.iobase  = old_serial_port[i].port;
-		serial8250_ports[i].port.irq     = irq_cannonicalize(old_serial_port[i].irq);
-		serial8250_ports[i].port.uartclk = old_serial_port[i].base_baud * 16;
-		serial8250_ports[i].port.flags   = old_serial_port[i].flags;
-		serial8250_ports[i].port.hub6    = old_serial_port[i].hub6;
-		serial8250_ports[i].port.ops     = &serial8250_pops;
+	for (i = 0, up = serial8250_ports; i < ARRAY_SIZE(old_serial_port);
+	     i++, up++) {
+		up->port.iobase   = old_serial_port[i].port;
+		up->port.irq      = irq_cannonicalize(old_serial_port[i].irq);
+		up->port.uartclk  = old_serial_port[i].baud_base * 16;
+		up->port.flags    = old_serial_port[i].flags |
+				    UPF_RESOURCES;
+		up->port.hub6     = old_serial_port[i].hub6;
+		up->port.membase  = old_serial_port[i].iomem_base;
+		up->port.iotype   = old_serial_port[i].io_type;
+		up->port.regshift = old_serial_port[i].iomem_reg_shift;
+		up->port.ops      = &serial8250_pops;
 	}
 }
 
diff -Nru a/drivers/serial/8250.h b/drivers/serial/8250.h
--- a/drivers/serial/8250.h	Mon Oct  7 11:25:25 2002
+++ b/drivers/serial/8250.h	Mon Oct  7 11:25:25 2002
@@ -30,11 +30,14 @@
 
 struct old_serial_port {
 	unsigned int uart;
-	unsigned int base_baud;
+	unsigned int baud_base;
 	unsigned int port;
 	unsigned int irq;
 	unsigned int flags;
 	unsigned char hub6;
+	unsigned char io_type;
+	unsigned char *iomem_base;
+	unsigned short iomem_reg_shift;
 };
 
 #undef SERIAL_DEBUG_PCI
diff -Nru a/drivers/serial/8250_cs.c b/drivers/serial/8250_cs.c
--- a/drivers/serial/8250_cs.c	Mon Oct  7 11:25:35 2002
+++ b/drivers/serial/8250_cs.c	Mon Oct  7 11:25:35 2002
@@ -42,7 +42,7 @@
 #include <linux/tty.h>
 #include <linux/serial.h>
 #include <linux/major.h>
-#include <linux/tqueue.h>
+#include <linux/workqueue.h>
 #include <asm/io.h>
 #include <asm/system.h>
 
@@ -107,7 +107,7 @@
 	int			manfid;
 	dev_node_t		node[4];
 	int			line[4];
-	struct tq_struct	remove;
+	struct work_struct	remove;
 } serial_info_t;
 
 static void serial_config(dev_link_t * link);
@@ -182,7 +182,7 @@
 	 */
 
 	if (link->state & DEV_CONFIG)
-		schedule_task(&info->remove);
+		schedule_work(&info->remove);
 }
 
 /*======================================================================
@@ -210,7 +210,7 @@
 	link = &info->link;
 	link->priv = info;
 
-	INIT_TQUEUE(&info->remove, do_serial_release, info);
+	INIT_WORK(&info->remove, do_serial_release, info);
 
 	link->io.Attributes1 = IO_DATA_PATH_WIDTH_8;
 	link->io.NumPorts1 = 8;
@@ -278,7 +278,7 @@
 	/*
 	 * Ensure any outstanding scheduled tasks are completed.
 	 */
-	flush_scheduled_tasks();
+	flush_scheduled_work();
 
 	/*
 	 * Ensure that the ports have been released.
diff -Nru a/drivers/serial/amba.c b/drivers/serial/amba.c
--- a/drivers/serial/amba.c	Mon Oct  7 11:25:26 2002
+++ b/drivers/serial/amba.c	Mon Oct  7 11:25:26 2002
@@ -152,7 +152,7 @@
 	status = UART_GET_FR(port);
 	while (UART_RX_DATA(status) && max_count--) {
 		if (tty->flip.count >= TTY_FLIPBUF_SIZE) {
-			tty->flip.tqueue.routine((void *)tty);
+			tty->flip.work.func((void *)tty);
 			if (tty->flip.count >= TTY_FLIPBUF_SIZE) {
 				printk(KERN_WARNING "TTY_DONT_FLIP set\n");
 				return;
diff -Nru a/drivers/serial/sunsab.c b/drivers/serial/sunsab.c
--- a/drivers/serial/sunsab.c	Mon Oct  7 11:25:32 2002
+++ b/drivers/serial/sunsab.c	Mon Oct  7 11:25:32 2002
@@ -138,7 +138,7 @@
 		unsigned char ch = buf[i];
 
 		if (unlikely(tty->flip.count >= TTY_FLIPBUF_SIZE)) {
-			tty->flip.tqueue.routine((void *)tty);
+			tty->flip.work.func((void *)tty);
 			if (tty->flip.count >= TTY_FLIPBUF_SIZE)
 				return; // if TTY_DONT_FLIP is set
 		}
diff -Nru a/drivers/serial/sunsu.c b/drivers/serial/sunsu.c
--- a/drivers/serial/sunsu.c	Mon Oct  7 11:25:26 2002
+++ b/drivers/serial/sunsu.c	Mon Oct  7 11:25:26 2002
@@ -323,7 +323,7 @@
 
 	do {
 		if (unlikely(tty->flip.count >= TTY_FLIPBUF_SIZE)) {
-			tty->flip.tqueue.routine((void *)tty);
+			tty->flip.work.func((void *)tty);
 			if (tty->flip.count >= TTY_FLIPBUF_SIZE)
 				return; // if TTY_DONT_FLIP is set
 		}
diff -Nru a/drivers/serial/sunzilog.c b/drivers/serial/sunzilog.c
--- a/drivers/serial/sunzilog.c	Mon Oct  7 11:25:39 2002
+++ b/drivers/serial/sunzilog.c	Mon Oct  7 11:25:39 2002
@@ -338,7 +338,7 @@
 		unsigned char ch, r1;
 
 		if (unlikely(tty->flip.count >= TTY_FLIPBUF_SIZE)) {
-			tty->flip.tqueue.routine((void *)tty);
+			tty->flip.work.func((void *)tty);
 			if (tty->flip.count >= TTY_FLIPBUF_SIZE)
 				return;
 		}
diff -Nru a/drivers/telephony/ixj.c b/drivers/telephony/ixj.c
--- a/drivers/telephony/ixj.c	Mon Oct  7 11:25:25 2002
+++ b/drivers/telephony/ixj.c	Mon Oct  7 11:25:25 2002
@@ -274,8 +274,8 @@
 
 #include "ixj.h"
 
-#define TYPE(dev) (MINOR(dev) >> 4)
-#define NUM(dev) (MINOR(dev) & 0xf)
+#define TYPE(dev) (minor(dev) >> 4)
+#define NUM(dev) (minor(dev) & 0xf)
 
 static int ixjdebug;
 static int hertz = HZ;
@@ -386,7 +386,7 @@
 #ifdef PERFMON_STATS
 #define ixj_perfmon(x)	((x)++)
 #else
-#define ixj_perfmon(x)	do {} while(0);
+#define ixj_perfmon(x)	do { } while(0)
 #endif
 
 static int ixj_convert_loaded;
@@ -2806,7 +2806,10 @@
 	};
 
 	while (len--)
-		*buff++ = table_ulaw2alaw[*(unsigned char *)buff];
+	{
+		*buff = table_ulaw2alaw[*(unsigned char *)buff];
+		buff++;
+	}
 }
 
 static void alaw2ulaw(unsigned char *buff, unsigned long len)
@@ -2848,7 +2851,10 @@
 	};
 
         while (len--)
-                *buff++ = table_alaw2ulaw[*(unsigned char *)buff];
+        {
+                *buff = table_alaw2ulaw[*(unsigned char *)buff];
+                buff++;
+	}
 }
 
 static ssize_t ixj_read(struct file * file_p, char *buf, size_t length, loff_t * ppos)
@@ -5943,7 +5949,7 @@
 	lcp = kmalloc(sizeof(IXJ_CADENCE), GFP_KERNEL);
 	if (lcp == NULL)
 		return -ENOMEM;
-	if (copy_from_user(lcp, (char *) cp, sizeof(IXJ_CADENCE)) || (unsigned)lcp->elements_used >= ~0U/sizeof(IXJ_CADENCE) )
+	if (copy_from_user(lcp, (char *) cp, sizeof(IXJ_CADENCE)) || (unsigned)lcp->elements_used >= ~0U/sizeof(IXJ_CADENCE_ELEMENT) )
         {
                 kfree(lcp);
                 return -EFAULT;
@@ -6202,7 +6208,7 @@
 	IXJ_FILTER_RAW jfr;
 
 	unsigned int raise, mant;
-	unsigned int minor = MINOR(inode->i_rdev);
+	unsigned int minor = minor(inode->i_rdev);
 	int board = NUM(inode->i_rdev);
 
 	IXJ *j = get_ixj(NUM(inode->i_rdev));
diff -Nru a/drivers/telephony/ixj.h b/drivers/telephony/ixj.h
--- a/drivers/telephony/ixj.h	Mon Oct  7 11:25:39 2002
+++ b/drivers/telephony/ixj.h	Mon Oct  7 11:25:39 2002
@@ -1199,7 +1199,7 @@
 	unsigned char cid_play_flag;
 	char play_mode;
 	IXJ_FLAGS flags;
-	unsigned int busyflags;
+	unsigned long busyflags;
 	unsigned int rec_frame_size;
 	unsigned int play_frame_size;
 	unsigned int cid_play_frame_size;
diff -Nru a/drivers/telephony/ixj_pcmcia.c b/drivers/telephony/ixj_pcmcia.c
--- a/drivers/telephony/ixj_pcmcia.c	Mon Oct  7 11:25:38 2002
+++ b/drivers/telephony/ixj_pcmcia.c	Mon Oct  7 11:25:38 2002
@@ -98,7 +98,6 @@
 static void ixj_detach(dev_link_t * link)
 {
 	dev_link_t **linkp;
-	long flags;
 	int ret;
 	DEBUG(0, "ixj_detach(0x%p)\n", link);
 	for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next)
@@ -106,13 +105,8 @@
 			break;
 	if (*linkp == NULL)
 		return;
-	save_flags(flags);
-	cli();
-	if (link->state & DEV_RELEASE_PENDING) {
-		del_timer(&link->release);
-		link->state &= ~DEV_RELEASE_PENDING;
-	}
-	restore_flags(flags);
+	del_timer_sync(&link->release);
+	link->state &= ~DEV_RELEASE_PENDING;
 	if (link->state & DEV_CONFIG)
 		ixj_cs_release((u_long) link);
 	if (link->handle) {
diff -Nru a/drivers/usb/Makefile b/drivers/usb/Makefile
--- a/drivers/usb/Makefile	Mon Oct  7 11:25:39 2002
+++ b/drivers/usb/Makefile	Mon Oct  7 11:25:39 2002
@@ -60,6 +60,7 @@
 obj-$(CONFIG_USB_LCD)		+= misc/
 obj-$(CONFIG_USB_RIO500)	+= misc/
 obj-$(CONFIG_USB_SPEEDTOUCH)	+= misc/
+obj-$(CONFIG_USB_TEST)		+= misc/
 obj-$(CONFIG_USB_TIGL)		+= misc/
 obj-$(CONFIG_USB_USS720)	+= misc/
 
diff -Nru a/drivers/usb/class/bluetty.c b/drivers/usb/class/bluetty.c
--- a/drivers/usb/class/bluetty.c	Mon Oct  7 11:25:26 2002
+++ b/drivers/usb/class/bluetty.c	Mon Oct  7 11:25:26 2002
@@ -195,7 +195,7 @@
 
 	wait_queue_head_t	write_wait;
 
-	struct tq_struct	tqueue;		/* task queue for line discipline waking up */
+	struct work_struct			work;	/* work queue entry for line discipline waking up */
 	
 	unsigned int		int_packet_pos;
 	unsigned char		int_buffer[EVENT_BUFFER_SIZE];
@@ -1006,7 +1006,7 @@
 	}
 
 	/* wake up our little function to let the tty layer know that something happened */
-	schedule_task(&bluetooth->tqueue);
+	schedule_work(&bluetooth->work);
 }
 
 
@@ -1112,8 +1112,7 @@
 	bluetooth->magic = USB_BLUETOOTH_MAGIC;
 	bluetooth->dev = dev;
 	bluetooth->minor = minor;
-	bluetooth->tqueue.routine = bluetooth_softint;
-	bluetooth->tqueue.data = bluetooth;
+	INIT_WORK(&bluetooth->work, bluetooth_softint, bluetooth);
 	init_MUTEX(&bluetooth->lock);
 
 	/* record the interface number for the control out */
diff -Nru a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c
--- a/drivers/usb/class/cdc-acm.c	Mon Oct  7 11:25:36 2002
+++ b/drivers/usb/class/cdc-acm.c	Mon Oct  7 11:25:36 2002
@@ -143,7 +143,7 @@
 	struct tty_struct *tty;				/* the coresponding tty */
 	struct urb *ctrlurb, *readurb, *writeurb;	/* urbs */
 	struct acm_line line;				/* line coding (bits, stop, parity) */
-	struct tq_struct tqueue;			/* task queue for line discipline waking up */
+	struct work_struct work;					/* work queue entry for line discipline waking up */
 	unsigned int ctrlin;				/* input control lines (DCD, DSR, RI, break, overruns) */
 	unsigned int ctrlout;				/* output control lines (DTR, RTS) */
 	unsigned int writesize;				/* max packet size for the output bulk endpoint */
@@ -272,7 +272,7 @@
 	if (urb->status)
 		dbg("nonzero write bulk status received: %d", urb->status);
 
-	schedule_task(&acm->tqueue);
+	schedule_work(&acm->work);
 }
 
 static void acm_softint(void *private)
@@ -578,8 +578,7 @@
 		acm->minor = minor;
 		acm->dev = dev;
 
-		acm->tqueue.routine = acm_softint;
-		acm->tqueue.data = acm;
+		INIT_WORK(&acm->work, acm_softint, acm);
 
 		if (!(buf = kmalloc(ctrlsize + readsize + acm->writesize, GFP_KERNEL))) {
 			err("out of memory");
diff -Nru a/drivers/usb/core/buffer.c b/drivers/usb/core/buffer.c
--- a/drivers/usb/core/buffer.c	Mon Oct  7 11:25:33 2002
+++ b/drivers/usb/core/buffer.c	Mon Oct  7 11:25:33 2002
@@ -42,6 +42,7 @@
 /**
  * hcd_buffer_create - initialize buffer pools
  * @hcd: the bus whose buffer pools are to be initialized
+ * Context: !in_interrupt()
  *
  * Call this as part of initializing a host controller that uses the pci dma
  * memory allocators.  It initializes some pools of dma-consistent memory that
@@ -60,7 +61,7 @@
 			continue;
 		snprintf (name, sizeof name, "buffer-%d", size);
 		hcd->pool [i] = pci_pool_create (name, hcd->pdev,
-				size, size, 0, SLAB_KERNEL);
+				size, size, 0);
 		if (!hcd->pool [i]) {
 			hcd_buffer_destroy (hcd);
 			return -ENOMEM;
@@ -74,6 +75,7 @@
 /**
  * hcd_buffer_destroy - deallocate buffer pools
  * @hcd: the bus whose buffer pools are to be destroyed
+ * Context: !in_interrupt()
  *
  * This frees the buffer pools created by hcd_buffer_create().
  */
diff -Nru a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
--- a/drivers/usb/core/hub.c	Mon Oct  7 11:25:35 2002
+++ b/drivers/usb/core/hub.c	Mon Oct  7 11:25:35 2002
@@ -240,7 +240,7 @@
 	/* tell keventd to clear state for this TT */
 	spin_lock_irqsave (&tt->lock, flags);
 	list_add_tail (&clear->clear_list, &tt->clear_list);
-	schedule_task (&tt->kevent);
+	schedule_work (&tt->kevent);
 	spin_unlock_irqrestore (&tt->lock, flags);
 }
 
@@ -337,7 +337,7 @@
 
 	spin_lock_init (&hub->tt.lock);
 	INIT_LIST_HEAD (&hub->tt.clear_list);
-	INIT_TQUEUE (&hub->tt.kevent, hub_tt_kevent, hub);
+	INIT_WORK (&hub->tt.kevent, hub_tt_kevent, hub);
 	switch (dev->descriptor.bDeviceProtocol) {
 		case 0:
 			break;
@@ -452,7 +452,7 @@
 
 	/* assuming we used keventd, it must quiesce too */
 	if (hub->tt.hub)
-		flush_scheduled_tasks ();
+		flush_scheduled_work ();
 
 	if (hub->urb) {
 		usb_unlink_urb(hub->urb);
diff -Nru a/drivers/usb/core/hub.h b/drivers/usb/core/hub.h
--- a/drivers/usb/core/hub.h	Mon Oct  7 11:25:26 2002
+++ b/drivers/usb/core/hub.h	Mon Oct  7 11:25:26 2002
@@ -9,7 +9,7 @@
  */
 
 #include <linux/list.h>
-#include <linux/tqueue.h>
+#include <linux/workqueue.h>
 #include <linux/compiler.h>	/* likely()/unlikely() */
 
 /*
@@ -158,7 +158,7 @@
 	/* for control/bulk error recovery (CLEAR_TT_BUFFER) */
 	spinlock_t		lock;
 	struct list_head	clear_list;	/* of usb_tt_clear */
-	struct tq_struct	kevent;
+	struct work_struct			kevent;
 };
 
 struct usb_tt_clear {
diff -Nru a/drivers/usb/core/message.c b/drivers/usb/core/message.c
--- a/drivers/usb/core/message.c	Mon Oct  7 11:25:27 2002
+++ b/drivers/usb/core/message.c	Mon Oct  7 11:25:27 2002
@@ -924,7 +924,7 @@
 int usb_string(struct usb_device *dev, int index, char *buf, size_t size)
 {
 	unsigned char *tbuf;
-	int err;
+	int err, len;
 	unsigned int u, idx;
 
 	if (size <= 0 || !buf || !index)
@@ -954,10 +954,15 @@
 	}
 
 	/*
-	 * Just ask for a maximum length string and then take the length
-	 * that was returned.
+	 * ask for the length of the string 
 	 */
-	err = usb_get_string(dev, dev->string_langid, index, tbuf, 255);
+
+	err = usb_get_string(dev, dev->string_langid, index, tbuf, 2);
+	if(err<2)
+		goto errout;
+	len=tbuf[0];	
+	
+	err = usb_get_string(dev, dev->string_langid, index, tbuf, len);
 	if (err < 0)
 		goto errout;
 
diff -Nru a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c
--- a/drivers/usb/core/usb.c	Mon Oct  7 11:25:35 2002
+++ b/drivers/usb/core/usb.c	Mon Oct  7 11:25:35 2002
@@ -624,7 +624,7 @@
 #else
 
 static int usb_hotplug (struct device *dev, char **envp,
-			char *buffer, int buffer_size)
+			int num_envp, char *buffer, int buffer_size)
 {
 	return -ENODEV;
 }
diff -Nru a/drivers/usb/host/ehci-mem.c b/drivers/usb/host/ehci-mem.c
--- a/drivers/usb/host/ehci-mem.c	Mon Oct  7 11:25:39 2002
+++ b/drivers/usb/host/ehci-mem.c	Mon Oct  7 11:25:39 2002
@@ -166,8 +166,7 @@
 	ehci->qtd_pool = pci_pool_create ("ehci_qtd", ehci->hcd.pdev,
 			sizeof (struct ehci_qtd),
 			32 /* byte alignment (for hw parts) */,
-			4096 /* can't cross 4K */,
-			flags);
+			4096 /* can't cross 4K */);
 	if (!ehci->qtd_pool) {
 		dbg ("no qtd pool");
 		ehci_mem_cleanup (ehci);
@@ -178,8 +177,7 @@
 	ehci->qh_pool = pci_pool_create ("ehci_qh", ehci->hcd.pdev,
 			sizeof (struct ehci_qh),
 			32 /* byte alignment (for hw parts) */,
-			4096 /* can't cross 4K */,
-			flags);
+			4096 /* can't cross 4K */);
 	if (!ehci->qh_pool) {
 		dbg ("no qh pool");
 		ehci_mem_cleanup (ehci);
@@ -190,8 +188,7 @@
 	ehci->itd_pool = pci_pool_create ("ehci_itd", ehci->hcd.pdev,
 			sizeof (struct ehci_itd),
 			32 /* byte alignment (for hw parts) */,
-			4096 /* can't cross 4K */,
-			flags);
+			4096 /* can't cross 4K */);
 	if (!ehci->itd_pool) {
 		dbg ("no itd pool");
 		ehci_mem_cleanup (ehci);
@@ -202,8 +199,7 @@
 	ehci->sitd_pool = pci_pool_create ("ehci_sitd", ehci->hcd.pdev,
 			sizeof (struct ehci_sitd),
 			32 /* byte alignment (for hw parts) */,
-			4096 /* can't cross 4K */,
-			flags);
+			4096 /* can't cross 4K */);
 	if (!ehci->sitd_pool) {
 		dbg ("no sitd pool");
 		ehci_mem_cleanup (ehci);
diff -Nru a/drivers/usb/host/ohci-mem.c b/drivers/usb/host/ohci-mem.c
--- a/drivers/usb/host/ohci-mem.c	Mon Oct  7 11:25:36 2002
+++ b/drivers/usb/host/ohci-mem.c	Mon Oct  7 11:25:36 2002
@@ -135,15 +135,13 @@
 	ohci->td_cache = pci_pool_create ("ohci_td", ohci->hcd.pdev,
 		sizeof (struct td),
 		32 /* byte alignment */,
-		0 /* no page-crossing issues */,
-		GFP_KERNEL);
+		0 /* no page-crossing issues */);
 	if (!ohci->td_cache)
 		return -ENOMEM;
 	ohci->ed_cache = pci_pool_create ("ohci_ed", ohci->hcd.pdev,
 		sizeof (struct ed),
 		16 /* byte alignment */,
-		0 /* no page-crossing issues */,
-		GFP_KERNEL);
+		0 /* no page-crossing issues */);
 	if (!ohci->ed_cache) {
 		pci_pool_destroy (ohci->td_cache);
 		return -ENOMEM;
@@ -177,6 +175,13 @@
 		if (!hash_add_td (hc, td, mem_flags)) {
 			pci_pool_free (hc->td_cache, td, dma);
 			return NULL;
+		}
+		// DEBUG ONLY want to see if these tds are really getting
+		// allocated.  the last one in a page shouldn't be getting
+		// allocated during these tests!
+		if ((dma & 0x0fff) == 0x0fc0) {
+			dbg ("td = %p", td);
+			dump_stack ();
 		}
 	}
 	return td;
diff -Nru a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c
--- a/drivers/usb/host/uhci-hcd.c	Mon Oct  7 11:25:38 2002
+++ b/drivers/usb/host/uhci-hcd.c	Mon Oct  7 11:25:38 2002
@@ -2149,14 +2149,14 @@
 	uhci->fl->dma_handle = dma_handle;
 
 	uhci->td_pool = pci_pool_create("uhci_td", hcd->pdev,
-		sizeof(struct uhci_td), 16, 0, GFP_ATOMIC);
+		sizeof(struct uhci_td), 16, 0);
 	if (!uhci->td_pool) {
 		err("unable to create td pci_pool");
 		goto err_create_td_pool;
 	}
 
 	uhci->qh_pool = pci_pool_create("uhci_qh", hcd->pdev,
-		sizeof(struct uhci_qh), 16, 0, GFP_ATOMIC);
+		sizeof(struct uhci_qh), 16, 0);
 	if (!uhci->qh_pool) {
 		err("unable to create qh pci_pool");
 		goto err_create_qh_pool;
diff -Nru a/drivers/usb/input/usbkbd.c b/drivers/usb/input/usbkbd.c
--- a/drivers/usb/input/usbkbd.c	Mon Oct  7 11:25:33 2002
+++ b/drivers/usb/input/usbkbd.c	Mon Oct  7 11:25:33 2002
@@ -280,7 +280,7 @@
 
 	if (!(buf = kmalloc(63, GFP_KERNEL))) {
 		usb_free_urb(kbd->irq);
-		usb_kbd_free_buffers(dev, kbd);
+		usb_kbd_free_mem(dev, kbd);
 		kfree(kbd);
 		return -ENOMEM;
 	}
@@ -321,7 +321,7 @@
 	if (kbd) {
 		usb_unlink_urb(kbd->irq);
 		input_unregister_device(&kbd->dev);
-		usb_kbd_free_buffers(interface_to_usbdev(intf), kbd);
+		usb_kbd_free_mem(interface_to_usbdev(intf), kbd);
 		kfree(kbd);
 	}
 }
diff -Nru a/drivers/usb/misc/Config.help b/drivers/usb/misc/Config.help
--- a/drivers/usb/misc/Config.help	Mon Oct  7 11:25:35 2002
+++ b/drivers/usb/misc/Config.help	Mon Oct  7 11:25:35 2002
@@ -112,6 +112,12 @@
   For more information, see Johan Verrept's webpages at
   <http://linux-usb.sourceforge.net/SpeedTouch/>.
 
+CONFIG_USB_TEST
+
+  This driver is for testing host controller software.  It is used
+  with specialized device firmware for regression and stress testing,
+  to help prevent problems from cropping up with 'real" drivers.
+
 CONFIG_USB_LCD
   Say Y here if you want to connect an USBLCD to your computer's
   USB port. The USBLCD is a small USB interface board for
diff -Nru a/drivers/usb/misc/Config.in b/drivers/usb/misc/Config.in
--- a/drivers/usb/misc/Config.in	Mon Oct  7 11:25:36 2002
+++ b/drivers/usb/misc/Config.in	Mon Oct  7 11:25:36 2002
@@ -9,3 +9,4 @@
 dep_tristate '  Tieman Voyager USB Braille display support (EXPERIMENTAL)' CONFIG_USB_BRLVGER $CONFIG_USB $CONFIG_EXPERIMENTAL
 dep_tristate '  USB LCD driver support' CONFIG_USB_LCD $CONFIG_USB
 dep_tristate '  Alcatel Speedtouch ADSL USB Modem' CONFIG_USB_SPEEDTOUCH $CONFIG_USB $CONFIG_ATM
+dep_tristate '  USB testing driver (DEVELOPMENT)' CONFIG_USB_TEST $CONFIG_USB_DEVICEFS $CONFIG_EXPERIMENTAL
diff -Nru a/drivers/usb/misc/Makefile b/drivers/usb/misc/Makefile
--- a/drivers/usb/misc/Makefile	Mon Oct  7 11:25:35 2002
+++ b/drivers/usb/misc/Makefile	Mon Oct  7 11:25:35 2002
@@ -11,6 +11,7 @@
 obj-$(CONFIG_USB_LCD)		+= usblcd.o
 obj-$(CONFIG_USB_RIO500)	+= rio500.o
 obj-$(CONFIG_USB_SPEEDTOUCH)	+= speedtouch.o atmsar.o
+obj-$(CONFIG_USB_TEST)		+= usbtest.o
 obj-$(CONFIG_USB_TIGL)		+= tiglusb.o
 obj-$(CONFIG_USB_USS720)	+= uss720.o
 
diff -Nru a/drivers/usb/misc/speedtouch.c b/drivers/usb/misc/speedtouch.c
--- a/drivers/usb/misc/speedtouch.c	Mon Oct  7 11:25:40 2002
+++ b/drivers/usb/misc/speedtouch.c	Mon Oct  7 11:25:40 2002
@@ -163,11 +163,11 @@
 void udsl_atm_processqueue (struct udsl_instance_data *instance);
 
 static struct atmdev_ops udsl_atm_devops = {
-	open:udsl_atm_open,
-	close:udsl_atm_close,
-	ioctl:udsl_atm_ioctl,
-	send:udsl_atm_send,
-	proc_read:udsl_atm_proc_read,
+	.open =		udsl_atm_open,
+	.close =	udsl_atm_close,
+	.ioctl =	udsl_atm_ioctl,
+	.send =		udsl_atm_send,
+	.proc_read =	udsl_atm_proc_read,
 };
 
 struct udsl_atm_dev_data {
@@ -182,7 +182,7 @@
 static void udsl_usb_disconnect (struct usb_interface *intf);
 int udsl_usb_send_data (struct udsl_instance_data *instance, struct atm_vcc *vcc,
 			struct sk_buff *skb);
-static int udsl_usb_ioctl (struct usb_device *hub, unsigned int code, void *user_data);
+static int udsl_usb_ioctl (struct usb_interface *intf, unsigned int code, void *user_data);
 static int udsl_usb_cancelsends (struct udsl_instance_data *instance, struct atm_vcc *vcc);
 
 static struct usb_driver udsl_usb_driver = {
@@ -886,8 +886,9 @@
 #define hex2int(c) ( (c >= '0')&&(c <= '9') ?  (c - '0') : ((c & 0xf)+9) )
 
 
-static int udsl_usb_ioctl (struct usb_device *dev, unsigned int code, void *user_data)
+static int udsl_usb_ioctl (struct usb_interface *intf, unsigned int code, void *user_data)
 {
+	struct usb_device *dev = interface_to_usbdev (intf);
 	struct udsl_instance_data *instance;
 	int i;
 
diff -Nru a/drivers/usb/misc/usbtest.c b/drivers/usb/misc/usbtest.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/usb/misc/usbtest.c	Mon Oct  7 11:26:06 2002
@@ -0,0 +1,570 @@
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+//#include <linux/time.h>
+#include <asm/scatterlist.h>
+
+#if !defined (DEBUG) && defined (CONFIG_USB_DEBUG)
+#   define DEBUG
+#endif
+#include <linux/usb.h>
+
+
+/*-------------------------------------------------------------------------*/
+
+// FIXME make these public somewhere; usbdevfs.h?
+//
+struct usbtest_param {
+	// inputs
+	unsigned		test_num;	/* 0..(TEST_CASES-1) */
+	int			iterations;
+	int			length;
+	int			vary;
+	int			sglen;
+
+	// outputs
+	struct timeval		duration;
+};
+#define USBTEST_REQUEST	_IOWR('U', 100, struct usbtest_param)
+
+/*-------------------------------------------------------------------------*/
+
+/* this is accessed only through usbfs ioctl calls.
+ * one ioctl to issue a test ... no locking needed!!!
+ * tests create other threads if they need them.
+ * urbs and buffers are allocated dynamically,
+ * and data generated deterministically.
+ *
+ * there's a minor complication on disconnect(), since
+ * usbfs.disconnect() waits till our ioctl completes.
+ */
+struct usbtest_dev {
+	struct usb_interface	*intf;
+	struct testdev_info	*info;
+	char			id [32];
+	int			in_pipe;
+	int			out_pipe;
+};
+
+static struct usb_device *testdev_to_usbdev (struct usbtest_dev *test)
+{
+	return interface_to_usbdev (test->intf);
+}
+
+/* set up all urbs so they can be used with either bulk or interrupt */
+#define	INTERRUPT_RATE		1	/* msec/transfer */
+
+/*-------------------------------------------------------------------------*/
+
+/* Support for testing basic non-queued I/O streams.
+ *
+ * These just package urbs as requests that can be easily canceled.
+ * Each urb's data buffer is dynamically allocated; callers can fill
+ * them with non-zero test data (or test for it) when appropriate.
+ */
+
+static void simple_callback (struct urb *urb)
+{
+	complete ((struct completion *) urb->context);
+}
+
+static struct urb *simple_alloc_urb (
+	struct usb_device	*udev,
+	int			pipe,
+	long			bytes
+)
+{
+	struct urb		*urb;
+
+	if (bytes < 0)
+		return 0;
+	urb = usb_alloc_urb (0, SLAB_KERNEL);
+	if (!urb)
+		return urb;
+	usb_fill_bulk_urb (urb, udev, pipe, 0, bytes, simple_callback, 0);
+	urb->interval = (udev->speed == USB_SPEED_HIGH)
+			? (INTERRUPT_RATE << 3)
+			: INTERRUPT_RATE,
+	urb->transfer_flags = URB_NO_DMA_MAP;
+	urb->transfer_buffer = usb_buffer_alloc (udev, bytes, SLAB_KERNEL,
+			&urb->transfer_dma);
+	if (!urb->transfer_buffer) {
+		usb_free_urb (urb);
+		urb = 0;
+	} else
+		memset (urb->transfer_buffer, 0, bytes);
+	return urb;
+}
+
+static void simple_free_urb (struct urb *urb)
+{
+	usb_buffer_free (urb->dev, urb->transfer_buffer_length,
+			urb->transfer_buffer, urb->transfer_dma);
+	usb_free_urb (urb);
+}
+
+static int simple_io (
+	struct urb		*urb,
+	int			iterations,
+	int			vary
+)
+{
+	struct usb_device	*udev = urb->dev;
+	int			max = urb->transfer_buffer_length;
+	struct completion	completion;
+	int			retval = 0;
+
+	urb->context = &completion;
+	while (iterations-- > 0 && retval == 0) {
+		init_completion (&completion);
+		if ((retval = usb_submit_urb (urb, SLAB_KERNEL)) != 0)
+			break;
+
+		/* NOTE:  no timeouts; can't be broken out of by interrupt */
+		wait_for_completion (&completion);
+		retval = urb->status;
+		urb->dev = udev;
+
+		if (vary) {
+			int	len = urb->transfer_buffer_length;
+
+			len += max;
+			len %= max;
+			if (len == 0)
+				len = (vary < max) ? vary : max;
+			urb->transfer_buffer_length = len;
+		}
+
+		/* FIXME if endpoint halted, clear halt (and log) */
+	}
+	urb->transfer_buffer_length = max;
+
+	// FIXME for unlink or fault handling tests, don't report
+	// failure if retval is as we expected ...
+	if (retval)
+		dbg ("simple_io failed, iterations left %d, status %d",
+				iterations, retval);
+	return retval;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* We use scatterlist primitives to test queued I/O.
+ * Yes, this also tests the scatterlist primitives.
+ */
+
+static void free_sglist (struct scatterlist *sg, int nents)
+{
+	unsigned		i;
+	
+	if (!sg)
+		return;
+	for (i = 0; i < nents; i++) {
+		if (!sg [i].page)
+			continue;
+		kfree (page_address (sg [i].page) + sg [i].offset);
+	}
+	kfree (sg);
+}
+
+static struct scatterlist *
+alloc_sglist (int nents, int max, int vary)
+{
+	struct scatterlist	*sg;
+	unsigned		i;
+	unsigned		size = max;
+
+	sg = kmalloc (nents * sizeof *sg, SLAB_KERNEL);
+	if (!sg)
+		return 0;
+	memset (sg, 0, nents * sizeof *sg);
+
+	for (i = 0; i < nents; i++) {
+		char		*buf;
+
+		buf = kmalloc (size, SLAB_KERNEL);
+		if (!buf) {
+			free_sglist (sg, i);
+			return 0;
+		}
+		memset (buf, 0, size);
+
+		/* kmalloc pages are always physically contiguous! */
+		sg [i].page = virt_to_page (buf);
+		sg [i].offset = ((unsigned) buf) & ~PAGE_MASK;
+		sg [i].length = size;
+
+		if (vary) {
+			size += vary;
+			size %= max;
+			if (size == 0)
+				size = (vary < max) ? vary : max;
+		}
+	}
+
+	return sg;
+}
+
+static int perform_sglist (
+	struct usb_device	*udev,
+	unsigned		iterations,
+	int			pipe,
+	struct usb_sg_request	*req,
+	struct scatterlist	*sg,
+	int			nents
+)
+{
+	int			retval = 0;
+
+	while (retval == 0 && iterations-- > 0) {
+		retval = usb_sg_init (req, udev, pipe,
+				(udev->speed == USB_SPEED_HIGH)
+					? (INTERRUPT_RATE << 3)
+					: INTERRUPT_RATE,
+				sg, nents, 0, SLAB_KERNEL);
+		
+		if (retval)
+			break;
+		usb_sg_wait (req);
+		retval = req->status;
+
+		/* FIXME if endpoint halted, clear halt (and log) */
+	}
+
+	// FIXME for unlink or fault handling tests, don't report
+	// failure if retval is as we expected ...
+
+	if (retval)
+		dbg ("perform_sglist failed, iterations left %d, status %d",
+				iterations, retval);
+	return retval;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+/* We only have this one interface to user space, through usbfs.
+ * User mode code can scan usbfs to find N different devices (maybe on
+ * different busses) to use when testing, and allocate one thread per
+ * test.  So discovery is simplified, and we have no device naming issues.
+ *
+ * Don't use these only as stress/load tests.  Use them along with with
+ * other USB bus activity:  plugging, unplugging, mousing, mp3 playback,
+ * video capture, and so on.  Run different tests at different times, in
+ * different sequences.  Nothing here should interact with other devices,
+ * except indirectly by consuming USB bandwidth and CPU resources for test
+ * threads and request completion.
+ */
+
+static int usbtest_ioctl (struct usb_interface *intf, unsigned int code, void *buf)
+{
+	struct usbtest_dev	*dev = dev_get_drvdata (&intf->dev);
+	struct usb_device	*udev = testdev_to_usbdev (dev);
+	struct usbtest_param	*param = buf;
+	int			retval = -EOPNOTSUPP;
+	struct urb		*urb;
+	struct scatterlist	*sg;
+	struct usb_sg_request	req;
+	struct timeval		start;
+
+	// FIXME USBDEVFS_CONNECTINFO doesn't say how fast the device is.
+
+	if (code != USBTEST_REQUEST)
+		return -EOPNOTSUPP;
+
+	if (param->iterations <= 0 || param->length < 0
+			|| param->sglen < 0 || param->vary < 0)
+		return -EINVAL;
+
+	/*
+	 * Just a bunch of test cases that every HCD is expected to handle.
+	 *
+	 * Some may need specific firmware, though it'd be good to have
+	 * one firmware image to handle all the test cases.
+	 *
+	 * FIXME add more tests!  cancel requests, verify the data, control
+	 * requests, and so on.
+	 */
+	do_gettimeofday (&start);
+	switch (param->test_num) {
+
+	case 0:
+		dbg ("%s TEST 0:  NOP", dev->id);
+		retval = 0;
+		break;
+
+	/* Simple non-queued bulk I/O tests */
+	case 1:
+		if (dev->out_pipe == 0)
+			break;
+		dbg ("%s TEST 1:  write %d bytes %u times", dev->id,
+				param->length, param->iterations);
+		urb = simple_alloc_urb (udev, dev->out_pipe, param->length);
+		if (!urb) {
+			retval = -ENOMEM;
+			break;
+		}
+		// FIRMWARE:  bulk sink (maybe accepts short writes)
+		retval = simple_io (urb, param->iterations, 0);
+		simple_free_urb (urb);
+		break;
+	case 2:
+		if (dev->in_pipe == 0)
+			break;
+		dbg ("%s TEST 2:  read %d bytes %u times", dev->id,
+				param->length, param->iterations);
+		urb = simple_alloc_urb (udev, dev->in_pipe, param->length);
+		if (!urb) {
+			retval = -ENOMEM;
+			break;
+		}
+		// FIRMWARE:  bulk source (maybe generates short writes)
+		retval = simple_io (urb, param->iterations, 0);
+		simple_free_urb (urb);
+		break;
+	case 3:
+		if (dev->out_pipe == 0 || param->vary == 0)
+			break;
+		dbg ("%s TEST 3:  write/%d 0..%d bytes %u times", dev->id,
+				param->vary, param->length, param->iterations);
+		urb = simple_alloc_urb (udev, dev->out_pipe, param->length);
+		if (!urb) {
+			retval = -ENOMEM;
+			break;
+		}
+		// FIRMWARE:  bulk sink (maybe accepts short writes)
+		retval = simple_io (urb, param->iterations, param->vary);
+		simple_free_urb (urb);
+		break;
+	case 4:
+		if (dev->in_pipe == 0 || param->vary == 0)
+			break;
+		dbg ("%s TEST 3:  read/%d 0..%d bytes %u times", dev->id,
+				param->vary, param->length, param->iterations);
+		urb = simple_alloc_urb (udev, dev->out_pipe, param->length);
+		if (!urb) {
+			retval = -ENOMEM;
+			break;
+		}
+		// FIRMWARE:  bulk source (maybe generates short writes)
+		retval = simple_io (urb, param->iterations, param->vary);
+		simple_free_urb (urb);
+		break;
+
+	/* Queued bulk I/O tests */
+	case 5:
+		if (dev->out_pipe == 0 || param->sglen == 0)
+			break;
+		dbg ("%s TEST 5:  write %d sglists, %d entries of %d bytes",
+				dev->id, param->iterations,
+				param->sglen, param->length);
+		sg = alloc_sglist (param->sglen, param->length, 0);
+		if (!sg) {
+			retval = -ENOMEM;
+			break;
+		}
+		// FIRMWARE:  bulk sink (maybe accepts short writes)
+		retval = perform_sglist (udev, param->iterations, dev->out_pipe,
+				&req, sg, param->sglen);
+		free_sglist (sg, param->sglen);
+		break;
+
+	case 6:
+		if (dev->in_pipe == 0 || param->sglen == 0)
+			break;
+		dbg ("%s TEST 6:  read %d sglists, %d entries of %d bytes",
+				dev->id, param->iterations,
+				param->sglen, param->length);
+		sg = alloc_sglist (param->sglen, param->length, 0);
+		if (!sg) {
+			retval = -ENOMEM;
+			break;
+		}
+		// FIRMWARE:  bulk source (maybe generates short writes)
+		retval = perform_sglist (udev, param->iterations, dev->in_pipe,
+				&req, sg, param->sglen);
+		free_sglist (sg, param->sglen);
+		break;
+	case 7:
+		if (dev->out_pipe == 0 || param->sglen == 0 || param->vary == 0)
+			break;
+		dbg ("%s TEST 7:  write/%d %d sglists, %d entries 0..%d bytes",
+				dev->id, param->vary, param->iterations,
+				param->sglen, param->length);
+		sg = alloc_sglist (param->sglen, param->length, param->vary);
+		if (!sg) {
+			retval = -ENOMEM;
+			break;
+		}
+		// FIRMWARE:  bulk sink (maybe accepts short writes)
+		retval = perform_sglist (udev, param->iterations, dev->out_pipe,
+				&req, sg, param->sglen);
+		free_sglist (sg, param->sglen);
+		break;
+	case 8:
+		if (dev->in_pipe == 0 || param->sglen == 0 || param->vary == 0)
+			break;
+		dbg ("%s TEST 8:  read/%d %d sglists, %d entries 0..%d bytes",
+				dev->id, param->vary, param->iterations,
+				param->sglen, param->length);
+		sg = alloc_sglist (param->sglen, param->length, param->vary);
+		if (!sg) {
+			retval = -ENOMEM;
+			break;
+		}
+		// FIRMWARE:  bulk source (maybe generates short writes)
+		retval = perform_sglist (udev, param->iterations, dev->in_pipe,
+				&req, sg, param->sglen);
+		free_sglist (sg, param->sglen);
+		break;
+
+	/* test cases for the unlink/cancel codepaths need a thread to
+	 * usb_unlink_urb() or usg_sg_cancel(), and a way to check if
+	 * the urb/sg_request was properly canceled.
+	 *
+	 * for the unlink-queued cases, the usb_sg_*() code uses/tests
+	 * the "streamed" cleanup mode, not the "packet" one
+	 */
+
+	}
+	do_gettimeofday (&param->duration);
+	param->duration.tv_sec -= start.tv_sec;
+	param->duration.tv_usec -= start.tv_usec;
+	if (param->duration.tv_usec < 0) {
+		param->duration.tv_usec += 1000 * 1000;
+		param->duration.tv_sec -= 1;
+	}
+	return retval;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* most programmable USB devices can be given firmware that will support the
+ * test cases above.  one basic question is which endpoints to use for
+ * testing; endpoint numbers are not always firmware-selectable.
+ *
+ * for now, the driver_info in the device_id table entry just encodes the
+ * endpoint info for a pair of bulk-capable endpoints, which we can use
+ * for some interrupt transfer tests too.  later this could get fancier.
+ */
+#define EP_PAIR(in,out) (((in)<<4)|(out))
+
+static int force_interrupt = 0;
+MODULE_PARM (force_interrupt, "i");
+MODULE_PARM_DESC (force_interrupt, "0 = test bulk (default), else interrupt");
+
+static int
+usbtest_probe (struct usb_interface *intf, const struct usb_device_id *id)
+{
+	struct usb_device	*udev;
+	struct usbtest_dev	*dev;
+	unsigned long		driver_info = id->driver_info;
+
+	udev = interface_to_usbdev (intf);
+
+	dev = kmalloc (sizeof *dev, SLAB_KERNEL);
+	if (!dev)
+		return -ENOMEM;
+	memset (dev, 0, sizeof *dev);
+	snprintf (dev->id, sizeof dev->id, "%s-%s",
+			udev->bus->bus_name, udev->devpath);
+	dev->intf = intf;
+
+	/* NOTE this doesn't yet test the handful of difference that are
+	 * visible with high speed devices:  bigger maxpacket (1K) and
+	 * "high bandwidth" modes (up to 3 packets/uframe).
+	 */
+	if (force_interrupt || udev->speed == USB_SPEED_LOW) {
+		if (driver_info & 0xf0)
+			dev->in_pipe = usb_rcvintpipe (udev,
+				(driver_info >> 4) & 0x0f);
+		if (driver_info & 0x0f)
+			dev->out_pipe = usb_sndintpipe (udev,
+				driver_info & 0x0f);
+
+#if 1
+		// FIXME disabling this until we finally get rid of
+		// interrupt "automagic" resubmission
+		dbg ("%s:  no interrupt transfers for now", dev->id);
+		kfree (dev);
+		return -ENODEV;
+#endif
+	} else {
+		if (driver_info & 0xf0)
+			dev->in_pipe = usb_rcvbulkpipe (udev,
+				(driver_info >> 4) & 0x0f);
+		if (driver_info & 0x0f)
+			dev->out_pipe = usb_sndbulkpipe (udev,
+				driver_info & 0x0f);
+	}
+
+	dev_set_drvdata (&intf->dev, dev);
+	info ("bound to %s ...%s%s", dev->id,
+			dev->out_pipe ? " writes" : "",
+			dev->in_pipe ? " reads" : "");
+	return 0;
+}
+
+static void usbtest_disconnect (struct usb_interface *intf)
+{
+	struct usbtest_dev	*dev = dev_get_drvdata (&intf->dev);
+
+	dev_set_drvdata (&intf->dev, 0);
+	info ("unbound %s", dev->id);
+	kfree (intf->private_data);
+}
+
+/* Basic testing only needs a device that can source or sink bulk traffic.
+ */
+static struct usb_device_id id_table [] = {
+
+	/* EZ-USB FX2 "bulksrc" or "bulkloop" firmware from Cypress
+	 * reads disabled on this one, my version has some problem there
+	 */
+	{ USB_DEVICE (0x0547, 0x1002),
+		.driver_info = EP_PAIR (0, 2),
+		},
+#if 1
+	// this does not coexist with a real iBOT2 driver!
+	// it makes a nice source of high speed bulk-in data
+	{ USB_DEVICE (0x0b62, 0x0059),
+		.driver_info = EP_PAIR (2, 0),
+		},
+#endif
+
+	/* can that old "usbstress-0.3" firmware be used with this? */
+
+	{ }
+};
+MODULE_DEVICE_TABLE (usb, id_table);
+
+static struct usb_driver usbtest_driver = {
+	.owner =	THIS_MODULE,
+	.name =		"usbtest",
+	.id_table =	id_table,
+	.probe =	usbtest_probe,
+	.ioctl =	usbtest_ioctl,
+	.disconnect =	usbtest_disconnect,
+};
+
+/*-------------------------------------------------------------------------*/
+
+static int __init usbtest_init (void)
+{
+	return usb_register (&usbtest_driver);
+}
+module_init (usbtest_init);
+
+static void __exit usbtest_exit (void)
+{
+	usb_deregister (&usbtest_driver);
+}
+module_exit (usbtest_exit);
+
+MODULE_DESCRIPTION ("USB HCD Testing Driver");
+MODULE_LICENSE ("GPL");
+
diff -Nru a/drivers/usb/net/pegasus.h b/drivers/usb/net/pegasus.h
--- a/drivers/usb/net/pegasus.h	Mon Oct  7 11:25:39 2002
+++ b/drivers/usb/net/pegasus.h	Mon Oct  7 11:25:39 2002
@@ -191,7 +191,7 @@
 		DEFAULT_GPIO_RESET | PEGASUS_II )
 PEGASUS_DEV( "Corega FEter USB-TX", VENDOR_COREGA, 0x0004,
 		DEFAULT_GPIO_RESET )
-PEGASUS_DEV( "Corega FEter", VENDOR_COREGA, 0x0004,
+PEGASUS_DEV( "Corega FEter", VENDOR_COREGA, 0x000d,
 		DEFAULT_GPIO_RESET | PEGASUS_II )
 PEGASUS_DEV( "D-Link DSB-650TX", VENDOR_DLINK, 0x4001,
 		LINKSYS_GPIO_RESET )
diff -Nru a/drivers/usb/net/rtl8150.c b/drivers/usb/net/rtl8150.c
--- a/drivers/usb/net/rtl8150.c	Mon Oct  7 11:25:36 2002
+++ b/drivers/usb/net/rtl8150.c	Mon Oct  7 11:25:36 2002
@@ -21,11 +21,11 @@
 #include <asm/uaccess.h>
 
 /* Version Information */
-#define DRIVER_VERSION "v0.5.5 (2002/07/22)"
+#define DRIVER_VERSION "v0.5.6 (2002/09/19)"
 #define DRIVER_AUTHOR "Petko Manolov <petkan@users.sourceforge.net>"
 #define DRIVER_DESC "rtl8150 based usb-ethernet driver"
 
-#define	IRD			0x0120
+#define	IDR			0x0120
 #define	MAR			0x0126
 #define	CR			0x012e
 #define	TCR			0x012f
@@ -45,6 +45,8 @@
 #define	ANLP			0x0146
 #define	AER			0x0148
 
+#define	IDR_EEPROM		0x1202
+
 #define	PHY_READ		0
 #define	PHY_WRITE		0x20
 #define	PHY_GO			0x40
@@ -73,6 +75,8 @@
 #define PRODUCT_ID_RTL8150		0x8150
 #define	PRODUCT_ID_LUAKTX		0x0012
 
+#undef	EEPROM_WRITE
+
 /* table of devices that work with this driver */
 static struct usb_device_id rtl8150_table[] = {
 	{USB_DEVICE(VENDOR_ID_REALTEK, PRODUCT_ID_RTL8150)},
@@ -111,7 +115,7 @@
 static inline struct sk_buff *pull_skb(rtl8150_t *);
 static void rtl8150_disconnect(struct usb_interface *intf);
 static int rtl8150_probe(struct usb_interface *intf,
-			 const struct usb_device_id *id);
+			   const struct usb_device_id *id);
 
 static struct usb_driver rtl8150_driver = {
 	.name =		"rtl8150",
@@ -231,10 +235,51 @@
 {
 	u8 node_id[6];
 
-	get_registers(dev, IRD, sizeof(node_id), node_id);
+	get_registers(dev, IDR, sizeof(node_id), node_id);
 	memcpy(dev->netdev->dev_addr, node_id, sizeof(node_id));
 }
 
+static int rtl8150_set_mac_address(struct net_device *netdev, void *p)
+{
+	struct sockaddr *addr = p;
+	rtl8150_t *dev;
+	int i;
+
+	if (netif_running(netdev))
+		return -EBUSY;
+	dev = netdev->priv;
+	if (dev == NULL) {
+		return -ENODEV;
+	}
+	memcpy(netdev->dev_addr, addr->sa_data, netdev->addr_len);
+	dbg("%s: Setting MAC address to ", netdev->name);
+	for (i = 0; i < 5; i++)
+		dbg("%02X:", netdev->dev_addr[i]);
+	dbg("%02X\n", netdev->dev_addr[i]);
+	/* Set the IDR registers. */
+	set_registers(dev, IDR, sizeof(netdev->dev_addr), netdev->dev_addr);
+#ifdef EEPROM_WRITE
+	{
+	u8 cr;
+	/* Get the CR contents. */
+	get_registers(dev, CR, 1, &cr);
+	/* Set the WEPROM bit (eeprom write enable). */
+	cr |= 0x20;
+	set_registers(dev, CR, 1, &cr);
+	/* Write the MAC address into eeprom. Eeprom writes must be word-sized,
+	   so we need to split them up. */
+	for (i = 0; i * 2 < netdev->addr_len; i++) {
+		set_registers(dev, IDR_EEPROM + (i * 2), 2, 
+		netdev->dev_addr + (i * 2));
+	}
+	/* Clear the WEPROM bit (preventing accidental eeprom writes). */
+	cr &= 0xdf;
+	set_registers(dev, CR, 1, &cr);
+	}
+#endif
+	return 0;
+}
+
 static int rtl8150_reset(rtl8150_t * dev)
 {
 	u8 data = 0x10;
@@ -764,6 +809,7 @@
 	netdev->tx_timeout = rtl8150_tx_timeout;
 	netdev->hard_start_xmit = rtl8150_start_xmit;
 	netdev->set_multicast_list = rtl8150_set_multicast;
+	netdev->set_mac_address = rtl8150_set_mac_address;
 	netdev->get_stats = rtl8150_netdev_stats;
 	netdev->mtu = RTL8150_MTU;
 	dev->intr_interval = 100;	/* 100ms */
@@ -782,7 +828,7 @@
 	info("%s: rtl8150 is detected", netdev->name);
 	
 	up(&dev->sem);
-	dev_set_drvdata (&intf->dev, dev);
+	dev_set_drvdata(&intf->dev, dev);
 	return 0;
 err:
 	unregister_netdev(dev->netdev);
@@ -794,10 +840,9 @@
 
 static void rtl8150_disconnect(struct usb_interface *intf)
 {
-	rtl8150_t *dev = dev_get_drvdata (&intf->dev);
-
-	dev_set_drvdata (&intf->dev, NULL);
+	rtl8150_t *dev = dev_get_drvdata(&intf->dev);
 
+	dev_set_drvdata(&intf->dev, NULL);
 	if (dev) {
 		set_bit(RTL8150_UNPLUG, &dev->flags);
 		unregister_netdev(dev->netdev);
diff -Nru a/drivers/usb/net/usbnet.c b/drivers/usb/net/usbnet.c
--- a/drivers/usb/net/usbnet.c	Mon Oct  7 11:25:25 2002
+++ b/drivers/usb/net/usbnet.c	Mon Oct  7 11:25:25 2002
@@ -112,7 +112,7 @@
 #include <linux/etherdevice.h>
 #include <linux/random.h>
 #include <linux/ethtool.h>
-#include <linux/tqueue.h>
+#include <linux/workqueue.h>
 #include <asm/uaccess.h>
 #include <asm/unaligned.h>
 
@@ -201,7 +201,7 @@
 	struct sk_buff_head	done;
 	struct tasklet_struct	bh;
 
-	struct tq_struct	kevent;
+	struct work_struct			kevent;
 	unsigned long		flags;
 #		define EVENT_TX_HALT	0
 #		define EVENT_RX_HALT	1
@@ -1278,13 +1278,13 @@
 
 /* some work can't be done in tasklets, so we use keventd
  *
- * NOTE:  annoying asymmetry:  if it's active, schedule_task() fails,
+ * NOTE:  annoying asymmetry:  if it's active, schedule_work() fails,
  * but tasklet_schedule() doesn't.  hope the failure is rare.
  */
 static void defer_kevent (struct usbnet *dev, int work)
 {
 	set_bit (work, &dev->flags);
-	if (!schedule_task (&dev->kevent))
+	if (!schedule_work (&dev->kevent))
 		err ("%s: kevent %d may have been dropped",
 			dev->net.name, work);
 	else
@@ -1965,7 +1965,7 @@
 	mutex_unlock (&usbnet_mutex);
 
 	// assuming we used keventd, it must quiesce too
-	flush_scheduled_tasks ();
+	flush_scheduled_work ();
 
 	kfree (dev);
 	usb_put_dev (xdev);
@@ -2016,7 +2016,7 @@
 	skb_queue_head_init (&dev->done);
 	dev->bh.func = usbnet_bh;
 	dev->bh.data = (unsigned long) dev;
-	INIT_TQUEUE (&dev->kevent, kevent, dev);
+	INIT_WORK (&dev->kevent, kevent, dev);
 
 	// set up network interface records
 	net = &dev->net;
diff -Nru a/drivers/usb/serial/Makefile b/drivers/usb/serial/Makefile
--- a/drivers/usb/serial/Makefile	Mon Oct  7 11:25:26 2002
+++ b/drivers/usb/serial/Makefile	Mon Oct  7 11:25:26 2002
@@ -4,7 +4,10 @@
 
 # Object file lists.
 
-obj-$(CONFIG_USB_SERIAL)                        += usbserial.o
+obj-$(CONFIG_USB_SERIAL)			+= usbserial.o
+
+usbserial-obj-$(CONFIG_USB_SERIAL_CONSOLE)	+= console.o
+
 obj-$(CONFIG_USB_SERIAL_VISOR)			+= visor.o
 obj-$(CONFIG_USB_SERIAL_IPAQ)			+= ipaq.o
 obj-$(CONFIG_USB_SERIAL_WHITEHEAT)		+= whiteheat.o
@@ -26,7 +29,9 @@
 obj-$(CONFIG_USB_SERIAL_SAFE)			+= safe_serial.o
 
 # Objects that export symbols.
-export-objs	:= usbserial.o
+export-objs	:= usb-serial.o
+
+usbserial-objs	:= usb-serial.o $(usbserial-obj-y)
 
 include $(TOPDIR)/Rules.make
 
diff -Nru a/drivers/usb/serial/console.c b/drivers/usb/serial/console.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/usb/serial/console.c	Mon Oct  7 11:26:06 2002
@@ -0,0 +1,266 @@
+/*
+ * USB Serial Console driver
+ *
+ * Copyright (C) 2001 - 2002 Greg Kroah-Hartman (greg@kroah.com)
+ *
+ *	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.
+ * 
+ * Thanks to Randy Dunlap for the original version of this code.
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/tty.h>
+#include <linux/console.h>
+#include <linux/usb.h>
+
+static int debug;
+
+#include "usb-serial.h"
+
+struct usbcons_info {
+	int			magic;
+	int			break_flag;
+	struct usb_serial_port	*port;
+};
+
+static struct usbcons_info usbcons_info;
+static struct console usbcons;
+
+/*
+ * ------------------------------------------------------------
+ * USB Serial console driver
+ *
+ * Much of the code here is copied from drivers/char/serial.c
+ * and implements a phony serial console in the same way that
+ * serial.c does so that in case some software queries it,
+ * it will get the same results.
+ *
+ * Things that are different from the way the serial port code
+ * does things, is that we call the lower level usb-serial
+ * driver code to initialize the device, and we set the initial
+ * console speeds based on the command line arguments.
+ * ------------------------------------------------------------
+ */
+
+
+/*
+ * The parsing of the command line works exactly like the
+ * serial.c code, except that the specifier is "ttyUSB" instead
+ * of "ttyS".
+ */
+static int __init usb_console_setup(struct console *co, char *options)
+{
+	struct usbcons_info *info = &usbcons_info;
+	int baud = 9600;
+	int bits = 8;
+	int parity = 'n';
+	int doflow = 0;
+	int cflag = CREAD | HUPCL | CLOCAL;
+	char *s;
+	struct usb_serial *serial;
+	struct usb_serial_port *port;
+	int retval = 0;
+	struct tty_struct *tty;
+	struct termios *termios;
+
+	dbg ("%s", __FUNCTION__);
+
+	if (options) {
+		baud = simple_strtoul(options, NULL, 10);
+		s = options;
+		while (*s >= '0' && *s <= '9')
+			s++;
+		if (*s)	parity = *s++;
+		if (*s)	bits   = *s++ - '0';
+		if (*s)	doflow = (*s++ == 'r');
+	}
+
+	/* build a cflag setting */
+	switch (baud) {
+		case 1200:
+			cflag |= B1200;
+			break;
+		case 2400:
+			cflag |= B2400;
+			break;
+		case 4800:
+			cflag |= B4800;
+			break;
+		case 19200:
+			cflag |= B19200;
+			break;
+		case 38400:
+			cflag |= B38400;
+			break;
+		case 57600:
+			cflag |= B57600;
+			break;
+		case 115200:
+			cflag |= B115200;
+			break;
+		case 9600:
+		default:
+			cflag |= B9600;
+			/*
+			 * Set this to a sane value to prevent a divide error
+			 */
+			baud  = 9600;
+			break;
+	}
+	switch (bits) {
+		case 7:
+			cflag |= CS7;
+			break;
+		default:
+		case 8:
+			cflag |= CS8;
+			break;
+	}
+	switch (parity) {
+		case 'o': case 'O':
+			cflag |= PARODD;
+			break;
+		case 'e': case 'E':
+			cflag |= PARENB;
+			break;
+	}
+	co->cflag = cflag;
+
+	/* grab the first serial port that happens to be connected */
+	serial = usb_serial_get_by_minor (0);
+	if (serial_paranoia_check (serial, __FUNCTION__)) {
+		/* no device is connected yet, sorry :( */
+		err ("No USB device connected to ttyUSB0");
+		return -ENODEV;
+	}
+
+	port = &serial->port[0];
+	down (&port->sem);
+	port->tty = NULL;
+
+	info->port = port;
+	 
+	++port->open_count;
+	if (port->open_count == 1) {
+		/* only call the device specific open if this 
+		 * is the first time the port is opened */
+		if (serial->type->open)
+			retval = serial->type->open(port, NULL);
+		else
+			retval = usb_serial_generic_open(port, NULL);
+		if (retval)
+			port->open_count = 0;
+	}
+
+	up (&port->sem);
+
+	if (retval) {
+		err ("could not open USB console port");
+		return retval;
+	}
+
+	if (serial->type->set_termios) {
+		/* build up a fake tty structure so that the open call has something
+		 * to look at to get the cflag value */
+		tty = kmalloc (sizeof (*tty), GFP_KERNEL);
+		if (!tty) {
+			err ("no more memory");
+			return -ENOMEM;
+		}
+		termios = kmalloc (sizeof (*termios), GFP_KERNEL);
+		if (!termios) {
+			err ("no more memory");
+			kfree (tty);
+			return -ENOMEM;
+		}
+		memset (tty, 0x00, sizeof(*tty));
+		memset (termios, 0x00, sizeof(*termios));
+		termios->c_cflag = cflag;
+		tty->termios = termios;
+		port->tty = tty;
+
+		/* set up the initial termios settings */
+		serial->type->set_termios(port, NULL);
+		port->tty = NULL;
+		kfree (termios);
+		kfree (tty);
+	}
+
+	return retval;
+}
+
+static void usb_console_write(struct console *co, const char *buf, unsigned count)
+{
+	static struct usbcons_info *info = &usbcons_info;
+	struct usb_serial_port *port = info->port;
+	struct usb_serial *serial = get_usb_serial (port, __FUNCTION__);
+	int retval = -ENODEV;
+
+	if (!serial || !port)
+		return;
+
+	if (count == 0)
+		return;
+
+	down (&port->sem);
+
+	dbg("%s - port %d, %d byte(s)", __FUNCTION__, port->number, count);
+
+	if (!port->open_count) {
+		dbg ("%s - port not opened", __FUNCTION__);
+		goto exit;
+	}
+
+	/* pass on to the driver specific version of this function if it is available */
+	if (serial->type->write)
+		retval = serial->type->write(port, 0, buf, count);
+	else
+		retval = usb_serial_generic_write(port, 0, buf, count);
+
+exit:
+	up (&port->sem);
+	dbg("%s - return value (if we had one): %d", __FUNCTION__, retval);
+}
+
+static struct console usbcons = {
+	.name =		"ttyUSB",
+	.write =	usb_console_write,
+	.setup =	usb_console_setup,
+	.flags =	CON_PRINTBUFFER,
+	.index =	-1,
+};
+
+void usb_serial_console_init (int serial_debug, int minor)
+{
+	debug = serial_debug;
+
+	if (minor == 0) {
+		/* 
+		 * Call register_console() if this is the first device plugged
+		 * in.  If we call it earlier, then the callback to
+		 * console_setup() will fail, as there is not a device seen by
+		 * the USB subsystem yet.
+		 */
+		/*
+		 * Register console.
+		 * NOTES:
+		 * console_setup() is called (back) immediately (from register_console).
+		 * console_write() is called immediately from register_console iff
+		 * CON_PRINTBUFFER is set in flags.
+		 */
+		dbg ("registering the USB serial console.");
+		register_console(&usbcons);
+	}
+}
+
+void usb_serial_console_exit (void)
+{
+	unregister_console(&usbcons);
+}
+
diff -Nru a/drivers/usb/serial/cyberjack.c b/drivers/usb/serial/cyberjack.c
--- a/drivers/usb/serial/cyberjack.c	Mon Oct  7 11:25:38 2002
+++ b/drivers/usb/serial/cyberjack.c	Mon Oct  7 11:25:38 2002
@@ -454,7 +454,7 @@
 	}
 
 exit:
-	schedule_task(&port->tqueue);
+	schedule_work(&port->work);
 }
 
 static int __init cyberjack_init (void)
diff -Nru a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c
--- a/drivers/usb/serial/digi_acceleport.c	Mon Oct  7 11:25:35 2002
+++ b/drivers/usb/serial/digi_acceleport.c	Mon Oct  7 11:25:35 2002
@@ -243,7 +243,7 @@
 #include <linux/tty_flip.h>
 #include <linux/module.h>
 #include <linux/spinlock.h>
-#include <linux/tqueue.h>
+#include <linux/workqueue.h>
 #include <asm/uaccess.h>
 #include <linux/usb.h>
 
@@ -437,7 +437,7 @@
 	wait_queue_head_t dp_flush_wait;
 	int dp_in_close;			/* close in progress */
 	wait_queue_head_t dp_close_wait;	/* wait queue for close */
-	struct tq_struct dp_wakeup_task;
+	struct work_struct dp_wakeup_work;
 };
 
 
@@ -1416,7 +1416,7 @@
 
 	/* also queue up a wakeup at scheduler time, in case we */
 	/* lost the race in write_chan(). */
-	schedule_task(&priv->dp_wakeup_task);
+	schedule_work(&priv->dp_wakeup_work);
 
 	spin_unlock( &priv->dp_port_lock );
 
@@ -1714,10 +1714,8 @@
 		init_waitqueue_head( &priv->dp_flush_wait );
 		priv->dp_in_close = 0;
 		init_waitqueue_head( &priv->dp_close_wait );
-		INIT_LIST_HEAD(&priv->dp_wakeup_task.list);
-		priv->dp_wakeup_task.sync = 0;
-		priv->dp_wakeup_task.routine = (void *)digi_wakeup_write_lock;
-		priv->dp_wakeup_task.data = (void *)(&serial->port[i]);
+		INIT_WORK(&priv->dp_wakeup_work, (void *)digi_wakeup_write_lock,
+				(void *)(&serial->port[i]));
 
 		/* initialize write wait queue for this port */
 		init_waitqueue_head( &serial->port[i].write_wait );
diff -Nru a/drivers/usb/serial/empeg.c b/drivers/usb/serial/empeg.c
--- a/drivers/usb/serial/empeg.c	Mon Oct  7 11:25:34 2002
+++ b/drivers/usb/serial/empeg.c	Mon Oct  7 11:25:34 2002
@@ -362,7 +362,7 @@
 		return;
 	}
 
-	schedule_task(&port->tqueue);
+	schedule_work(&port->work);
 }
 
 
diff -Nru a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c
--- a/drivers/usb/serial/ftdi_sio.c	Mon Oct  7 11:25:27 2002
+++ b/drivers/usb/serial/ftdi_sio.c	Mon Oct  7 11:25:27 2002
@@ -485,7 +485,7 @@
 		return;
 	}
 
-	schedule_task(&port->tqueue);
+	schedule_work(&port->work);
 } /* ftdi_sio_write_bulk_callback */
 
 
diff -Nru a/drivers/usb/serial/ipaq.c b/drivers/usb/serial/ipaq.c
--- a/drivers/usb/serial/ipaq.c	Mon Oct  7 11:25:28 2002
+++ b/drivers/usb/serial/ipaq.c	Mon Oct  7 11:25:28 2002
@@ -466,7 +466,7 @@
 		spin_unlock_irqrestore(&write_list_lock, flags);
 	}
 
-	schedule_task(&port->tqueue);
+	schedule_work(&port->work);
 }
 
 static int ipaq_write_room(struct usb_serial_port *port)
diff -Nru a/drivers/usb/serial/ir-usb.c b/drivers/usb/serial/ir-usb.c
--- a/drivers/usb/serial/ir-usb.c	Mon Oct  7 11:25:35 2002
+++ b/drivers/usb/serial/ir-usb.c	Mon Oct  7 11:25:35 2002
@@ -426,7 +426,7 @@
 		urb->actual_length,
 		urb->transfer_buffer);
 
-	schedule_task(&port->tqueue);
+	schedule_work(&port->work);
 }
 
 static void ir_read_bulk_callback (struct urb *urb)
diff -Nru a/drivers/usb/serial/keyspan.c b/drivers/usb/serial/keyspan.c
--- a/drivers/usb/serial/keyspan.c	Mon Oct  7 11:25:25 2002
+++ b/drivers/usb/serial/keyspan.c	Mon Oct  7 11:25:25 2002
@@ -448,7 +448,7 @@
 	dbg ("%s - urb %d", __FUNCTION__, urb == p_priv->out_urbs[1]); 
 
 	if (port->open_count)
-		schedule_task(&port->tqueue);
+		schedule_work(&port->work);
 }
 
 static void	usa26_inack_callback(struct urb *urb)
diff -Nru a/drivers/usb/serial/keyspan_pda.c b/drivers/usb/serial/keyspan_pda.c
--- a/drivers/usb/serial/keyspan_pda.c	Mon Oct  7 11:25:36 2002
+++ b/drivers/usb/serial/keyspan_pda.c	Mon Oct  7 11:25:36 2002
@@ -76,7 +76,7 @@
 #include <linux/tty_flip.h>
 #include <linux/module.h>
 #include <linux/spinlock.h>
-#include <linux/tqueue.h>
+#include <linux/workqueue.h>
 #include <asm/uaccess.h>
 #include <linux/usb.h>
 
@@ -125,8 +125,8 @@
 struct keyspan_pda_private {
 	int			tx_room;
 	int			tx_throttled;
-	struct tq_struct	wakeup_task;
-	struct tq_struct	unthrottle_task;
+	struct work_struct			wakeup_work;
+	struct work_struct			unthrottle_work;
 };
 
 
@@ -267,7 +267,7 @@
 			tty = serial->port[0].tty;
 			priv->tx_throttled = 0;
 			/* queue up a wakeup at scheduler time */
-			schedule_task(&priv->wakeup_task);
+			schedule_work(&priv->wakeup_work);
 			break;
 		default:
 			break;
@@ -611,7 +611,7 @@
 
 	if (request_unthrottle) {
 		priv->tx_throttled = 1; /* block writers */
-		schedule_task(&priv->unthrottle_task);
+		schedule_work(&priv->unthrottle_work);
 	}
 
 	rc = count;
@@ -638,7 +638,7 @@
 	}
 	
 	/* queue up a wakeup at scheduler time */
-	schedule_task(&priv->wakeup_task);
+	schedule_work(&priv->wakeup_work);
 }
 
 
@@ -791,14 +791,11 @@
 	if (!priv)
 		return (1); /* error */
 	init_waitqueue_head(&serial->port[0].write_wait);
-	INIT_LIST_HEAD(&priv->wakeup_task.list);
-	priv->wakeup_task.sync = 0;
-	priv->wakeup_task.routine = (void *)keyspan_pda_wakeup_write;
-	priv->wakeup_task.data = (void *)(&serial->port[0]);
-	INIT_LIST_HEAD(&priv->unthrottle_task.list);
-	priv->unthrottle_task.sync = 0;
-	priv->unthrottle_task.routine = (void *)keyspan_pda_request_unthrottle;
-	priv->unthrottle_task.data = (void *)(serial);
+	INIT_WORK(&priv->wakeup_work, (void *)keyspan_pda_wakeup_write,
+			(void *)(&serial->port[0]));
+	INIT_WORK(&priv->unthrottle_work,
+			(void *)keyspan_pda_request_unthrottle,
+			(void *)(serial));
 	return (0);
 }
 
diff -Nru a/drivers/usb/serial/kl5kusb105.c b/drivers/usb/serial/kl5kusb105.c
--- a/drivers/usb/serial/kl5kusb105.c	Mon Oct  7 11:25:35 2002
+++ b/drivers/usb/serial/kl5kusb105.c	Mon Oct  7 11:25:35 2002
@@ -577,7 +577,7 @@
 	}
 
 	/* from generic_write_bulk_callback */
-	schedule_task(&port->tqueue);
+	schedule_work(&port->work);
 } /* klsi_105_write_bulk_completion_callback */
 
 
diff -Nru a/drivers/usb/serial/mct_u232.c b/drivers/usb/serial/mct_u232.c
--- a/drivers/usb/serial/mct_u232.c	Mon Oct  7 11:25:36 2002
+++ b/drivers/usb/serial/mct_u232.c	Mon Oct  7 11:25:36 2002
@@ -507,7 +507,7 @@
 		
 	} else {
 		/* from generic_write_bulk_callback */
-		schedule_task(&port->tqueue);
+		schedule_work(&port->work);
 	}
 
 	return;
diff -Nru a/drivers/usb/serial/omninet.c b/drivers/usb/serial/omninet.c
--- a/drivers/usb/serial/omninet.c	Mon Oct  7 11:25:29 2002
+++ b/drivers/usb/serial/omninet.c	Mon Oct  7 11:25:29 2002
@@ -359,7 +359,7 @@
 		return;
 	}
 
-	schedule_task(&port->tqueue);
+	schedule_work(&port->work);
 
 //	dbg("omninet_write_bulk_callback, tty %0x\n", tty);
 }
diff -Nru a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c
--- a/drivers/usb/serial/pl2303.c	Mon Oct  7 11:25:35 2002
+++ b/drivers/usb/serial/pl2303.c	Mon Oct  7 11:25:35 2002
@@ -705,7 +705,7 @@
 		return;
 	}
 
-	schedule_task(&port->tqueue);
+	schedule_work(&port->work);
 }
 
 
diff -Nru a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/usb/serial/usb-serial.c	Mon Oct  7 11:25:32 2002
@@ -0,0 +1,1683 @@
+/*
+ * USB Serial Converter driver
+ *
+ * Copyright (C) 1999 - 2002 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (c) 2000 Peter Berger (pberger@brimson.com)
+ * Copyright (c) 2000 Al Borchers (borchers@steinerpoint.com)
+ *
+ *	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 driver was originally based on the ACM driver by Armin Fuerst (which was 
+ * based on a driver by Brad Keryan)
+ *
+ * See Documentation/usb/usb-serial.txt for more information on using this driver
+ *
+ * (10/02/2002) gkh
+ *	moved the console code to console.c and out of this file.
+ *
+ * (06/05/2002) gkh
+ *	moved location of startup() call in serial_probe() until after all
+ *	of the port information and endpoints are initialized.  This makes
+ *	things easier for some drivers.
+ *
+ * (04/10/2002) gkh
+ *	added serial_read_proc function which creates a
+ *	/proc/tty/driver/usb-serial file.
+ *
+ * (03/27/2002) gkh
+ *	Got USB serial console code working properly and merged into the main
+ *	version of the tree.  Thanks to Randy Dunlap for the initial version
+ *	of this code, and for pushing me to finish it up.
+ *	The USB serial console works with any usb serial driver device.
+ *
+ * (03/21/2002) gkh
+ *	Moved all manipulation of port->open_count into the core.  Now the
+ *	individual driver's open and close functions are called only when the
+ *	first open() and last close() is called.  Making the drivers a bit
+ *	smaller and simpler.
+ *	Fixed a bug if a driver didn't have the owner field set.
+ *
+ * (02/26/2002) gkh
+ *	Moved all locking into the main serial_* functions, instead of having 
+ *	the individual drivers have to grab the port semaphore.  This should
+ *	reduce races.
+ *	Reworked the MOD_INC logic a bit to always increment and decrement, even
+ *	if the generic driver is being used.
+ *
+ * (10/10/2001) gkh
+ *	usb_serial_disconnect() now sets the serial->dev pointer is to NULL to
+ *	help prevent child drivers from accessing the device since it is now
+ *	gone.
+ *
+ * (09/13/2001) gkh
+ *	Moved generic driver initialize after we have registered with the USB
+ *	core.  Thanks to Randy Dunlap for pointing this problem out.
+ *
+ * (07/03/2001) gkh
+ *	Fixed module paramater size.  Thanks to John Brockmeyer for the pointer.
+ *	Fixed vendor and product getting defined through the MODULE_PARM macro
+ *	if the Generic driver wasn't compiled in.
+ *	Fixed problem with generic_shutdown() not being called for drivers that
+ *	don't have a shutdown() function.
+ *
+ * (06/06/2001) gkh
+ *	added evil hack that is needed for the prolific pl2303 device due to the
+ *	crazy way its endpoints are set up.
+ *
+ * (05/30/2001) gkh
+ *	switched from using spinlock to a semaphore, which fixes lots of problems.
+ *
+ * (04/08/2001) gb
+ *	Identify version on module load.
+ *
+ * 2001_02_05 gkh
+ *	Fixed buffer overflows bug with the generic serial driver.  Thanks to
+ *	Todd Squires <squirest@ct0.com> for fixing this.
+ *
+ * (01/10/2001) gkh
+ *	Fixed bug where the generic serial adaptor grabbed _any_ device that was
+ *	offered to it.
+ *
+ * (12/12/2000) gkh
+ *	Removed MOD_INC and MOD_DEC from poll and disconnect functions, and
+ *	moved them to the serial_open and serial_close functions.
+ *	Also fixed bug with there not being a MOD_DEC for the generic driver
+ *	(thanks to Gary Brubaker for finding this.)
+ *
+ * (11/29/2000) gkh
+ *	Small NULL pointer initialization cleanup which saves a bit of disk image
+ *
+ * (11/01/2000) Adam J. Richter
+ *	instead of using idVendor/idProduct pairs, usb serial drivers
+ *	now identify their hardware interest with usb_device_id tables,
+ *	which they usually have anyhow for use with MODULE_DEVICE_TABLE.
+ *
+ * (10/05/2000) gkh
+ *	Fixed bug with urb->dev not being set properly, now that the usb
+ *	core needs it.
+ * 
+ * (09/11/2000) gkh
+ *	Removed DEBUG #ifdefs with call to usb_serial_debug_data
+ *
+ * (08/28/2000) gkh
+ *	Added port_lock to port structure.
+ *	Added locks for SMP safeness to generic driver
+ *	Fixed the ability to open a generic device's port more than once.
+ *
+ * (07/23/2000) gkh
+ *	Added bulk_out_endpointAddress to port structure.
+ *
+ * (07/19/2000) gkh, pberger, and borchers
+ *	Modifications to allow usb-serial drivers to be modules.
+ *
+ * (07/03/2000) gkh
+ *	Added more debugging to serial_ioctl call
+ * 
+ * (06/25/2000) gkh
+ *	Changed generic_write_bulk_callback to not call wake_up_interruptible
+ *	directly, but to have port_softint do it at a safer time.
+ *
+ * (06/23/2000) gkh
+ *	Cleaned up debugging statements in a quest to find UHCI timeout bug.
+ *
+ * (05/22/2000) gkh
+ *	Changed the makefile, enabling the big CONFIG_USB_SERIAL_SOMTHING to be 
+ *	removed from the individual device source files.
+ *
+ * (05/03/2000) gkh
+ *	Added the Digi Acceleport driver from Al Borchers and Peter Berger.
+ * 
+ * (05/02/2000) gkh
+ *	Changed devfs and tty register code to work properly now. This was based on
+ *	the ACM driver changes by Vojtech Pavlik.
+ *
+ * (04/27/2000) Ryan VanderBijl
+ * 	Put calls to *_paranoia_checks into one function.
+ * 
+ * (04/23/2000) gkh
+ *	Fixed bug that Randy Dunlap found for Generic devices with no bulk out ports.
+ *	Moved when the startup code printed out the devices that are supported.
+ *
+ * (04/19/2000) gkh
+ *	Added driver for ZyXEL omni.net lcd plus ISDN TA
+ *	Made startup info message specify which drivers were compiled in.
+ *
+ * (04/03/2000) gkh
+ *	Changed the probe process to remove the module unload races.
+ *	Changed where the tty layer gets initialized to have devfs work nicer.
+ *	Added initial devfs support.
+ *
+ * (03/26/2000) gkh
+ *	Split driver up into device specific pieces.
+ * 
+ * (03/19/2000) gkh
+ *	Fixed oops that could happen when device was removed while a program
+ *	was talking to the device.
+ *	Removed the static urbs and now all urbs are created and destroyed
+ *	dynamically.
+ *	Reworked the internal interface. Now everything is based on the 
+ *	usb_serial_port structure instead of the larger usb_serial structure.
+ *	This fixes the bug that a multiport device could not have more than
+ *	one port open at one time.
+ *
+ * (03/17/2000) gkh
+ *	Added config option for debugging messages.
+ *	Added patch for keyspan pda from Brian Warner.
+ *
+ * (03/06/2000) gkh
+ *	Added the keyspan pda code from Brian Warner <warner@lothar.com>
+ *	Moved a bunch of the port specific stuff into its own structure. This
+ *	is in anticipation of the true multiport devices (there's a bug if you
+ *	try to access more than one port of any multiport device right now)
+ *
+ * (02/21/2000) gkh
+ *	Made it so that any serial devices only have to specify which functions
+ *	they want to overload from the generic function calls (great, 
+ *	inheritance in C, in a driver, just what I wanted...)
+ *	Added support for set_termios and ioctl function calls. No drivers take
+ *	advantage of this yet.
+ *	Removed the #ifdef MODULE, now there is no module specific code.
+ *	Cleaned up a few comments in usb-serial.h that were wrong (thanks again
+ *	to Miles Lott).
+ *	Small fix to get_free_serial.
+ *
+ * (02/14/2000) gkh
+ *	Removed the Belkin and Peracom functionality from the driver due to
+ *	the lack of support from the vendor, and me not wanting people to 
+ *	accidenatly buy the device, expecting it to work with Linux.
+ *	Added read_bulk_callback and write_bulk_callback to the type structure
+ *	for the needs of the FTDI and WhiteHEAT driver.
+ *	Changed all reverences to FTDI to FTDI_SIO at the request of Bill
+ *	Ryder.
+ *	Changed the output urb size back to the max endpoint size to make
+ *	the ftdi_sio driver have it easier, and due to the fact that it didn't
+ *	really increase the speed any.
+ *
+ * (02/11/2000) gkh
+ *	Added VISOR_FUNCTION_CONSOLE to the visor startup function. This was a
+ *	patch from Miles Lott (milos@insync.net).
+ *	Fixed bug with not restoring the minor range that a device grabs, if
+ *	the startup function fails (thanks Miles for finding this).
+ *
+ * (02/05/2000) gkh
+ *	Added initial framework for the Keyspan PDA serial converter so that
+ *	Brian Warner has a place to put his code.
+ *	Made the ezusb specific functions generic enough that different
+ *	devices can use them (whiteheat and keyspan_pda both need them).
+ *	Split out a whole bunch of structure and other stuff to a seperate
+ *	usb-serial.h file.
+ *	Made the Visor connection messages a little more understandable, now
+ *	that Miles Lott (milos@insync.net) has gotten the Generic channel to
+ *	work. Also made them always show up in the log file.
+ * 
+ * (01/25/2000) gkh
+ *	Added initial framework for FTDI serial converter so that Bill Ryder
+ *	has a place to put his code.
+ *	Added the vendor specific info from Handspring. Now we can print out
+ *	informational debug messages as well as understand what is happening.
+ *
+ * (01/23/2000) gkh
+ *	Fixed problem of crash when trying to open a port that didn't have a
+ *	device assigned to it. Made the minor node finding a little smarter,
+ *	now it looks to find a continous space for the new device.
+ *
+ * (01/21/2000) gkh
+ *	Fixed bug in visor_startup with patch from Miles Lott (milos@insync.net)
+ *	Fixed get_serial_by_minor which was all messed up for multi port 
+ *	devices. Fixed multi port problem for generic devices. Now the number
+ *	of ports is determined by the number of bulk out endpoints for the
+ *	generic device.
+ *
+ * (01/19/2000) gkh
+ *	Removed lots of cruft that was around from the old (pre urb) driver 
+ *	interface.
+ *	Made the serial_table dynamic. This should save lots of memory when
+ *	the number of minor nodes goes up to 256.
+ *	Added initial support for devices that have more than one port. 
+ *	Added more debugging comments for the Visor, and added a needed 
+ *	set_configuration call.
+ *
+ * (01/17/2000) gkh
+ *	Fixed the WhiteHEAT firmware (my processing tool had a bug)
+ *	and added new debug loader firmware for it.
+ *	Removed the put_char function as it isn't really needed.
+ *	Added visor startup commands as found by the Win98 dump.
+ * 
+ * (01/13/2000) gkh
+ *	Fixed the vendor id for the generic driver to the one I meant it to be.
+ *
+ * (01/12/2000) gkh
+ *	Forget the version numbering...that's pretty useless...
+ *	Made the driver able to be compiled so that the user can select which
+ *	converter they want to use. This allows people who only want the Visor
+ *	support to not pay the memory size price of the WhiteHEAT.
+ *	Fixed bug where the generic driver (idVendor=0000 and idProduct=0000)
+ *	grabbed the root hub. Not good.
+ * 
+ * version 0.4.0 (01/10/2000) gkh
+ *	Added whiteheat.h containing the firmware for the ConnectTech WhiteHEAT
+ *	device. Added startup function to allow firmware to be downloaded to
+ *	a device if it needs to be.
+ *	Added firmware download logic to the WhiteHEAT device.
+ *	Started to add #defines to split up the different drivers for potential
+ *	configuration option.
+ *	
+ * version 0.3.1 (12/30/99) gkh
+ *      Fixed problems with urb for bulk out.
+ *      Added initial support for multiple sets of endpoints. This enables
+ *      the Handspring Visor to be attached successfully. Only the first
+ *      bulk in / bulk out endpoint pair is being used right now.
+ *
+ * version 0.3.0 (12/27/99) gkh
+ *	Added initial support for the Handspring Visor based on a patch from
+ *	Miles Lott (milos@sneety.insync.net)
+ *	Cleaned up the code a bunch and converted over to using urbs only.
+ *
+ * version 0.2.3 (12/21/99) gkh
+ *	Added initial support for the Connect Tech WhiteHEAT converter.
+ *	Incremented the number of ports in expectation of getting the
+ *	WhiteHEAT to work properly (4 ports per connection).
+ *	Added notification on insertion and removal of what port the
+ *	device is/was connected to (and what kind of device it was).
+ *
+ * version 0.2.2 (12/16/99) gkh
+ *	Changed major number to the new allocated number. We're legal now!
+ *
+ * version 0.2.1 (12/14/99) gkh
+ *	Fixed bug that happens when device node is opened when there isn't a
+ *	device attached to it. Thanks to marek@webdesign.no for noticing this.
+ *
+ * version 0.2.0 (11/10/99) gkh
+ *	Split up internals to make it easier to add different types of serial 
+ *	converters to the code.
+ *	Added a "generic" driver that gets it's vendor and product id
+ *	from when the module is loaded. Thanks to David E. Nelson (dnelson@jump.net)
+ *	for the idea and sample code (from the usb scanner driver.)
+ *	Cleared up any licensing questions by releasing it under the GNU GPL.
+ *
+ * version 0.1.2 (10/25/99) gkh
+ * 	Fixed bug in detecting device.
+ *
+ * version 0.1.1 (10/05/99) gkh
+ * 	Changed the major number to not conflict with anything else.
+ *
+ * version 0.1 (09/28/99) gkh
+ * 	Can recognize the two different devices and start up a read from
+ *	device when asked to. Writes also work. No control signals yet, this
+ *	all is vendor specific data (i.e. no spec), also no control for
+ *	different baud rates or other bit settings.
+ *	Currently we are using the same devid as the acm driver. This needs
+ *	to change.
+ * 
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/tty.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/list.h>
+#include <linux/smp_lock.h>
+#include <asm/uaccess.h>
+#include <linux/usb.h>
+
+
+#ifdef CONFIG_USB_SERIAL_DEBUG
+	static int debug = 1;
+#else
+	static int debug;
+#endif
+
+#include "usb-serial.h"
+#include "pl2303.h"
+
+/*
+ * Version Information
+ */
+#define DRIVER_VERSION "v1.6"
+#define DRIVER_AUTHOR "Greg Kroah-Hartman, greg@kroah.com, http://www.kroah.com/linux/"
+#define DRIVER_DESC "USB Serial Driver core"
+
+/* function prototypes for a "generic" type serial converter (no flow control, not all endpoints needed) */
+/* need to always compile these in, as some of the other devices use these functions as their own. */
+/* if a driver does not provide a function pointer, the generic function will be called. */
+int usb_serial_generic_open		(struct usb_serial_port *port, struct file *filp);
+int usb_serial_generic_write		(struct usb_serial_port *port, int from_user, const unsigned char *buf, int count);
+static void generic_close		(struct usb_serial_port *port, struct file *filp);
+static int  generic_write_room		(struct usb_serial_port *port);
+static int  generic_chars_in_buffer	(struct usb_serial_port *port);
+static void generic_read_bulk_callback	(struct urb *urb);
+static void generic_write_bulk_callback	(struct urb *urb);
+static void generic_shutdown		(struct usb_serial *serial);
+
+
+#ifdef CONFIG_USB_SERIAL_GENERIC
+static __u16	vendor	= 0x05f9;
+static __u16	product	= 0xffff;
+
+static struct usb_device_id generic_device_ids[2]; /* Initially all zeroes. */
+
+/* All of the device info needed for the Generic Serial Converter */
+static struct usb_serial_device_type generic_device = {
+	.owner =		THIS_MODULE,
+	.name =			"Generic",
+	.id_table =		generic_device_ids,
+	.num_interrupt_in =	NUM_DONT_CARE,
+	.num_bulk_in =		NUM_DONT_CARE,
+	.num_bulk_out =		NUM_DONT_CARE,
+	.num_ports =		1,
+	.shutdown =		generic_shutdown,
+};
+
+/* we want to look at all devices, as the vendor/product id can change
+ * depending on the command line argument */
+static struct usb_device_id generic_serial_ids[] = {
+	{.driver_info = 42},
+	{}
+};
+#endif
+
+/* Driver structure we register with the USB core */
+static struct usb_driver usb_serial_driver = {
+	.name =		"serial",
+	.probe =	usb_serial_probe,
+	.disconnect =	usb_serial_disconnect,
+#ifdef CONFIG_USB_SERIAL_GENERIC
+	.id_table =	generic_serial_ids,
+#endif
+};
+
+/* There is no MODULE_DEVICE_TABLE for usbserial.c.  Instead
+   the MODULE_DEVICE_TABLE declarations in each serial driver
+   cause the "hotplug" program to pull in whatever module is necessary
+   via modprobe, and modprobe will load usbserial because the serial
+   drivers depend on it.
+*/
+
+static int			serial_refcount;
+static struct tty_driver	serial_tty_driver;
+static struct tty_struct *	serial_tty[SERIAL_TTY_MINORS];
+static struct termios *		serial_termios[SERIAL_TTY_MINORS];
+static struct termios *		serial_termios_locked[SERIAL_TTY_MINORS];
+static struct usb_serial	*serial_table[SERIAL_TTY_MINORS];	/* initially all NULL */
+static LIST_HEAD(usb_serial_driver_list);
+
+
+struct usb_serial *usb_serial_get_by_minor (unsigned int minor)
+{
+	return serial_table[minor];
+}
+
+static struct usb_serial *get_free_serial (struct usb_serial *serial, int num_ports, unsigned int *minor)
+{
+	unsigned int i, j;
+	int good_spot;
+
+	dbg("%s %d", __FUNCTION__, num_ports);
+
+	*minor = 0;
+	for (i = 0; i < SERIAL_TTY_MINORS; ++i) {
+		if (serial_table[i])
+			continue;
+
+		good_spot = 1;
+		for (j = 1; j <= num_ports-1; ++j)
+			if ((serial_table[i+j]) || (i+j >= SERIAL_TTY_MINORS)) {
+				good_spot = 0;
+				i += j;
+				break;
+			}
+		if (good_spot == 0)
+			continue;
+			
+		serial->magic = USB_SERIAL_MAGIC;
+		*minor = i;
+		dbg("%s - minor base = %d", __FUNCTION__, *minor);
+		for (i = *minor; (i < (*minor + num_ports)) && (i < SERIAL_TTY_MINORS); ++i)
+			serial_table[i] = serial;
+		return serial;
+	}
+	return NULL;
+}
+
+static void return_serial (struct usb_serial *serial)
+{
+	int i;
+
+	dbg("%s", __FUNCTION__);
+
+	if (serial == NULL)
+		return;
+
+	for (i = 0; i < serial->num_ports; ++i) {
+		serial_table[serial->minor + i] = NULL;
+	}
+
+	return;
+}
+
+#ifdef USES_EZUSB_FUNCTIONS
+/* EZ-USB Control and Status Register.  Bit 0 controls 8051 reset */
+#define CPUCS_REG    0x7F92
+
+int ezusb_writememory (struct usb_serial *serial, int address, unsigned char *data, int length, __u8 bRequest)
+{
+	int result;
+	unsigned char *transfer_buffer;
+
+	/* dbg("ezusb_writememory %x, %d", address, length); */
+	if (!serial->dev) {
+		dbg("%s - no physical device present, failing.", __FUNCTION__);
+		return -ENODEV;
+	}
+
+	transfer_buffer =  kmalloc (length, GFP_KERNEL);
+	if (!transfer_buffer) {
+		err("%s - kmalloc(%d) failed.", __FUNCTION__, length);
+		return -ENOMEM;
+	}
+	memcpy (transfer_buffer, data, length);
+	result = usb_control_msg (serial->dev, usb_sndctrlpipe(serial->dev, 0), bRequest, 0x40, address, 0, transfer_buffer, length, 3*HZ);
+	kfree (transfer_buffer);
+	return result;
+}
+
+int ezusb_set_reset (struct usb_serial *serial, unsigned char reset_bit)
+{
+	int	response;
+	dbg("%s - %d", __FUNCTION__, reset_bit);
+	response = ezusb_writememory (serial, CPUCS_REG, &reset_bit, 1, 0xa0);
+	if (response < 0) {
+		err("%s- %d failed", __FUNCTION__, reset_bit);
+	}
+	return response;
+}
+
+#endif	/* USES_EZUSB_FUNCTIONS */
+
+/*****************************************************************************
+ * Driver tty interface functions
+ *****************************************************************************/
+static int serial_open (struct tty_struct *tty, struct file * filp)
+{
+	struct usb_serial *serial;
+	struct usb_serial_port *port;
+	unsigned int portNumber;
+	int retval = 0;
+	
+	dbg("%s", __FUNCTION__);
+
+	/* initialize the pointer incase something fails */
+	tty->driver_data = NULL;
+
+	/* get the serial object associated with this tty pointer */
+	serial = usb_serial_get_by_minor (minor(tty->device));
+
+	if (serial_paranoia_check (serial, __FUNCTION__))
+		return -ENODEV;
+
+	/* set up our port structure making the tty driver remember our port object, and us it */
+	portNumber = minor(tty->device) - serial->minor;
+	port = &serial->port[portNumber];
+	tty->driver_data = port;
+
+	down (&port->sem);
+	port->tty = tty;
+	 
+	/* lock this module before we call it */
+	if (serial->type->owner)
+		__MOD_INC_USE_COUNT(serial->type->owner);
+
+	++port->open_count;
+	if (port->open_count == 1) {
+		/* only call the device specific open if this 
+		 * is the first time the port is opened */
+		if (serial->type->open)
+			retval = serial->type->open(port, filp);
+		else
+			retval = usb_serial_generic_open(port, filp);
+		if (retval) {
+			port->open_count = 0;
+			if (serial->type->owner)
+				__MOD_DEC_USE_COUNT(serial->type->owner);
+		}
+	}
+
+	up (&port->sem);
+	return retval;
+}
+
+static void __serial_close(struct usb_serial_port *port, struct file *filp)
+{
+	if (!port->open_count) {
+		dbg ("%s - port not opened", __FUNCTION__);
+		return;
+	}
+
+	--port->open_count;
+	if (port->open_count <= 0) {
+		/* only call the device specific close if this 
+		 * port is being closed by the last owner */
+		if (port->serial->type->close)
+			port->serial->type->close(port, filp);
+		else
+			generic_close(port, filp);
+		port->open_count = 0;
+	}
+
+	if (port->serial->type->owner)
+		__MOD_DEC_USE_COUNT(port->serial->type->owner);
+}
+
+static void serial_close(struct tty_struct *tty, struct file * filp)
+{
+	struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data;
+	struct usb_serial *serial = get_usb_serial (port, __FUNCTION__);
+
+	if (!serial)
+		return;
+
+	down (&port->sem);
+
+	dbg("%s - port %d", __FUNCTION__, port->number);
+
+	/* if disconnect beat us to the punch here, there's nothing to do */
+	if (tty->driver_data) {
+		__serial_close(port, filp);
+	}
+
+	up (&port->sem);
+}
+
+static int serial_write (struct tty_struct * tty, int from_user, const unsigned char *buf, int count)
+{
+	struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data;
+	struct usb_serial *serial = get_usb_serial (port, __FUNCTION__);
+	int retval = -EINVAL;
+
+	if (!serial)
+		return -ENODEV;
+
+	down (&port->sem);
+
+	dbg("%s - port %d, %d byte(s)", __FUNCTION__, port->number, count);
+
+	if (!port->open_count) {
+		dbg("%s - port not opened", __FUNCTION__);
+		goto exit;
+	}
+
+	/* pass on to the driver specific version of this function if it is available */
+	if (serial->type->write)
+		retval = serial->type->write(port, from_user, buf, count);
+	else
+		retval = usb_serial_generic_write(port, from_user, buf, count);
+
+exit:
+	up (&port->sem);
+	return retval;
+}
+
+static int serial_write_room (struct tty_struct *tty) 
+{
+	struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data;
+	struct usb_serial *serial = get_usb_serial (port, __FUNCTION__);
+	int retval = -EINVAL;
+
+	if (!serial)
+		return -ENODEV;
+
+	down (&port->sem);
+
+	dbg("%s - port %d", __FUNCTION__, port->number);
+
+	if (!port->open_count) {
+		dbg("%s - port not open", __FUNCTION__);
+		goto exit;
+	}
+
+	/* pass on to the driver specific version of this function if it is available */
+	if (serial->type->write_room)
+		retval = serial->type->write_room(port);
+	else
+		retval = generic_write_room(port);
+
+exit:
+	up (&port->sem);
+	return retval;
+}
+
+static int serial_chars_in_buffer (struct tty_struct *tty) 
+{
+	struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data;
+	struct usb_serial *serial = get_usb_serial (port, __FUNCTION__);
+	int retval = -EINVAL;
+
+	if (!serial)
+		return -ENODEV;
+
+	down (&port->sem);
+
+	dbg("%s = port %d", __FUNCTION__, port->number);
+
+	if (!port->open_count) {
+		dbg("%s - port not open", __FUNCTION__);
+		goto exit;
+	}
+
+	/* pass on to the driver specific version of this function if it is available */
+	if (serial->type->chars_in_buffer)
+		retval = serial->type->chars_in_buffer(port);
+	else
+		retval = generic_chars_in_buffer(port);
+
+exit:
+	up (&port->sem);
+	return retval;
+}
+
+static void serial_throttle (struct tty_struct * tty)
+{
+	struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data;
+	struct usb_serial *serial = get_usb_serial (port, __FUNCTION__);
+
+	if (!serial)
+		return;
+
+	down (&port->sem);
+
+	dbg("%s - port %d", __FUNCTION__, port->number);
+
+	if (!port->open_count) {
+		dbg ("%s - port not open", __FUNCTION__);
+		goto exit;
+	}
+
+	/* pass on to the driver specific version of this function */
+	if (serial->type->throttle)
+		serial->type->throttle(port);
+
+exit:
+	up (&port->sem);
+}
+
+static void serial_unthrottle (struct tty_struct * tty)
+{
+	struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data;
+	struct usb_serial *serial = get_usb_serial (port, __FUNCTION__);
+
+	if (!serial)
+		return;
+
+	down (&port->sem);
+
+	dbg("%s - port %d", __FUNCTION__, port->number);
+
+	if (!port->open_count) {
+		dbg("%s - port not open", __FUNCTION__);
+		goto exit;
+	}
+
+	/* pass on to the driver specific version of this function */
+	if (serial->type->unthrottle)
+		serial->type->unthrottle(port);
+
+exit:
+	up (&port->sem);
+}
+
+static int serial_ioctl (struct tty_struct *tty, struct file * file, unsigned int cmd, unsigned long arg)
+{
+	struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data;
+	struct usb_serial *serial = get_usb_serial (port, __FUNCTION__);
+	int retval = -ENODEV;
+
+	if (!serial)
+		return -ENODEV;
+
+	down (&port->sem);
+
+	dbg("%s - port %d, cmd 0x%.4x", __FUNCTION__, port->number, cmd);
+
+	if (!port->open_count) {
+		dbg ("%s - port not open", __FUNCTION__);
+		goto exit;
+	}
+
+	/* pass on to the driver specific version of this function if it is available */
+	if (serial->type->ioctl)
+		retval = serial->type->ioctl(port, file, cmd, arg);
+	else
+		retval = -ENOIOCTLCMD;
+
+exit:
+	up (&port->sem);
+	return retval;
+}
+
+static void serial_set_termios (struct tty_struct *tty, struct termios * old)
+{
+	struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data;
+	struct usb_serial *serial = get_usb_serial (port, __FUNCTION__);
+
+	if (!serial)
+		return;
+
+	down (&port->sem);
+
+	dbg("%s - port %d", __FUNCTION__, port->number);
+
+	if (!port->open_count) {
+		dbg("%s - port not open", __FUNCTION__);
+		goto exit;
+	}
+
+	/* pass on to the driver specific version of this function if it is available */
+	if (serial->type->set_termios)
+		serial->type->set_termios(port, old);
+
+exit:
+	up (&port->sem);
+}
+
+static void serial_break (struct tty_struct *tty, int break_state)
+{
+	struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data;
+	struct usb_serial *serial = get_usb_serial (port, __FUNCTION__);
+
+	if (!serial)
+		return;
+
+	down (&port->sem);
+
+	dbg("%s - port %d", __FUNCTION__, port->number);
+
+	if (!port->open_count) {
+		dbg("%s - port not open", __FUNCTION__);
+		goto exit;
+	}
+
+	/* pass on to the driver specific version of this function if it is available */
+	if (serial->type->break_ctl)
+		serial->type->break_ctl(port, break_state);
+
+exit:
+	up (&port->sem);
+}
+
+static void serial_shutdown (struct usb_serial *serial)
+{
+	dbg ("%s", __FUNCTION__);
+
+	if (serial->type->shutdown)
+		serial->type->shutdown(serial);
+	else
+		generic_shutdown(serial);
+}
+
+static int serial_read_proc (char *page, char **start, off_t off, int count, int *eof, void *data)
+{
+	struct usb_serial *serial;
+	int length = 0;
+	int i;
+	off_t begin = 0;
+	char tmp[40];
+
+	dbg("%s", __FUNCTION__);
+	length += sprintf (page, "usbserinfo:1.0 driver:%s\n", DRIVER_VERSION);
+	for (i = 0; i < SERIAL_TTY_MINORS && length < PAGE_SIZE; ++i) {
+		serial = usb_serial_get_by_minor(i);
+		if (serial == NULL)
+			continue;
+
+		length += sprintf (page+length, "%d:", i);
+		if (serial->type->owner)
+			length += sprintf (page+length, " module:%s", serial->type->owner->name);
+		length += sprintf (page+length, " name:\"%s\"", serial->type->name);
+		length += sprintf (page+length, " vendor:%04x product:%04x", serial->vendor, serial->product);
+		length += sprintf (page+length, " num_ports:%d", serial->num_ports);
+		length += sprintf (page+length, " port:%d", i - serial->minor + 1);
+
+		usb_make_path(serial->dev, tmp, sizeof(tmp));
+		length += sprintf (page+length, " path:%s", tmp);
+			
+		length += sprintf (page+length, "\n");
+		if ((length + begin) > (off + count))
+			goto done;
+		if ((length + begin) < off) {
+			begin += length;
+			length = 0;
+		}
+	}
+	*eof = 1;
+done:
+	if (off >= (length + begin))
+		return 0;
+	*start = page + (off-begin);
+	return ((count < begin+length-off) ? count : begin+length-off);
+}
+
+/*****************************************************************************
+ * generic devices specific driver functions
+ *****************************************************************************/
+int usb_serial_generic_open (struct usb_serial_port *port, struct file *filp)
+{
+	struct usb_serial *serial = port->serial;
+	int result = 0;
+
+	if (port_paranoia_check (port, __FUNCTION__))
+		return -ENODEV;
+
+	dbg("%s - port %d", __FUNCTION__, port->number);
+
+	/* force low_latency on so that our tty_push actually forces the data through, 
+	   otherwise it is scheduled, and with high data rates (like with OHCI) data
+	   can get lost. */
+	if (port->tty)
+		port->tty->low_latency = 1;
+
+	/* if we have a bulk interrupt, start reading from it */
+	if (serial->num_bulk_in) {
+		/* Start reading from the device */
+		usb_fill_bulk_urb (port->read_urb, serial->dev,
+				   usb_rcvbulkpipe(serial->dev, port->bulk_in_endpointAddress),
+				   port->read_urb->transfer_buffer,
+				   port->read_urb->transfer_buffer_length,
+				   ((serial->type->read_bulk_callback) ?
+				     serial->type->read_bulk_callback :
+				     generic_read_bulk_callback),
+				   port);
+		result = usb_submit_urb(port->read_urb, GFP_KERNEL);
+		if (result)
+			err("%s - failed resubmitting read urb, error %d", __FUNCTION__, result);
+	}
+
+	return result;
+}
+
+static void generic_cleanup (struct usb_serial_port *port)
+{
+	struct usb_serial *serial = port->serial;
+
+	dbg("%s - port %d", __FUNCTION__, port->number);
+
+	if (serial->dev) {
+		/* shutdown any bulk reads that might be going on */
+		if (serial->num_bulk_out)
+			usb_unlink_urb (port->write_urb);
+		if (serial->num_bulk_in)
+			usb_unlink_urb (port->read_urb);
+	}
+}
+
+static void generic_close (struct usb_serial_port *port, struct file * filp)
+{
+	dbg("%s - port %d", __FUNCTION__, port->number);
+	generic_cleanup (port);
+}
+
+int usb_serial_generic_write (struct usb_serial_port *port, int from_user, const unsigned char *buf, int count)
+{
+	struct usb_serial *serial = port->serial;
+	int result;
+
+	dbg("%s - port %d", __FUNCTION__, port->number);
+
+	if (count == 0) {
+		dbg("%s - write request of 0 bytes", __FUNCTION__);
+		return (0);
+	}
+
+	/* only do something if we have a bulk out endpoint */
+	if (serial->num_bulk_out) {
+		if (port->write_urb->status == -EINPROGRESS) {
+			dbg("%s - already writing", __FUNCTION__);
+			return (0);
+		}
+
+		count = (count > port->bulk_out_size) ? port->bulk_out_size : count;
+
+		if (from_user) {
+			if (copy_from_user(port->write_urb->transfer_buffer, buf, count))
+				return -EFAULT;
+		}
+		else {
+			memcpy (port->write_urb->transfer_buffer, buf, count);
+		}
+
+		usb_serial_debug_data (__FILE__, __FUNCTION__, count, port->write_urb->transfer_buffer);
+
+		/* set up our urb */
+		usb_fill_bulk_urb (port->write_urb, serial->dev,
+				   usb_sndbulkpipe (serial->dev,
+						    port->bulk_out_endpointAddress),
+				   port->write_urb->transfer_buffer, count,
+				   ((serial->type->write_bulk_callback) ? 
+				     serial->type->write_bulk_callback :
+				     generic_write_bulk_callback), port);
+
+		/* send the data out the bulk port */
+		result = usb_submit_urb(port->write_urb, GFP_ATOMIC);
+		if (result)
+			err("%s - failed submitting write urb, error %d", __FUNCTION__, result);
+		else
+			result = count;
+
+		return result;
+	}
+
+	/* no bulk out, so return 0 bytes written */
+	return (0);
+}
+
+static int generic_write_room (struct usb_serial_port *port)
+{
+	struct usb_serial *serial = port->serial;
+	int room = 0;
+
+	dbg("%s - port %d", __FUNCTION__, port->number);
+	
+	if (serial->num_bulk_out) {
+		if (port->write_urb->status != -EINPROGRESS)
+			room = port->bulk_out_size;
+	}
+
+	dbg("%s - returns %d", __FUNCTION__, room);
+	return (room);
+}
+
+static int generic_chars_in_buffer (struct usb_serial_port *port)
+{
+	struct usb_serial *serial = port->serial;
+	int chars = 0;
+
+	dbg("%s - port %d", __FUNCTION__, port->number);
+
+	if (serial->num_bulk_out) {
+		if (port->write_urb->status == -EINPROGRESS)
+			chars = port->write_urb->transfer_buffer_length;
+	}
+
+	dbg("%s - returns %d", __FUNCTION__, chars);
+	return (chars);
+}
+
+static void generic_read_bulk_callback (struct urb *urb)
+{
+	struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
+	struct usb_serial *serial = get_usb_serial (port, __FUNCTION__);
+	struct tty_struct *tty;
+	unsigned char *data = urb->transfer_buffer;
+	int i;
+	int result;
+
+	dbg("%s - port %d", __FUNCTION__, port->number);
+
+	if (!serial) {
+		dbg("%s - bad serial pointer, exiting", __FUNCTION__);
+		return;
+	}
+
+	if (urb->status) {
+		dbg("%s - nonzero read bulk status received: %d", __FUNCTION__, urb->status);
+		return;
+	}
+
+	usb_serial_debug_data (__FILE__, __FUNCTION__, urb->actual_length, data);
+
+	tty = port->tty;
+	if (tty && urb->actual_length) {
+		for (i = 0; i < urb->actual_length ; ++i) {
+			/* if we insert more than TTY_FLIPBUF_SIZE characters, we drop them. */
+			if(tty->flip.count >= TTY_FLIPBUF_SIZE) {
+				tty_flip_buffer_push(tty);
+			}
+			/* this doesn't actually push the data through unless tty->low_latency is set */
+			tty_insert_flip_char(tty, data[i], 0);
+		}
+	  	tty_flip_buffer_push(tty);
+	}
+
+	/* Continue trying to always read  */
+	usb_fill_bulk_urb (port->read_urb, serial->dev,
+			   usb_rcvbulkpipe (serial->dev,
+				   	    port->bulk_in_endpointAddress),
+			   port->read_urb->transfer_buffer,
+			   port->read_urb->transfer_buffer_length,
+			   ((serial->type->read_bulk_callback) ? 
+			     serial->type->read_bulk_callback : 
+			     generic_read_bulk_callback), port);
+	result = usb_submit_urb(port->read_urb, GFP_ATOMIC);
+	if (result)
+		err("%s - failed resubmitting read urb, error %d", __FUNCTION__, result);
+}
+
+static void generic_write_bulk_callback (struct urb *urb)
+{
+	struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
+	struct usb_serial *serial = get_usb_serial (port, __FUNCTION__);
+
+	dbg("%s - port %d", __FUNCTION__, port->number);
+
+	if (!serial) {
+		dbg("%s - bad serial pointer, exiting", __FUNCTION__);
+		return;
+	}
+
+	if (urb->status) {
+		dbg("%s - nonzero write bulk status received: %d", __FUNCTION__, urb->status);
+		return;
+	}
+
+	usb_serial_port_softint((void *)port);
+
+	schedule_work(&port->work);
+}
+
+static void generic_shutdown (struct usb_serial *serial)
+{
+	int i;
+
+	dbg("%s", __FUNCTION__);
+
+	/* stop reads and writes on all ports */
+	for (i=0; i < serial->num_ports; ++i) {
+		generic_cleanup (&serial->port[i]);
+	}
+}
+
+void usb_serial_port_softint(void *private)
+{
+	struct usb_serial_port *port = (struct usb_serial_port *)private;
+	struct usb_serial *serial;
+	struct tty_struct *tty;
+
+	dbg("%s - port %d", __FUNCTION__, port->number);
+	
+	if (!port)
+		return;
+
+	serial = get_usb_serial (port, __FUNCTION__);
+	if (!serial)
+		return;
+
+	tty = port->tty;
+	if (!tty)
+		return;
+
+	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup) {
+		dbg("%s - write wakeup call.", __FUNCTION__);
+		(tty->ldisc.write_wakeup)(tty);
+	}
+
+	wake_up_interruptible(&tty->write_wait);
+}
+
+static struct usb_serial * create_serial (struct usb_device *dev, 
+					  struct usb_interface *interface,
+					  struct usb_serial_device_type *type)
+{
+	struct usb_serial *serial;
+
+	serial = kmalloc (sizeof (*serial), GFP_KERNEL);
+	if (!serial) {
+		err ("%s - out of memory", __FUNCTION__);
+		return NULL;
+	}
+	memset (serial, 0, sizeof(*serial));
+	serial->dev = dev;
+	serial->type = type;
+	serial->interface = interface;
+	serial->vendor = dev->descriptor.idVendor;
+	serial->product = dev->descriptor.idProduct;
+
+	return serial;
+}
+
+int usb_serial_probe(struct usb_interface *interface,
+			       const struct usb_device_id *id)
+{
+	struct usb_device *dev = interface_to_usbdev (interface);
+	struct usb_serial *serial = NULL;
+	struct usb_serial_port *port;
+	struct usb_interface_descriptor *iface_desc;
+	struct usb_endpoint_descriptor *endpoint;
+	struct usb_endpoint_descriptor *interrupt_in_endpoint[MAX_NUM_PORTS];
+	struct usb_endpoint_descriptor *bulk_in_endpoint[MAX_NUM_PORTS];
+	struct usb_endpoint_descriptor *bulk_out_endpoint[MAX_NUM_PORTS];
+	struct usb_serial_device_type *type = NULL;
+	struct list_head *tmp;
+	int retval;
+	int found;
+	int minor;
+	int buffer_size;
+	int i;
+	int num_interrupt_in = 0;
+	int num_bulk_in = 0;
+	int num_bulk_out = 0;
+	int num_ports = 0;
+	int max_endpoints;
+	const struct usb_device_id *id_pattern = NULL;
+
+	/* loop through our list of known serial converters, and see if this
+	   device matches. */
+	found = 0;
+	list_for_each (tmp, &usb_serial_driver_list) {
+		type = list_entry(tmp, struct usb_serial_device_type, driver_list);
+		id_pattern = usb_match_id(interface, type->id_table);
+		if (id_pattern != NULL) {
+			dbg("descriptor matches");
+			found = 1;
+			break;
+		}
+	}
+	if (!found) {
+		/* no match */
+		dbg("none matched");
+		return -ENODEV;
+	}
+
+	serial = create_serial (dev, interface, type);
+	if (!serial) {
+		err ("%s - out of memory", __FUNCTION__);
+		return -ENODEV;
+	}
+
+	/* if this device type has a probe function, call it */
+	if (type->probe) {
+		if (type->owner)
+			__MOD_INC_USE_COUNT(type->owner);
+		retval = type->probe (serial);
+		if (type->owner)
+			__MOD_DEC_USE_COUNT(type->owner);
+
+		if (retval < 0) {
+			dbg ("sub driver rejected device");
+			kfree (serial);
+			return -ENODEV;
+		}
+	}
+
+	/* descriptor matches, let's find the endpoints needed */
+	/* check out the endpoints */
+	iface_desc = &interface->altsetting[0];
+	for (i = 0; i < iface_desc->bNumEndpoints; ++i) {
+		endpoint = &iface_desc->endpoint[i];
+		
+		if ((endpoint->bEndpointAddress & 0x80) &&
+		    ((endpoint->bmAttributes & 3) == 0x02)) {
+			/* we found a bulk in endpoint */
+			dbg("found bulk in");
+			bulk_in_endpoint[num_bulk_in] = endpoint;
+			++num_bulk_in;
+		}
+
+		if (((endpoint->bEndpointAddress & 0x80) == 0x00) &&
+		    ((endpoint->bmAttributes & 3) == 0x02)) {
+			/* we found a bulk out endpoint */
+			dbg("found bulk out");
+			bulk_out_endpoint[num_bulk_out] = endpoint;
+			++num_bulk_out;
+		}
+		
+		if ((endpoint->bEndpointAddress & 0x80) &&
+		    ((endpoint->bmAttributes & 3) == 0x03)) {
+			/* we found a interrupt in endpoint */
+			dbg("found interrupt in");
+			interrupt_in_endpoint[num_interrupt_in] = endpoint;
+			++num_interrupt_in;
+		}
+	}
+
+#if defined(CONFIG_USB_SERIAL_PL2303) || defined(CONFIG_USB_SERIAL_PL2303_MODULE)
+#if 0
+	/* BEGIN HORRIBLE HACK FOR PL2303 */ 
+	/* this is needed due to the looney way its endpoints are set up */
+	if (ifnum == 1) {
+		if (((dev->descriptor.idVendor == PL2303_VENDOR_ID) &&
+		     (dev->descriptor.idProduct == PL2303_PRODUCT_ID)) ||
+		    ((dev->descriptor.idVendor == ATEN_VENDOR_ID) &&
+		     (dev->descriptor.idProduct == ATEN_PRODUCT_ID))) {
+			/* check out the endpoints of the other interface*/
+			interface = &dev->actconfig->interface[ifnum ^ 1];
+			iface_desc = &interface->altsetting[0];
+			for (i = 0; i < iface_desc->bNumEndpoints; ++i) {
+				endpoint = &iface_desc->endpoint[i];
+				if ((endpoint->bEndpointAddress & 0x80) &&
+				    ((endpoint->bmAttributes & 3) == 0x03)) {
+					/* we found a interrupt in endpoint */
+					dbg("found interrupt in for Prolific device on separate interface");
+					interrupt_in_endpoint[num_interrupt_in] = endpoint;
+					++num_interrupt_in;
+				}
+			}
+		}
+	}
+	/* END HORRIBLE HACK FOR PL2303 */
+#endif
+#endif
+
+	/* found all that we need */
+	info("%s converter detected", type->name);
+
+#ifdef CONFIG_USB_SERIAL_GENERIC
+	if (type == &generic_device) {
+		num_ports = num_bulk_out;
+		if (num_ports == 0) {
+			err("Generic device with no bulk out, not allowed.");
+			kfree (serial);
+			return -EIO;
+		}
+	}
+#endif
+	if (!num_ports) {
+		/* if this device type has a calc_num_ports function, call it */
+		if (type->calc_num_ports) {
+			if (type->owner)
+				__MOD_INC_USE_COUNT(type->owner);
+			num_ports = type->calc_num_ports (serial);
+			if (type->owner)
+				__MOD_DEC_USE_COUNT(type->owner);
+		}
+		if (!num_ports)
+			num_ports = type->num_ports;
+	}
+
+	if (get_free_serial (serial, num_ports, &minor) == NULL) {
+		err("No more free serial devices");
+		kfree (serial);
+		return -ENOMEM;
+	}
+
+	serial->minor = minor;
+	serial->num_ports = num_ports;
+	serial->num_bulk_in = num_bulk_in;
+	serial->num_bulk_out = num_bulk_out;
+	serial->num_interrupt_in = num_interrupt_in;
+
+	/* set up the endpoint information */
+	for (i = 0; i < num_bulk_in; ++i) {
+		endpoint = bulk_in_endpoint[i];
+		port = &serial->port[i];
+		port->read_urb = usb_alloc_urb (0, GFP_KERNEL);
+		if (!port->read_urb) {
+			err("No free urbs available");
+			goto probe_error;
+		}
+		buffer_size = endpoint->wMaxPacketSize;
+		port->bulk_in_endpointAddress = endpoint->bEndpointAddress;
+		port->bulk_in_buffer = kmalloc (buffer_size, GFP_KERNEL);
+		if (!port->bulk_in_buffer) {
+			err("Couldn't allocate bulk_in_buffer");
+			goto probe_error;
+		}
+		usb_fill_bulk_urb (port->read_urb, dev,
+				   usb_rcvbulkpipe (dev,
+					   	    endpoint->bEndpointAddress),
+				   port->bulk_in_buffer, buffer_size,
+				   ((serial->type->read_bulk_callback) ? 
+				     serial->type->read_bulk_callback : 
+				     generic_read_bulk_callback),
+				   port);
+	}
+
+	for (i = 0; i < num_bulk_out; ++i) {
+		endpoint = bulk_out_endpoint[i];
+		port = &serial->port[i];
+		port->write_urb = usb_alloc_urb(0, GFP_KERNEL);
+		if (!port->write_urb) {
+			err("No free urbs available");
+			goto probe_error;
+		}
+		buffer_size = endpoint->wMaxPacketSize;
+		port->bulk_out_size = buffer_size;
+		port->bulk_out_endpointAddress = endpoint->bEndpointAddress;
+		port->bulk_out_buffer = kmalloc (buffer_size, GFP_KERNEL);
+		if (!port->bulk_out_buffer) {
+			err("Couldn't allocate bulk_out_buffer");
+			goto probe_error;
+		}
+		usb_fill_bulk_urb (port->write_urb, dev,
+				   usb_sndbulkpipe (dev,
+						    endpoint->bEndpointAddress),
+				   port->bulk_out_buffer, buffer_size, 
+				   ((serial->type->write_bulk_callback) ? 
+				     serial->type->write_bulk_callback : 
+				     generic_write_bulk_callback),
+				   port);
+	}
+
+	for (i = 0; i < num_interrupt_in; ++i) {
+		endpoint = interrupt_in_endpoint[i];
+		port = &serial->port[i];
+		port->interrupt_in_urb = usb_alloc_urb(0, GFP_KERNEL);
+		if (!port->interrupt_in_urb) {
+			err("No free urbs available");
+			goto probe_error;
+		}
+		buffer_size = endpoint->wMaxPacketSize;
+		port->interrupt_in_endpointAddress = endpoint->bEndpointAddress;
+		port->interrupt_in_buffer = kmalloc (buffer_size, GFP_KERNEL);
+		if (!port->interrupt_in_buffer) {
+			err("Couldn't allocate interrupt_in_buffer");
+			goto probe_error;
+		}
+		usb_fill_int_urb (port->interrupt_in_urb, dev, 
+				  usb_rcvintpipe (dev,
+						  endpoint->bEndpointAddress),
+				  port->interrupt_in_buffer, buffer_size, 
+				  serial->type->read_int_callback, port, 
+				  endpoint->bInterval);
+	}
+
+	/* initialize some parts of the port structures */
+	/* we don't use num_ports here cauz some devices have more endpoint pairs than ports */
+	max_endpoints = max(num_bulk_in, num_bulk_out);
+	max_endpoints = max(max_endpoints, num_interrupt_in);
+	max_endpoints = max(max_endpoints, (int)serial->num_ports);
+	dbg("%s - setting up %d port structures for this device", __FUNCTION__, max_endpoints);
+	for (i = 0; i < max_endpoints; ++i) {
+		port = &serial->port[i];
+		port->number = i + serial->minor;
+		port->serial = serial;
+		port->magic = USB_SERIAL_PORT_MAGIC;
+		INIT_WORK(&port->work, usb_serial_port_softint, port);
+		init_MUTEX (&port->sem);
+	}
+
+	/* if this device type has an attach function, call it */
+	if (type->attach) {
+		if (type->owner)
+			__MOD_INC_USE_COUNT(type->owner);
+		retval = type->attach (serial);
+		if (type->owner)
+			__MOD_DEC_USE_COUNT(type->owner);
+		if (retval < 0)
+			goto probe_error;
+		if (retval > 0) {
+			/* quietly accept this device, but don't bind to a serial port
+			 * as it's about to disappear */
+			dev_set_drvdata (&interface->dev, serial);
+			return 0;
+		}
+	}
+
+	/* initialize the devfs nodes for this device and let the user know what ports we are bound to */
+	for (i = 0; i < serial->num_ports; ++i) {
+		tty_register_devfs (&serial_tty_driver, 0, serial->port[i].number);
+		info("%s converter now attached to ttyUSB%d (or usb/tts/%d for devfs)", 
+		     type->name, serial->port[i].number, serial->port[i].number);
+	}
+
+	usb_serial_console_init (debug, minor);
+
+	/* success */
+	dev_set_drvdata (&interface->dev, serial);
+	return 0;
+
+
+probe_error:
+	for (i = 0; i < num_bulk_in; ++i) {
+		port = &serial->port[i];
+		if (port->read_urb)
+			usb_free_urb (port->read_urb);
+		if (port->bulk_in_buffer)
+			kfree (port->bulk_in_buffer);
+	}
+	for (i = 0; i < num_bulk_out; ++i) {
+		port = &serial->port[i];
+		if (port->write_urb)
+			usb_free_urb (port->write_urb);
+		if (port->bulk_out_buffer)
+			kfree (port->bulk_out_buffer);
+	}
+	for (i = 0; i < num_interrupt_in; ++i) {
+		port = &serial->port[i];
+		if (port->interrupt_in_urb)
+			usb_free_urb (port->interrupt_in_urb);
+		if (port->interrupt_in_buffer)
+			kfree (port->interrupt_in_buffer);
+	}
+
+	/* return the minor range that this device had */
+	return_serial (serial);
+
+	/* free up any memory that we allocated */
+	kfree (serial);
+	return -EIO;
+}
+
+void usb_serial_disconnect(struct usb_interface *interface)
+{
+	struct usb_serial *serial = dev_get_drvdata (&interface->dev);
+	struct usb_serial_port *port;
+	int i;
+
+	dbg ("%s", __FUNCTION__);
+
+	dev_set_drvdata (&interface->dev, NULL);
+	if (serial) {
+		/* fail all future close/read/write/ioctl/etc calls */
+		for (i = 0; i < serial->num_ports; ++i) {
+			port = &serial->port[i];
+			down (&port->sem);
+			if (port->tty != NULL) {
+				while (port->open_count > 0) {
+					__serial_close(port, NULL);
+				}
+				port->tty->driver_data = NULL;
+			}
+			up (&port->sem);
+		}
+
+		serial->dev = NULL;
+		serial_shutdown (serial);
+
+		for (i = 0; i < serial->num_ports; ++i)
+			serial->port[i].open_count = 0;
+
+		for (i = 0; i < serial->num_bulk_in; ++i) {
+			port = &serial->port[i];
+			if (port->read_urb) {
+				usb_unlink_urb (port->read_urb);
+				usb_free_urb (port->read_urb);
+			}
+			if (port->bulk_in_buffer)
+				kfree (port->bulk_in_buffer);
+		}
+		for (i = 0; i < serial->num_bulk_out; ++i) {
+			port = &serial->port[i];
+			if (port->write_urb) {
+				usb_unlink_urb (port->write_urb);
+				usb_free_urb (port->write_urb);
+			}
+			if (port->bulk_out_buffer)
+				kfree (port->bulk_out_buffer);
+		}
+		for (i = 0; i < serial->num_interrupt_in; ++i) {
+			port = &serial->port[i];
+			if (port->interrupt_in_urb) {
+				usb_unlink_urb (port->interrupt_in_urb);
+				usb_free_urb (port->interrupt_in_urb);
+			}
+			if (port->interrupt_in_buffer)
+				kfree (port->interrupt_in_buffer);
+		}
+
+		for (i = 0; i < serial->num_ports; ++i) {
+			tty_unregister_devfs (&serial_tty_driver, serial->port[i].number);
+			info("%s converter now disconnected from ttyUSB%d", serial->type->name, serial->port[i].number);
+		}
+
+		/* return the minor range that this device had */
+		return_serial (serial);
+
+		/* free up any memory that we allocated */
+		kfree (serial);
+	}
+	info("device disconnected");
+
+}
+
+
+static struct tty_driver serial_tty_driver = {
+	.magic =		TTY_DRIVER_MAGIC,
+	.driver_name =		"usb-serial",
+#ifndef CONFIG_DEVFS_FS
+	.name =			"ttyUSB",
+#else
+	.name =			"usb/tts/%d",
+#endif
+	.major =		SERIAL_TTY_MAJOR,
+	.minor_start =		0,
+	.num =			SERIAL_TTY_MINORS,
+	.type =			TTY_DRIVER_TYPE_SERIAL,
+	.subtype =		SERIAL_TYPE_NORMAL,
+	.flags =		TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS,
+
+	.refcount =		&serial_refcount,
+	.table =		serial_tty,
+	.termios =		serial_termios,
+	.termios_locked =	serial_termios_locked,
+
+	.open =			serial_open,
+	.close =		serial_close,
+	.write =		serial_write,
+	.write_room =		serial_write_room,
+	.ioctl =		serial_ioctl,
+	.set_termios =		serial_set_termios,
+	.throttle =		serial_throttle,
+	.unthrottle =		serial_unthrottle,
+	.break_ctl =		serial_break,
+	.chars_in_buffer =	serial_chars_in_buffer,
+	.read_proc =		serial_read_proc,
+};
+
+
+static int __init usb_serial_init(void)
+{
+	int i;
+	int result;
+
+	/* Initalize our global data */
+	for (i = 0; i < SERIAL_TTY_MINORS; ++i) {
+		serial_table[i] = NULL;
+	}
+
+	/* register the tty driver */
+	serial_tty_driver.init_termios          = tty_std_termios;
+	serial_tty_driver.init_termios.c_cflag  = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+	if (tty_register_driver (&serial_tty_driver)) {
+		err("%s - failed to register tty driver", __FUNCTION__);
+		return -1;
+	}
+
+	/* register the USB driver */
+	result = usb_register(&usb_serial_driver);
+	if (result < 0) {
+		tty_unregister_driver(&serial_tty_driver);
+		err("usb_register failed for the usb-serial driver. Error number %d", result);
+		return -1;
+	}
+
+#ifdef CONFIG_USB_SERIAL_GENERIC
+	generic_device_ids[0].idVendor = vendor;
+	generic_device_ids[0].idProduct = product;
+	generic_device_ids[0].match_flags = USB_DEVICE_ID_MATCH_VENDOR | USB_DEVICE_ID_MATCH_PRODUCT;
+	/* register our generic driver with ourselves */
+	usb_serial_register (&generic_device);
+#endif
+
+	info(DRIVER_DESC " " DRIVER_VERSION);
+
+	return 0;
+}
+
+
+static void __exit usb_serial_exit(void)
+{
+	usb_serial_console_exit();
+
+#ifdef CONFIG_USB_SERIAL_GENERIC
+	/* remove our generic driver */
+	usb_serial_deregister (&generic_device);
+#endif
+	
+	usb_deregister(&usb_serial_driver);
+	tty_unregister_driver(&serial_tty_driver);
+}
+
+
+module_init(usb_serial_init);
+module_exit(usb_serial_exit);
+
+
+int usb_serial_register(struct usb_serial_device_type *new_device)
+{
+	/* Add this device to our list of devices */
+	list_add(&new_device->driver_list, &usb_serial_driver_list);
+
+	info ("USB Serial support registered for %s", new_device->name);
+
+	return 0;
+}
+
+
+void usb_serial_deregister(struct usb_serial_device_type *device)
+{
+	struct usb_serial *serial;
+	int i;
+
+	info("USB Serial deregistering driver %s", device->name);
+
+	/* clear out the serial_table if the device is attached to a port */
+	for(i = 0; i < SERIAL_TTY_MINORS; ++i) {
+		serial = serial_table[i];
+		if ((serial != NULL) && (serial->type == device)) {
+			usb_driver_release_interface (&usb_serial_driver, serial->interface);
+			usb_serial_disconnect (serial->interface);
+		}
+	}
+
+	list_del(&device->driver_list);
+}
+
+
+
+/* If the usb-serial core is built into the core, the usb-serial drivers
+   need these symbols to load properly as modules. */
+EXPORT_SYMBOL(usb_serial_register);
+EXPORT_SYMBOL(usb_serial_deregister);
+EXPORT_SYMBOL(usb_serial_probe);
+EXPORT_SYMBOL(usb_serial_disconnect);
+EXPORT_SYMBOL(usb_serial_port_softint);
+#ifdef USES_EZUSB_FUNCTIONS
+	EXPORT_SYMBOL(ezusb_writememory);
+	EXPORT_SYMBOL(ezusb_set_reset);
+#endif
+
+
+
+
+/* Module information */
+MODULE_AUTHOR( DRIVER_AUTHOR );
+MODULE_DESCRIPTION( DRIVER_DESC );
+MODULE_LICENSE("GPL");
+
+MODULE_PARM(debug, "i");
+MODULE_PARM_DESC(debug, "Debug enabled or not");
+
+#ifdef CONFIG_USB_SERIAL_GENERIC
+MODULE_PARM(vendor, "h");
+MODULE_PARM_DESC(vendor, "User specified USB idVendor");
+
+MODULE_PARM(product, "h");
+MODULE_PARM_DESC(product, "User specified USB idProduct");
+#endif
diff -Nru a/drivers/usb/serial/usb-serial.h b/drivers/usb/serial/usb-serial.h
--- a/drivers/usb/serial/usb-serial.h	Mon Oct  7 11:25:40 2002
+++ b/drivers/usb/serial/usb-serial.h	Mon Oct  7 11:25:40 2002
@@ -87,7 +87,7 @@
  * @bulk_out_endpointAddress: endpoint address for the bulk out pipe for this
  *	port.
  * @write_wait: a wait_queue_head_t used by the port.
- * @tqueue: task queue for the line discipline waking up.
+ * @work: work queue entry for the line discipline waking up.
  * @open_count: number of times this port has been opened.
  * @sem: struct semaphore used to lock this structure.
  * @private: place to put any driver specific information that is needed.  The
@@ -117,7 +117,7 @@
 	__u8			bulk_out_endpointAddress;
 
 	wait_queue_head_t	write_wait;
-	struct tq_struct	tqueue;
+	struct work_struct	work;
 	int			open_count;
 	struct semaphore	sem;
 	void *			private;
@@ -256,6 +256,19 @@
 extern int ezusb_set_reset (struct usb_serial *serial, unsigned char reset_bit);
 #endif
 
+/* USB Serial console functions */
+#ifdef CONFIG_USB_SERIAL_CONSOLE
+extern void usb_serial_console_init (int debug, int minor);
+extern void usb_serial_console_exit (void);
+#else
+static inline void usb_serial_console_init (int debug, int minor) { }
+static inline void usb_serial_console_exit (void) { }
+#endif
+
+/* Functions needed by the usb serial console code */
+extern struct usb_serial *usb_serial_get_by_minor (unsigned int minor);
+extern int usb_serial_generic_open (struct usb_serial_port *port, struct file *filp);
+extern int usb_serial_generic_write (struct usb_serial_port *port, int from_user, const unsigned char *buf, int count);
 
 /* Inline functions to check the sanity of a pointer that is passed to us */
 static inline int serial_paranoia_check (struct usb_serial *serial, const char *function)
diff -Nru a/drivers/usb/serial/usbserial.c b/drivers/usb/serial/usbserial.c
--- a/drivers/usb/serial/usbserial.c	Mon Oct  7 11:25:32 2002
+++ /dev/null	Wed Dec 31 16:00:00 1969
@@ -1,1951 +0,0 @@
-/*
- * USB Serial Converter driver
- *
- * Copyright (C) 1999 - 2002 Greg Kroah-Hartman (greg@kroah.com)
- * Copyright (c) 2000 Peter Berger (pberger@brimson.com)
- * Copyright (c) 2000 Al Borchers (borchers@steinerpoint.com)
- *
- *	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 driver was originally based on the ACM driver by Armin Fuerst (which was 
- * based on a driver by Brad Keryan)
- *
- * See Documentation/usb/usb-serial.txt for more information on using this driver
- *
- * (06/05/2002) gkh
- *	moved location of startup() call in serial_probe() until after all
- *	of the port information and endpoints are initialized.  This makes
- *	things easier for some drivers.
- *
- * (04/10/2002) gkh
- *	added serial_read_proc function which creates a
- *	/proc/tty/driver/usb-serial file.
- *
- * (03/27/2002) gkh
- *	Got USB serial console code working properly and merged into the main
- *	version of the tree.  Thanks to Randy Dunlap for the initial version
- *	of this code, and for pushing me to finish it up.
- *	The USB serial console works with any usb serial driver device.
- *
- * (03/21/2002) gkh
- *	Moved all manipulation of port->open_count into the core.  Now the
- *	individual driver's open and close functions are called only when the
- *	first open() and last close() is called.  Making the drivers a bit
- *	smaller and simpler.
- *	Fixed a bug if a driver didn't have the owner field set.
- *
- * (02/26/2002) gkh
- *	Moved all locking into the main serial_* functions, instead of having 
- *	the individual drivers have to grab the port semaphore.  This should
- *	reduce races.
- *	Reworked the MOD_INC logic a bit to always increment and decrement, even
- *	if the generic driver is being used.
- *
- * (10/10/2001) gkh
- *	usb_serial_disconnect() now sets the serial->dev pointer is to NULL to
- *	help prevent child drivers from accessing the device since it is now
- *	gone.
- *
- * (09/13/2001) gkh
- *	Moved generic driver initialize after we have registered with the USB
- *	core.  Thanks to Randy Dunlap for pointing this problem out.
- *
- * (07/03/2001) gkh
- *	Fixed module paramater size.  Thanks to John Brockmeyer for the pointer.
- *	Fixed vendor and product getting defined through the MODULE_PARM macro
- *	if the Generic driver wasn't compiled in.
- *	Fixed problem with generic_shutdown() not being called for drivers that
- *	don't have a shutdown() function.
- *
- * (06/06/2001) gkh
- *	added evil hack that is needed for the prolific pl2303 device due to the
- *	crazy way its endpoints are set up.
- *
- * (05/30/2001) gkh
- *	switched from using spinlock to a semaphore, which fixes lots of problems.
- *
- * (04/08/2001) gb
- *	Identify version on module load.
- *
- * 2001_02_05 gkh
- *	Fixed buffer overflows bug with the generic serial driver.  Thanks to
- *	Todd Squires <squirest@ct0.com> for fixing this.
- *
- * (01/10/2001) gkh
- *	Fixed bug where the generic serial adaptor grabbed _any_ device that was
- *	offered to it.
- *
- * (12/12/2000) gkh
- *	Removed MOD_INC and MOD_DEC from poll and disconnect functions, and
- *	moved them to the serial_open and serial_close functions.
- *	Also fixed bug with there not being a MOD_DEC for the generic driver
- *	(thanks to Gary Brubaker for finding this.)
- *
- * (11/29/2000) gkh
- *	Small NULL pointer initialization cleanup which saves a bit of disk image
- *
- * (11/01/2000) Adam J. Richter
- *	instead of using idVendor/idProduct pairs, usb serial drivers
- *	now identify their hardware interest with usb_device_id tables,
- *	which they usually have anyhow for use with MODULE_DEVICE_TABLE.
- *
- * (10/05/2000) gkh
- *	Fixed bug with urb->dev not being set properly, now that the usb
- *	core needs it.
- * 
- * (09/11/2000) gkh
- *	Removed DEBUG #ifdefs with call to usb_serial_debug_data
- *
- * (08/28/2000) gkh
- *	Added port_lock to port structure.
- *	Added locks for SMP safeness to generic driver
- *	Fixed the ability to open a generic device's port more than once.
- *
- * (07/23/2000) gkh
- *	Added bulk_out_endpointAddress to port structure.
- *
- * (07/19/2000) gkh, pberger, and borchers
- *	Modifications to allow usb-serial drivers to be modules.
- *
- * (07/03/2000) gkh
- *	Added more debugging to serial_ioctl call
- * 
- * (06/25/2000) gkh
- *	Changed generic_write_bulk_callback to not call wake_up_interruptible
- *	directly, but to have port_softint do it at a safer time.
- *
- * (06/23/2000) gkh
- *	Cleaned up debugging statements in a quest to find UHCI timeout bug.
- *
- * (05/22/2000) gkh
- *	Changed the makefile, enabling the big CONFIG_USB_SERIAL_SOMTHING to be 
- *	removed from the individual device source files.
- *
- * (05/03/2000) gkh
- *	Added the Digi Acceleport driver from Al Borchers and Peter Berger.
- * 
- * (05/02/2000) gkh
- *	Changed devfs and tty register code to work properly now. This was based on
- *	the ACM driver changes by Vojtech Pavlik.
- *
- * (04/27/2000) Ryan VanderBijl
- * 	Put calls to *_paranoia_checks into one function.
- * 
- * (04/23/2000) gkh
- *	Fixed bug that Randy Dunlap found for Generic devices with no bulk out ports.
- *	Moved when the startup code printed out the devices that are supported.
- *
- * (04/19/2000) gkh
- *	Added driver for ZyXEL omni.net lcd plus ISDN TA
- *	Made startup info message specify which drivers were compiled in.
- *
- * (04/03/2000) gkh
- *	Changed the probe process to remove the module unload races.
- *	Changed where the tty layer gets initialized to have devfs work nicer.
- *	Added initial devfs support.
- *
- * (03/26/2000) gkh
- *	Split driver up into device specific pieces.
- * 
- * (03/19/2000) gkh
- *	Fixed oops that could happen when device was removed while a program
- *	was talking to the device.
- *	Removed the static urbs and now all urbs are created and destroyed
- *	dynamically.
- *	Reworked the internal interface. Now everything is based on the 
- *	usb_serial_port structure instead of the larger usb_serial structure.
- *	This fixes the bug that a multiport device could not have more than
- *	one port open at one time.
- *
- * (03/17/2000) gkh
- *	Added config option for debugging messages.
- *	Added patch for keyspan pda from Brian Warner.
- *
- * (03/06/2000) gkh
- *	Added the keyspan pda code from Brian Warner <warner@lothar.com>
- *	Moved a bunch of the port specific stuff into its own structure. This
- *	is in anticipation of the true multiport devices (there's a bug if you
- *	try to access more than one port of any multiport device right now)
- *
- * (02/21/2000) gkh
- *	Made it so that any serial devices only have to specify which functions
- *	they want to overload from the generic function calls (great, 
- *	inheritance in C, in a driver, just what I wanted...)
- *	Added support for set_termios and ioctl function calls. No drivers take
- *	advantage of this yet.
- *	Removed the #ifdef MODULE, now there is no module specific code.
- *	Cleaned up a few comments in usb-serial.h that were wrong (thanks again
- *	to Miles Lott).
- *	Small fix to get_free_serial.
- *
- * (02/14/2000) gkh
- *	Removed the Belkin and Peracom functionality from the driver due to
- *	the lack of support from the vendor, and me not wanting people to 
- *	accidenatly buy the device, expecting it to work with Linux.
- *	Added read_bulk_callback and write_bulk_callback to the type structure
- *	for the needs of the FTDI and WhiteHEAT driver.
- *	Changed all reverences to FTDI to FTDI_SIO at the request of Bill
- *	Ryder.
- *	Changed the output urb size back to the max endpoint size to make
- *	the ftdi_sio driver have it easier, and due to the fact that it didn't
- *	really increase the speed any.
- *
- * (02/11/2000) gkh
- *	Added VISOR_FUNCTION_CONSOLE to the visor startup function. This was a
- *	patch from Miles Lott (milos@insync.net).
- *	Fixed bug with not restoring the minor range that a device grabs, if
- *	the startup function fails (thanks Miles for finding this).
- *
- * (02/05/2000) gkh
- *	Added initial framework for the Keyspan PDA serial converter so that
- *	Brian Warner has a place to put his code.
- *	Made the ezusb specific functions generic enough that different
- *	devices can use them (whiteheat and keyspan_pda both need them).
- *	Split out a whole bunch of structure and other stuff to a seperate
- *	usb-serial.h file.
- *	Made the Visor connection messages a little more understandable, now
- *	that Miles Lott (milos@insync.net) has gotten the Generic channel to
- *	work. Also made them always show up in the log file.
- * 
- * (01/25/2000) gkh
- *	Added initial framework for FTDI serial converter so that Bill Ryder
- *	has a place to put his code.
- *	Added the vendor specific info from Handspring. Now we can print out
- *	informational debug messages as well as understand what is happening.
- *
- * (01/23/2000) gkh
- *	Fixed problem of crash when trying to open a port that didn't have a
- *	device assigned to it. Made the minor node finding a little smarter,
- *	now it looks to find a continous space for the new device.
- *
- * (01/21/2000) gkh
- *	Fixed bug in visor_startup with patch from Miles Lott (milos@insync.net)
- *	Fixed get_serial_by_minor which was all messed up for multi port 
- *	devices. Fixed multi port problem for generic devices. Now the number
- *	of ports is determined by the number of bulk out endpoints for the
- *	generic device.
- *
- * (01/19/2000) gkh
- *	Removed lots of cruft that was around from the old (pre urb) driver 
- *	interface.
- *	Made the serial_table dynamic. This should save lots of memory when
- *	the number of minor nodes goes up to 256.
- *	Added initial support for devices that have more than one port. 
- *	Added more debugging comments for the Visor, and added a needed 
- *	set_configuration call.
- *
- * (01/17/2000) gkh
- *	Fixed the WhiteHEAT firmware (my processing tool had a bug)
- *	and added new debug loader firmware for it.
- *	Removed the put_char function as it isn't really needed.
- *	Added visor startup commands as found by the Win98 dump.
- * 
- * (01/13/2000) gkh
- *	Fixed the vendor id for the generic driver to the one I meant it to be.
- *
- * (01/12/2000) gkh
- *	Forget the version numbering...that's pretty useless...
- *	Made the driver able to be compiled so that the user can select which
- *	converter they want to use. This allows people who only want the Visor
- *	support to not pay the memory size price of the WhiteHEAT.
- *	Fixed bug where the generic driver (idVendor=0000 and idProduct=0000)
- *	grabbed the root hub. Not good.
- * 
- * version 0.4.0 (01/10/2000) gkh
- *	Added whiteheat.h containing the firmware for the ConnectTech WhiteHEAT
- *	device. Added startup function to allow firmware to be downloaded to
- *	a device if it needs to be.
- *	Added firmware download logic to the WhiteHEAT device.
- *	Started to add #defines to split up the different drivers for potential
- *	configuration option.
- *	
- * version 0.3.1 (12/30/99) gkh
- *      Fixed problems with urb for bulk out.
- *      Added initial support for multiple sets of endpoints. This enables
- *      the Handspring Visor to be attached successfully. Only the first
- *      bulk in / bulk out endpoint pair is being used right now.
- *
- * version 0.3.0 (12/27/99) gkh
- *	Added initial support for the Handspring Visor based on a patch from
- *	Miles Lott (milos@sneety.insync.net)
- *	Cleaned up the code a bunch and converted over to using urbs only.
- *
- * version 0.2.3 (12/21/99) gkh
- *	Added initial support for the Connect Tech WhiteHEAT converter.
- *	Incremented the number of ports in expectation of getting the
- *	WhiteHEAT to work properly (4 ports per connection).
- *	Added notification on insertion and removal of what port the
- *	device is/was connected to (and what kind of device it was).
- *
- * version 0.2.2 (12/16/99) gkh
- *	Changed major number to the new allocated number. We're legal now!
- *
- * version 0.2.1 (12/14/99) gkh
- *	Fixed bug that happens when device node is opened when there isn't a
- *	device attached to it. Thanks to marek@webdesign.no for noticing this.
- *
- * version 0.2.0 (11/10/99) gkh
- *	Split up internals to make it easier to add different types of serial 
- *	converters to the code.
- *	Added a "generic" driver that gets it's vendor and product id
- *	from when the module is loaded. Thanks to David E. Nelson (dnelson@jump.net)
- *	for the idea and sample code (from the usb scanner driver.)
- *	Cleared up any licensing questions by releasing it under the GNU GPL.
- *
- * version 0.1.2 (10/25/99) gkh
- * 	Fixed bug in detecting device.
- *
- * version 0.1.1 (10/05/99) gkh
- * 	Changed the major number to not conflict with anything else.
- *
- * version 0.1 (09/28/99) gkh
- * 	Can recognize the two different devices and start up a read from
- *	device when asked to. Writes also work. No control signals yet, this
- *	all is vendor specific data (i.e. no spec), also no control for
- *	different baud rates or other bit settings.
- *	Currently we are using the same devid as the acm driver. This needs
- *	to change.
- * 
- */
-
-#include <linux/config.h>
-#include <linux/kernel.h>
-#include <linux/errno.h>
-#include <linux/init.h>
-#include <linux/slab.h>
-#include <linux/tty.h>
-#include <linux/tty_driver.h>
-#include <linux/tty_flip.h>
-#include <linux/module.h>
-#include <linux/spinlock.h>
-#include <linux/list.h>
-#include <linux/smp_lock.h>
-#include <asm/uaccess.h>
-#include <linux/usb.h>
-
-#ifdef MODULE
-#undef CONFIG_USB_SERIAL_CONSOLE
-#endif
-#ifdef CONFIG_USB_SERIAL_CONSOLE
-#include <linux/console.h>
-#include <linux/serial.h>
-#endif
-
-#ifdef CONFIG_USB_SERIAL_DEBUG
-	static int debug = 1;
-#else
-	static int debug;
-#endif
-
-#include "usb-serial.h"
-#include "pl2303.h"
-
-/*
- * Version Information
- */
-#define DRIVER_VERSION "v1.6"
-#define DRIVER_AUTHOR "Greg Kroah-Hartman, greg@kroah.com, http://www.kroah.com/linux-usb/"
-#define DRIVER_DESC "USB Serial Driver core"
-
-/* function prototypes for a "generic" type serial converter (no flow control, not all endpoints needed) */
-/* need to always compile these in, as some of the other devices use these functions as their own. */
-/* if a driver does not provide a function pointer, the generic function will be called. */
-static int  generic_open		(struct usb_serial_port *port, struct file *filp);
-static void generic_close		(struct usb_serial_port *port, struct file *filp);
-static int  generic_write		(struct usb_serial_port *port, int from_user, const unsigned char *buf, int count);
-static int  generic_write_room		(struct usb_serial_port *port);
-static int  generic_chars_in_buffer	(struct usb_serial_port *port);
-static void generic_read_bulk_callback	(struct urb *urb);
-static void generic_write_bulk_callback	(struct urb *urb);
-static void generic_shutdown		(struct usb_serial *serial);
-
-
-#ifdef CONFIG_USB_SERIAL_GENERIC
-static __u16	vendor	= 0x05f9;
-static __u16	product	= 0xffff;
-
-static struct usb_device_id generic_device_ids[2]; /* Initially all zeroes. */
-
-/* All of the device info needed for the Generic Serial Converter */
-static struct usb_serial_device_type generic_device = {
-	.owner =		THIS_MODULE,
-	.name =			"Generic",
-	.id_table =		generic_device_ids,
-	.num_interrupt_in =	NUM_DONT_CARE,
-	.num_bulk_in =		NUM_DONT_CARE,
-	.num_bulk_out =		NUM_DONT_CARE,
-	.num_ports =		1,
-	.shutdown =		generic_shutdown,
-};
-
-/* we want to look at all devices, as the vendor/product id can change
- * depending on the command line argument */
-static struct usb_device_id generic_serial_ids[] = {
-	{.driver_info = 42},
-	{}
-};
-#endif
-
-/* Driver structure we register with the USB core */
-static struct usb_driver usb_serial_driver = {
-	.name =		"serial",
-	.probe =	usb_serial_probe,
-	.disconnect =	usb_serial_disconnect,
-#ifdef CONFIG_USB_SERIAL_GENERIC
-	.id_table =	generic_serial_ids,
-#endif
-};
-
-/* There is no MODULE_DEVICE_TABLE for usbserial.c.  Instead
-   the MODULE_DEVICE_TABLE declarations in each serial driver
-   cause the "hotplug" program to pull in whatever module is necessary
-   via modprobe, and modprobe will load usbserial because the serial
-   drivers depend on it.
-*/
-
-static int			serial_refcount;
-static struct tty_driver	serial_tty_driver;
-static struct tty_struct *	serial_tty[SERIAL_TTY_MINORS];
-static struct termios *		serial_termios[SERIAL_TTY_MINORS];
-static struct termios *		serial_termios_locked[SERIAL_TTY_MINORS];
-static struct usb_serial	*serial_table[SERIAL_TTY_MINORS];	/* initially all NULL */
-static LIST_HEAD(usb_serial_driver_list);
-
-#ifdef CONFIG_USB_SERIAL_CONSOLE
-struct usbcons_info {
-	int			magic;
-	int			break_flag;
-	struct usb_serial_port	*port;
-};
-
-static struct usbcons_info usbcons_info;
-static struct console usbcons;
-#endif /* CONFIG_USB_SERIAL_CONSOLE */
-
-static struct usb_serial *get_serial_by_minor (unsigned int minor)
-{
-	return serial_table[minor];
-}
-
-static struct usb_serial *get_free_serial (struct usb_serial *serial, int num_ports, unsigned int *minor)
-{
-	unsigned int i, j;
-	int good_spot;
-
-	dbg("%s %d", __FUNCTION__, num_ports);
-
-	*minor = 0;
-	for (i = 0; i < SERIAL_TTY_MINORS; ++i) {
-		if (serial_table[i])
-			continue;
-
-		good_spot = 1;
-		for (j = 1; j <= num_ports-1; ++j)
-			if ((serial_table[i+j]) || (i+j >= SERIAL_TTY_MINORS)) {
-				good_spot = 0;
-				i += j;
-				break;
-			}
-		if (good_spot == 0)
-			continue;
-			
-		serial->magic = USB_SERIAL_MAGIC;
-		*minor = i;
-		dbg("%s - minor base = %d", __FUNCTION__, *minor);
-		for (i = *minor; (i < (*minor + num_ports)) && (i < SERIAL_TTY_MINORS); ++i)
-			serial_table[i] = serial;
-		return serial;
-	}
-	return NULL;
-}
-
-static void return_serial (struct usb_serial *serial)
-{
-	int i;
-
-	dbg("%s", __FUNCTION__);
-
-	if (serial == NULL)
-		return;
-
-	for (i = 0; i < serial->num_ports; ++i) {
-		serial_table[serial->minor + i] = NULL;
-	}
-
-	return;
-}
-
-#ifdef USES_EZUSB_FUNCTIONS
-/* EZ-USB Control and Status Register.  Bit 0 controls 8051 reset */
-#define CPUCS_REG    0x7F92
-
-int ezusb_writememory (struct usb_serial *serial, int address, unsigned char *data, int length, __u8 bRequest)
-{
-	int result;
-	unsigned char *transfer_buffer;
-
-	/* dbg("ezusb_writememory %x, %d", address, length); */
-	if (!serial->dev) {
-		dbg("%s - no physical device present, failing.", __FUNCTION__);
-		return -ENODEV;
-	}
-
-	transfer_buffer =  kmalloc (length, GFP_KERNEL);
-	if (!transfer_buffer) {
-		err("%s - kmalloc(%d) failed.", __FUNCTION__, length);
-		return -ENOMEM;
-	}
-	memcpy (transfer_buffer, data, length);
-	result = usb_control_msg (serial->dev, usb_sndctrlpipe(serial->dev, 0), bRequest, 0x40, address, 0, transfer_buffer, length, 3*HZ);
-	kfree (transfer_buffer);
-	return result;
-}
-
-int ezusb_set_reset (struct usb_serial *serial, unsigned char reset_bit)
-{
-	int	response;
-	dbg("%s - %d", __FUNCTION__, reset_bit);
-	response = ezusb_writememory (serial, CPUCS_REG, &reset_bit, 1, 0xa0);
-	if (response < 0) {
-		err("%s- %d failed", __FUNCTION__, reset_bit);
-	}
-	return response;
-}
-
-#endif	/* USES_EZUSB_FUNCTIONS */
-
-/*****************************************************************************
- * Driver tty interface functions
- *****************************************************************************/
-static int serial_open (struct tty_struct *tty, struct file * filp)
-{
-	struct usb_serial *serial;
-	struct usb_serial_port *port;
-	unsigned int portNumber;
-	int retval = 0;
-	
-	dbg("%s", __FUNCTION__);
-
-	/* initialize the pointer incase something fails */
-	tty->driver_data = NULL;
-
-	/* get the serial object associated with this tty pointer */
-	serial = get_serial_by_minor (minor(tty->device));
-
-	if (serial_paranoia_check (serial, __FUNCTION__))
-		return -ENODEV;
-
-	/* set up our port structure making the tty driver remember our port object, and us it */
-	portNumber = minor(tty->device) - serial->minor;
-	port = &serial->port[portNumber];
-	tty->driver_data = port;
-
-	down (&port->sem);
-	port->tty = tty;
-	 
-	/* lock this module before we call it */
-	if (serial->type->owner)
-		__MOD_INC_USE_COUNT(serial->type->owner);
-
-	++port->open_count;
-	if (port->open_count == 1) {
-		/* only call the device specific open if this 
-		 * is the first time the port is opened */
-		if (serial->type->open)
-			retval = serial->type->open(port, filp);
-		else
-			retval = generic_open(port, filp);
-		if (retval) {
-			port->open_count = 0;
-			if (serial->type->owner)
-				__MOD_DEC_USE_COUNT(serial->type->owner);
-		}
-	}
-
-	up (&port->sem);
-	return retval;
-}
-
-static void __serial_close(struct usb_serial_port *port, struct file *filp)
-{
-	if (!port->open_count) {
-		dbg ("%s - port not opened", __FUNCTION__);
-		return;
-	}
-
-	--port->open_count;
-	if (port->open_count <= 0) {
-		/* only call the device specific close if this 
-		 * port is being closed by the last owner */
-		if (port->serial->type->close)
-			port->serial->type->close(port, filp);
-		else
-			generic_close(port, filp);
-		port->open_count = 0;
-	}
-
-	if (port->serial->type->owner)
-		__MOD_DEC_USE_COUNT(port->serial->type->owner);
-}
-
-static void serial_close(struct tty_struct *tty, struct file * filp)
-{
-	struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data;
-	struct usb_serial *serial = get_usb_serial (port, __FUNCTION__);
-
-	if (!serial)
-		return;
-
-	down (&port->sem);
-
-	dbg("%s - port %d", __FUNCTION__, port->number);
-
-	/* if disconnect beat us to the punch here, there's nothing to do */
-	if (tty->driver_data) {
-		__serial_close(port, filp);
-	}
-
-	up (&port->sem);
-}
-
-static int serial_write (struct tty_struct * tty, int from_user, const unsigned char *buf, int count)
-{
-	struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data;
-	struct usb_serial *serial = get_usb_serial (port, __FUNCTION__);
-	int retval = -EINVAL;
-
-	if (!serial)
-		return -ENODEV;
-
-	down (&port->sem);
-
-	dbg("%s - port %d, %d byte(s)", __FUNCTION__, port->number, count);
-
-	if (!port->open_count) {
-		dbg("%s - port not opened", __FUNCTION__);
-		goto exit;
-	}
-
-	/* pass on to the driver specific version of this function if it is available */
-	if (serial->type->write)
-		retval = serial->type->write(port, from_user, buf, count);
-	else
-		retval = generic_write(port, from_user, buf, count);
-
-exit:
-	up (&port->sem);
-	return retval;
-}
-
-static int serial_write_room (struct tty_struct *tty) 
-{
-	struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data;
-	struct usb_serial *serial = get_usb_serial (port, __FUNCTION__);
-	int retval = -EINVAL;
-
-	if (!serial)
-		return -ENODEV;
-
-	down (&port->sem);
-
-	dbg("%s - port %d", __FUNCTION__, port->number);
-
-	if (!port->open_count) {
-		dbg("%s - port not open", __FUNCTION__);
-		goto exit;
-	}
-
-	/* pass on to the driver specific version of this function if it is available */
-	if (serial->type->write_room)
-		retval = serial->type->write_room(port);
-	else
-		retval = generic_write_room(port);
-
-exit:
-	up (&port->sem);
-	return retval;
-}
-
-static int serial_chars_in_buffer (struct tty_struct *tty) 
-{
-	struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data;
-	struct usb_serial *serial = get_usb_serial (port, __FUNCTION__);
-	int retval = -EINVAL;
-
-	if (!serial)
-		return -ENODEV;
-
-	down (&port->sem);
-
-	dbg("%s = port %d", __FUNCTION__, port->number);
-
-	if (!port->open_count) {
-		dbg("%s - port not open", __FUNCTION__);
-		goto exit;
-	}
-
-	/* pass on to the driver specific version of this function if it is available */
-	if (serial->type->chars_in_buffer)
-		retval = serial->type->chars_in_buffer(port);
-	else
-		retval = generic_chars_in_buffer(port);
-
-exit:
-	up (&port->sem);
-	return retval;
-}
-
-static void serial_throttle (struct tty_struct * tty)
-{
-	struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data;
-	struct usb_serial *serial = get_usb_serial (port, __FUNCTION__);
-
-	if (!serial)
-		return;
-
-	down (&port->sem);
-
-	dbg("%s - port %d", __FUNCTION__, port->number);
-
-	if (!port->open_count) {
-		dbg ("%s - port not open", __FUNCTION__);
-		goto exit;
-	}
-
-	/* pass on to the driver specific version of this function */
-	if (serial->type->throttle)
-		serial->type->throttle(port);
-
-exit:
-	up (&port->sem);
-}
-
-static void serial_unthrottle (struct tty_struct * tty)
-{
-	struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data;
-	struct usb_serial *serial = get_usb_serial (port, __FUNCTION__);
-
-	if (!serial)
-		return;
-
-	down (&port->sem);
-
-	dbg("%s - port %d", __FUNCTION__, port->number);
-
-	if (!port->open_count) {
-		dbg("%s - port not open", __FUNCTION__);
-		goto exit;
-	}
-
-	/* pass on to the driver specific version of this function */
-	if (serial->type->unthrottle)
-		serial->type->unthrottle(port);
-
-exit:
-	up (&port->sem);
-}
-
-static int serial_ioctl (struct tty_struct *tty, struct file * file, unsigned int cmd, unsigned long arg)
-{
-	struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data;
-	struct usb_serial *serial = get_usb_serial (port, __FUNCTION__);
-	int retval = -ENODEV;
-
-	if (!serial)
-		return -ENODEV;
-
-	down (&port->sem);
-
-	dbg("%s - port %d, cmd 0x%.4x", __FUNCTION__, port->number, cmd);
-
-	if (!port->open_count) {
-		dbg ("%s - port not open", __FUNCTION__);
-		goto exit;
-	}
-
-	/* pass on to the driver specific version of this function if it is available */
-	if (serial->type->ioctl)
-		retval = serial->type->ioctl(port, file, cmd, arg);
-	else
-		retval = -ENOIOCTLCMD;
-
-exit:
-	up (&port->sem);
-	return retval;
-}
-
-static void serial_set_termios (struct tty_struct *tty, struct termios * old)
-{
-	struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data;
-	struct usb_serial *serial = get_usb_serial (port, __FUNCTION__);
-
-	if (!serial)
-		return;
-
-	down (&port->sem);
-
-	dbg("%s - port %d", __FUNCTION__, port->number);
-
-	if (!port->open_count) {
-		dbg("%s - port not open", __FUNCTION__);
-		goto exit;
-	}
-
-	/* pass on to the driver specific version of this function if it is available */
-	if (serial->type->set_termios)
-		serial->type->set_termios(port, old);
-
-exit:
-	up (&port->sem);
-}
-
-static void serial_break (struct tty_struct *tty, int break_state)
-{
-	struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data;
-	struct usb_serial *serial = get_usb_serial (port, __FUNCTION__);
-
-	if (!serial)
-		return;
-
-	down (&port->sem);
-
-	dbg("%s - port %d", __FUNCTION__, port->number);
-
-	if (!port->open_count) {
-		dbg("%s - port not open", __FUNCTION__);
-		goto exit;
-	}
-
-	/* pass on to the driver specific version of this function if it is available */
-	if (serial->type->break_ctl)
-		serial->type->break_ctl(port, break_state);
-
-exit:
-	up (&port->sem);
-}
-
-static void serial_shutdown (struct usb_serial *serial)
-{
-	dbg ("%s", __FUNCTION__);
-
-	if (serial->type->shutdown)
-		serial->type->shutdown(serial);
-	else
-		generic_shutdown(serial);
-}
-
-static int serial_read_proc (char *page, char **start, off_t off, int count, int *eof, void *data)
-{
-	struct usb_serial *serial;
-	int length = 0;
-	int i;
-	off_t begin = 0;
-	char tmp[40];
-
-	dbg("%s", __FUNCTION__);
-	length += sprintf (page, "usbserinfo:1.0 driver:%s\n", DRIVER_VERSION);
-	for (i = 0; i < SERIAL_TTY_MINORS && length < PAGE_SIZE; ++i) {
-		serial = get_serial_by_minor(i);
-		if (serial == NULL)
-			continue;
-
-		length += sprintf (page+length, "%d:", i);
-		if (serial->type->owner)
-			length += sprintf (page+length, " module:%s", serial->type->owner->name);
-		length += sprintf (page+length, " name:\"%s\"", serial->type->name);
-		length += sprintf (page+length, " vendor:%04x product:%04x", serial->vendor, serial->product);
-		length += sprintf (page+length, " num_ports:%d", serial->num_ports);
-		length += sprintf (page+length, " port:%d", i - serial->minor + 1);
-
-		usb_make_path(serial->dev, tmp, sizeof(tmp));
-		length += sprintf (page+length, " path:%s", tmp);
-			
-		length += sprintf (page+length, "\n");
-		if ((length + begin) > (off + count))
-			goto done;
-		if ((length + begin) < off) {
-			begin += length;
-			length = 0;
-		}
-	}
-	*eof = 1;
-done:
-	if (off >= (length + begin))
-		return 0;
-	*start = page + (off-begin);
-	return ((count < begin+length-off) ? count : begin+length-off);
-}
-
-/*****************************************************************************
- * generic devices specific driver functions
- *****************************************************************************/
-static int generic_open (struct usb_serial_port *port, struct file *filp)
-{
-	struct usb_serial *serial = port->serial;
-	int result = 0;
-
-	if (port_paranoia_check (port, __FUNCTION__))
-		return -ENODEV;
-
-	dbg("%s - port %d", __FUNCTION__, port->number);
-
-	/* force low_latency on so that our tty_push actually forces the data through, 
-	   otherwise it is scheduled, and with high data rates (like with OHCI) data
-	   can get lost. */
-	if (port->tty)
-		port->tty->low_latency = 1;
-
-	/* if we have a bulk interrupt, start reading from it */
-	if (serial->num_bulk_in) {
-		/* Start reading from the device */
-		usb_fill_bulk_urb (port->read_urb, serial->dev,
-				   usb_rcvbulkpipe(serial->dev, port->bulk_in_endpointAddress),
-				   port->read_urb->transfer_buffer,
-				   port->read_urb->transfer_buffer_length,
-				   ((serial->type->read_bulk_callback) ?
-				     serial->type->read_bulk_callback :
-				     generic_read_bulk_callback),
-				   port);
-		result = usb_submit_urb(port->read_urb, GFP_KERNEL);
-		if (result)
-			err("%s - failed resubmitting read urb, error %d", __FUNCTION__, result);
-	}
-
-	return result;
-}
-
-static void generic_cleanup (struct usb_serial_port *port)
-{
-	struct usb_serial *serial = port->serial;
-
-	dbg("%s - port %d", __FUNCTION__, port->number);
-
-	if (serial->dev) {
-		/* shutdown any bulk reads that might be going on */
-		if (serial->num_bulk_out)
-			usb_unlink_urb (port->write_urb);
-		if (serial->num_bulk_in)
-			usb_unlink_urb (port->read_urb);
-	}
-}
-
-static void generic_close (struct usb_serial_port *port, struct file * filp)
-{
-	dbg("%s - port %d", __FUNCTION__, port->number);
-	generic_cleanup (port);
-}
-
-static int generic_write (struct usb_serial_port *port, int from_user, const unsigned char *buf, int count)
-{
-	struct usb_serial *serial = port->serial;
-	int result;
-
-	dbg("%s - port %d", __FUNCTION__, port->number);
-
-	if (count == 0) {
-		dbg("%s - write request of 0 bytes", __FUNCTION__);
-		return (0);
-	}
-
-	/* only do something if we have a bulk out endpoint */
-	if (serial->num_bulk_out) {
-		if (port->write_urb->status == -EINPROGRESS) {
-			dbg("%s - already writing", __FUNCTION__);
-			return (0);
-		}
-
-		count = (count > port->bulk_out_size) ? port->bulk_out_size : count;
-
-		if (from_user) {
-			if (copy_from_user(port->write_urb->transfer_buffer, buf, count))
-				return -EFAULT;
-		}
-		else {
-			memcpy (port->write_urb->transfer_buffer, buf, count);
-		}
-
-		usb_serial_debug_data (__FILE__, __FUNCTION__, count, port->write_urb->transfer_buffer);
-
-		/* set up our urb */
-		usb_fill_bulk_urb (port->write_urb, serial->dev,
-				   usb_sndbulkpipe (serial->dev,
-						    port->bulk_out_endpointAddress),
-				   port->write_urb->transfer_buffer, count,
-				   ((serial->type->write_bulk_callback) ? 
-				     serial->type->write_bulk_callback :
-				     generic_write_bulk_callback), port);
-
-		/* send the data out the bulk port */
-		result = usb_submit_urb(port->write_urb, GFP_ATOMIC);
-		if (result)
-			err("%s - failed submitting write urb, error %d", __FUNCTION__, result);
-		else
-			result = count;
-
-		return result;
-	}
-
-	/* no bulk out, so return 0 bytes written */
-	return (0);
-}
-
-static int generic_write_room (struct usb_serial_port *port)
-{
-	struct usb_serial *serial = port->serial;
-	int room = 0;
-
-	dbg("%s - port %d", __FUNCTION__, port->number);
-	
-	if (serial->num_bulk_out) {
-		if (port->write_urb->status != -EINPROGRESS)
-			room = port->bulk_out_size;
-	}
-
-	dbg("%s - returns %d", __FUNCTION__, room);
-	return (room);
-}
-
-static int generic_chars_in_buffer (struct usb_serial_port *port)
-{
-	struct usb_serial *serial = port->serial;
-	int chars = 0;
-
-	dbg("%s - port %d", __FUNCTION__, port->number);
-
-	if (serial->num_bulk_out) {
-		if (port->write_urb->status == -EINPROGRESS)
-			chars = port->write_urb->transfer_buffer_length;
-	}
-
-	dbg("%s - returns %d", __FUNCTION__, chars);
-	return (chars);
-}
-
-static void generic_read_bulk_callback (struct urb *urb)
-{
-	struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
-	struct usb_serial *serial = get_usb_serial (port, __FUNCTION__);
-	struct tty_struct *tty;
-	unsigned char *data = urb->transfer_buffer;
-	int i;
-	int result;
-
-	dbg("%s - port %d", __FUNCTION__, port->number);
-
-	if (!serial) {
-		dbg("%s - bad serial pointer, exiting", __FUNCTION__);
-		return;
-	}
-
-	if (urb->status) {
-		dbg("%s - nonzero read bulk status received: %d", __FUNCTION__, urb->status);
-		return;
-	}
-
-	usb_serial_debug_data (__FILE__, __FUNCTION__, urb->actual_length, data);
-
-	tty = port->tty;
-	if (tty && urb->actual_length) {
-		for (i = 0; i < urb->actual_length ; ++i) {
-			/* if we insert more than TTY_FLIPBUF_SIZE characters, we drop them. */
-			if(tty->flip.count >= TTY_FLIPBUF_SIZE) {
-				tty_flip_buffer_push(tty);
-			}
-			/* this doesn't actually push the data through unless tty->low_latency is set */
-			tty_insert_flip_char(tty, data[i], 0);
-		}
-	  	tty_flip_buffer_push(tty);
-	}
-
-	/* Continue trying to always read  */
-	usb_fill_bulk_urb (port->read_urb, serial->dev,
-			   usb_rcvbulkpipe (serial->dev,
-				   	    port->bulk_in_endpointAddress),
-			   port->read_urb->transfer_buffer,
-			   port->read_urb->transfer_buffer_length,
-			   ((serial->type->read_bulk_callback) ? 
-			     serial->type->read_bulk_callback : 
-			     generic_read_bulk_callback), port);
-	result = usb_submit_urb(port->read_urb, GFP_ATOMIC);
-	if (result)
-		err("%s - failed resubmitting read urb, error %d", __FUNCTION__, result);
-}
-
-static void generic_write_bulk_callback (struct urb *urb)
-{
-	struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
-	struct usb_serial *serial = get_usb_serial (port, __FUNCTION__);
-
-	dbg("%s - port %d", __FUNCTION__, port->number);
-
-	if (!serial) {
-		dbg("%s - bad serial pointer, exiting", __FUNCTION__);
-		return;
-	}
-
-	if (urb->status) {
-		dbg("%s - nonzero write bulk status received: %d", __FUNCTION__, urb->status);
-		return;
-	}
-
-	usb_serial_port_softint((void *)port);
-
-	schedule_task(&port->tqueue);
-}
-
-static void generic_shutdown (struct usb_serial *serial)
-{
-	int i;
-
-	dbg("%s", __FUNCTION__);
-
-	/* stop reads and writes on all ports */
-	for (i=0; i < serial->num_ports; ++i) {
-		generic_cleanup (&serial->port[i]);
-	}
-}
-
-void usb_serial_port_softint(void *private)
-{
-	struct usb_serial_port *port = (struct usb_serial_port *)private;
-	struct usb_serial *serial;
-	struct tty_struct *tty;
-
-	dbg("%s - port %d", __FUNCTION__, port->number);
-	
-	if (!port)
-		return;
-
-	serial = get_usb_serial (port, __FUNCTION__);
-	if (!serial)
-		return;
-
-	tty = port->tty;
-	if (!tty)
-		return;
-
-	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup) {
-		dbg("%s - write wakeup call.", __FUNCTION__);
-		(tty->ldisc.write_wakeup)(tty);
-	}
-
-	wake_up_interruptible(&tty->write_wait);
-}
-
-static struct usb_serial * create_serial (struct usb_device *dev, 
-					  struct usb_interface *interface,
-					  struct usb_serial_device_type *type)
-{
-	struct usb_serial *serial;
-
-	serial = kmalloc (sizeof (*serial), GFP_KERNEL);
-	if (!serial) {
-		err ("%s - out of memory", __FUNCTION__);
-		return NULL;
-	}
-	memset (serial, 0, sizeof(*serial));
-	serial->dev = dev;
-	serial->type = type;
-	serial->interface = interface;
-	serial->vendor = dev->descriptor.idVendor;
-	serial->product = dev->descriptor.idProduct;
-
-	return serial;
-}
-
-int usb_serial_probe(struct usb_interface *interface,
-			       const struct usb_device_id *id)
-{
-	struct usb_device *dev = interface_to_usbdev (interface);
-	struct usb_serial *serial = NULL;
-	struct usb_serial_port *port;
-	struct usb_interface_descriptor *iface_desc;
-	struct usb_endpoint_descriptor *endpoint;
-	struct usb_endpoint_descriptor *interrupt_in_endpoint[MAX_NUM_PORTS];
-	struct usb_endpoint_descriptor *bulk_in_endpoint[MAX_NUM_PORTS];
-	struct usb_endpoint_descriptor *bulk_out_endpoint[MAX_NUM_PORTS];
-	struct usb_serial_device_type *type = NULL;
-	struct list_head *tmp;
-	int retval;
-	int found;
-	int minor;
-	int buffer_size;
-	int i;
-	int num_interrupt_in = 0;
-	int num_bulk_in = 0;
-	int num_bulk_out = 0;
-	int num_ports = 0;
-	int max_endpoints;
-	const struct usb_device_id *id_pattern = NULL;
-
-	/* loop through our list of known serial converters, and see if this
-	   device matches. */
-	found = 0;
-	list_for_each (tmp, &usb_serial_driver_list) {
-		type = list_entry(tmp, struct usb_serial_device_type, driver_list);
-		id_pattern = usb_match_id(interface, type->id_table);
-		if (id_pattern != NULL) {
-			dbg("descriptor matches");
-			found = 1;
-			break;
-		}
-	}
-	if (!found) {
-		/* no match */
-		dbg("none matched");
-		return -ENODEV;
-	}
-
-	serial = create_serial (dev, interface, type);
-	if (!serial) {
-		err ("%s - out of memory", __FUNCTION__);
-		return -ENODEV;
-	}
-
-	/* if this device type has a probe function, call it */
-	if (type->probe) {
-		if (type->owner)
-			__MOD_INC_USE_COUNT(type->owner);
-		retval = type->probe (serial);
-		if (type->owner)
-			__MOD_DEC_USE_COUNT(type->owner);
-
-		if (retval < 0) {
-			dbg ("sub driver rejected device");
-			kfree (serial);
-			return -ENODEV;
-		}
-	}
-
-	/* descriptor matches, let's find the endpoints needed */
-	/* check out the endpoints */
-	iface_desc = &interface->altsetting[0];
-	for (i = 0; i < iface_desc->bNumEndpoints; ++i) {
-		endpoint = &iface_desc->endpoint[i];
-		
-		if ((endpoint->bEndpointAddress & 0x80) &&
-		    ((endpoint->bmAttributes & 3) == 0x02)) {
-			/* we found a bulk in endpoint */
-			dbg("found bulk in");
-			bulk_in_endpoint[num_bulk_in] = endpoint;
-			++num_bulk_in;
-		}
-
-		if (((endpoint->bEndpointAddress & 0x80) == 0x00) &&
-		    ((endpoint->bmAttributes & 3) == 0x02)) {
-			/* we found a bulk out endpoint */
-			dbg("found bulk out");
-			bulk_out_endpoint[num_bulk_out] = endpoint;
-			++num_bulk_out;
-		}
-		
-		if ((endpoint->bEndpointAddress & 0x80) &&
-		    ((endpoint->bmAttributes & 3) == 0x03)) {
-			/* we found a interrupt in endpoint */
-			dbg("found interrupt in");
-			interrupt_in_endpoint[num_interrupt_in] = endpoint;
-			++num_interrupt_in;
-		}
-	}
-
-#if defined(CONFIG_USB_SERIAL_PL2303) || defined(CONFIG_USB_SERIAL_PL2303_MODULE)
-#if 0
-	/* BEGIN HORRIBLE HACK FOR PL2303 */ 
-	/* this is needed due to the looney way its endpoints are set up */
-	if (ifnum == 1) {
-		if (((dev->descriptor.idVendor == PL2303_VENDOR_ID) &&
-		     (dev->descriptor.idProduct == PL2303_PRODUCT_ID)) ||
-		    ((dev->descriptor.idVendor == ATEN_VENDOR_ID) &&
-		     (dev->descriptor.idProduct == ATEN_PRODUCT_ID))) {
-			/* check out the endpoints of the other interface*/
-			interface = &dev->actconfig->interface[ifnum ^ 1];
-			iface_desc = &interface->altsetting[0];
-			for (i = 0; i < iface_desc->bNumEndpoints; ++i) {
-				endpoint = &iface_desc->endpoint[i];
-				if ((endpoint->bEndpointAddress & 0x80) &&
-				    ((endpoint->bmAttributes & 3) == 0x03)) {
-					/* we found a interrupt in endpoint */
-					dbg("found interrupt in for Prolific device on separate interface");
-					interrupt_in_endpoint[num_interrupt_in] = endpoint;
-					++num_interrupt_in;
-				}
-			}
-		}
-	}
-	/* END HORRIBLE HACK FOR PL2303 */
-#endif
-#endif
-
-	/* found all that we need */
-	info("%s converter detected", type->name);
-
-#ifdef CONFIG_USB_SERIAL_GENERIC
-	if (type == &generic_device) {
-		num_ports = num_bulk_out;
-		if (num_ports == 0) {
-			err("Generic device with no bulk out, not allowed.");
-			kfree (serial);
-			return -EIO;
-		}
-	}
-#endif
-	if (!num_ports) {
-		/* if this device type has a calc_num_ports function, call it */
-		if (type->calc_num_ports) {
-			if (type->owner)
-				__MOD_INC_USE_COUNT(type->owner);
-			num_ports = type->calc_num_ports (serial);
-			if (type->owner)
-				__MOD_DEC_USE_COUNT(type->owner);
-		}
-		if (!num_ports)
-			num_ports = type->num_ports;
-	}
-
-	if (get_free_serial (serial, num_ports, &minor) == NULL) {
-		err("No more free serial devices");
-		kfree (serial);
-		return -ENOMEM;
-	}
-
-	serial->minor = minor;
-	serial->num_ports = num_ports;
-	serial->num_bulk_in = num_bulk_in;
-	serial->num_bulk_out = num_bulk_out;
-	serial->num_interrupt_in = num_interrupt_in;
-
-	/* set up the endpoint information */
-	for (i = 0; i < num_bulk_in; ++i) {
-		endpoint = bulk_in_endpoint[i];
-		port = &serial->port[i];
-		port->read_urb = usb_alloc_urb (0, GFP_KERNEL);
-		if (!port->read_urb) {
-			err("No free urbs available");
-			goto probe_error;
-		}
-		buffer_size = endpoint->wMaxPacketSize;
-		port->bulk_in_endpointAddress = endpoint->bEndpointAddress;
-		port->bulk_in_buffer = kmalloc (buffer_size, GFP_KERNEL);
-		if (!port->bulk_in_buffer) {
-			err("Couldn't allocate bulk_in_buffer");
-			goto probe_error;
-		}
-		usb_fill_bulk_urb (port->read_urb, dev,
-				   usb_rcvbulkpipe (dev,
-					   	    endpoint->bEndpointAddress),
-				   port->bulk_in_buffer, buffer_size,
-				   ((serial->type->read_bulk_callback) ? 
-				     serial->type->read_bulk_callback : 
-				     generic_read_bulk_callback),
-				   port);
-	}
-
-	for (i = 0; i < num_bulk_out; ++i) {
-		endpoint = bulk_out_endpoint[i];
-		port = &serial->port[i];
-		port->write_urb = usb_alloc_urb(0, GFP_KERNEL);
-		if (!port->write_urb) {
-			err("No free urbs available");
-			goto probe_error;
-		}
-		buffer_size = endpoint->wMaxPacketSize;
-		port->bulk_out_size = buffer_size;
-		port->bulk_out_endpointAddress = endpoint->bEndpointAddress;
-		port->bulk_out_buffer = kmalloc (buffer_size, GFP_KERNEL);
-		if (!port->bulk_out_buffer) {
-			err("Couldn't allocate bulk_out_buffer");
-			goto probe_error;
-		}
-		usb_fill_bulk_urb (port->write_urb, dev,
-				   usb_sndbulkpipe (dev,
-						    endpoint->bEndpointAddress),
-				   port->bulk_out_buffer, buffer_size, 
-				   ((serial->type->write_bulk_callback) ? 
-				     serial->type->write_bulk_callback : 
-				     generic_write_bulk_callback),
-				   port);
-	}
-
-	for (i = 0; i < num_interrupt_in; ++i) {
-		endpoint = interrupt_in_endpoint[i];
-		port = &serial->port[i];
-		port->interrupt_in_urb = usb_alloc_urb(0, GFP_KERNEL);
-		if (!port->interrupt_in_urb) {
-			err("No free urbs available");
-			goto probe_error;
-		}
-		buffer_size = endpoint->wMaxPacketSize;
-		port->interrupt_in_endpointAddress = endpoint->bEndpointAddress;
-		port->interrupt_in_buffer = kmalloc (buffer_size, GFP_KERNEL);
-		if (!port->interrupt_in_buffer) {
-			err("Couldn't allocate interrupt_in_buffer");
-			goto probe_error;
-		}
-		usb_fill_int_urb (port->interrupt_in_urb, dev, 
-				  usb_rcvintpipe (dev,
-						  endpoint->bEndpointAddress),
-				  port->interrupt_in_buffer, buffer_size, 
-				  serial->type->read_int_callback, port, 
-				  endpoint->bInterval);
-	}
-
-	/* initialize some parts of the port structures */
-	/* we don't use num_ports here cauz some devices have more endpoint pairs than ports */
-	max_endpoints = max(num_bulk_in, num_bulk_out);
-	max_endpoints = max(max_endpoints, num_interrupt_in);
-	max_endpoints = max(max_endpoints, (int)serial->num_ports);
-	dbg("%s - setting up %d port structures for this device", __FUNCTION__, max_endpoints);
-	for (i = 0; i < max_endpoints; ++i) {
-		port = &serial->port[i];
-		port->number = i + serial->minor;
-		port->serial = serial;
-		port->magic = USB_SERIAL_PORT_MAGIC;
-		port->tqueue.routine = usb_serial_port_softint;
-		port->tqueue.data = port;
-		init_MUTEX (&port->sem);
-	}
-
-	/* if this device type has an attach function, call it */
-	if (type->attach) {
-		if (type->owner)
-			__MOD_INC_USE_COUNT(type->owner);
-		retval = type->attach (serial);
-		if (type->owner)
-			__MOD_DEC_USE_COUNT(type->owner);
-		if (retval < 0)
-			goto probe_error;
-		if (retval > 0) {
-			/* quietly accept this device, but don't bind to a serial port
-			 * as it's about to disappear */
-			dev_set_drvdata (&interface->dev, serial);
-			return 0;
-		}
-	}
-
-	/* initialize the devfs nodes for this device and let the user know what ports we are bound to */
-	for (i = 0; i < serial->num_ports; ++i) {
-		tty_register_devfs (&serial_tty_driver, 0, serial->port[i].number);
-		info("%s converter now attached to ttyUSB%d (or usb/tts/%d for devfs)", 
-		     type->name, serial->port[i].number, serial->port[i].number);
-	}
-
-#ifdef CONFIG_USB_SERIAL_CONSOLE
-	if (minor == 0) {
-		/* 
-		 * Call register_console() if this is the first device plugged
-		 * in.  If we call it earlier, then the callback to
-		 * console_setup() will fail, as there is not a device seen by
-		 * the USB subsystem yet.
-		 */
-		/*
-		 * Register console.
-		 * NOTES:
-		 * console_setup() is called (back) immediately (from register_console).
-		 * console_write() is called immediately from register_console iff
-		 * CON_PRINTBUFFER is set in flags.
-		 */
-		dbg ("registering the USB serial console.");
-		register_console(&usbcons);
-	}
-#endif
-
-	/* success */
-	dev_set_drvdata (&interface->dev, serial);
-	return 0;
-
-
-probe_error:
-	for (i = 0; i < num_bulk_in; ++i) {
-		port = &serial->port[i];
-		if (port->read_urb)
-			usb_free_urb (port->read_urb);
-		if (port->bulk_in_buffer)
-			kfree (port->bulk_in_buffer);
-	}
-	for (i = 0; i < num_bulk_out; ++i) {
-		port = &serial->port[i];
-		if (port->write_urb)
-			usb_free_urb (port->write_urb);
-		if (port->bulk_out_buffer)
-			kfree (port->bulk_out_buffer);
-	}
-	for (i = 0; i < num_interrupt_in; ++i) {
-		port = &serial->port[i];
-		if (port->interrupt_in_urb)
-			usb_free_urb (port->interrupt_in_urb);
-		if (port->interrupt_in_buffer)
-			kfree (port->interrupt_in_buffer);
-	}
-
-	/* return the minor range that this device had */
-	return_serial (serial);
-
-	/* free up any memory that we allocated */
-	kfree (serial);
-	return -EIO;
-}
-
-void usb_serial_disconnect(struct usb_interface *interface)
-{
-	struct usb_serial *serial = dev_get_drvdata (&interface->dev);
-	struct usb_serial_port *port;
-	int i;
-
-	dbg ("%s", __FUNCTION__);
-
-	dev_set_drvdata (&interface->dev, NULL);
-	if (serial) {
-		/* fail all future close/read/write/ioctl/etc calls */
-		for (i = 0; i < serial->num_ports; ++i) {
-			port = &serial->port[i];
-			down (&port->sem);
-			if (port->tty != NULL) {
-				while (port->open_count > 0) {
-					__serial_close(port, NULL);
-				}
-				port->tty->driver_data = NULL;
-			}
-			up (&port->sem);
-		}
-
-		serial->dev = NULL;
-		serial_shutdown (serial);
-
-		for (i = 0; i < serial->num_ports; ++i)
-			serial->port[i].open_count = 0;
-
-		for (i = 0; i < serial->num_bulk_in; ++i) {
-			port = &serial->port[i];
-			if (port->read_urb) {
-				usb_unlink_urb (port->read_urb);
-				usb_free_urb (port->read_urb);
-			}
-			if (port->bulk_in_buffer)
-				kfree (port->bulk_in_buffer);
-		}
-		for (i = 0; i < serial->num_bulk_out; ++i) {
-			port = &serial->port[i];
-			if (port->write_urb) {
-				usb_unlink_urb (port->write_urb);
-				usb_free_urb (port->write_urb);
-			}
-			if (port->bulk_out_buffer)
-				kfree (port->bulk_out_buffer);
-		}
-		for (i = 0; i < serial->num_interrupt_in; ++i) {
-			port = &serial->port[i];
-			if (port->interrupt_in_urb) {
-				usb_unlink_urb (port->interrupt_in_urb);
-				usb_free_urb (port->interrupt_in_urb);
-			}
-			if (port->interrupt_in_buffer)
-				kfree (port->interrupt_in_buffer);
-		}
-
-		for (i = 0; i < serial->num_ports; ++i) {
-			tty_unregister_devfs (&serial_tty_driver, serial->port[i].number);
-			info("%s converter now disconnected from ttyUSB%d", serial->type->name, serial->port[i].number);
-		}
-
-		/* return the minor range that this device had */
-		return_serial (serial);
-
-		/* free up any memory that we allocated */
-		kfree (serial);
-	}
-	info("device disconnected");
-
-}
-
-
-static struct tty_driver serial_tty_driver = {
-	.magic =		TTY_DRIVER_MAGIC,
-	.driver_name =		"usb-serial",
-#ifndef CONFIG_DEVFS_FS
-	.name =			"ttyUSB",
-#else
-	.name =			"usb/tts/%d",
-#endif
-	.major =		SERIAL_TTY_MAJOR,
-	.minor_start =		0,
-	.num =			SERIAL_TTY_MINORS,
-	.type =			TTY_DRIVER_TYPE_SERIAL,
-	.subtype =		SERIAL_TYPE_NORMAL,
-	.flags =		TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS,
-
-	.refcount =		&serial_refcount,
-	.table =		serial_tty,
-	.termios =		serial_termios,
-	.termios_locked =	serial_termios_locked,
-
-	.open =			serial_open,
-	.close =		serial_close,
-	.write =		serial_write,
-	.write_room =		serial_write_room,
-	.ioctl =		serial_ioctl,
-	.set_termios =		serial_set_termios,
-	.throttle =		serial_throttle,
-	.unthrottle =		serial_unthrottle,
-	.break_ctl =		serial_break,
-	.chars_in_buffer =	serial_chars_in_buffer,
-	.read_proc =		serial_read_proc,
-};
-
-
-static int __init usb_serial_init(void)
-{
-	int i;
-	int result;
-
-	/* Initalize our global data */
-	for (i = 0; i < SERIAL_TTY_MINORS; ++i) {
-		serial_table[i] = NULL;
-	}
-
-	/* register the tty driver */
-	serial_tty_driver.init_termios          = tty_std_termios;
-	serial_tty_driver.init_termios.c_cflag  = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
-	if (tty_register_driver (&serial_tty_driver)) {
-		err("%s - failed to register tty driver", __FUNCTION__);
-		return -1;
-	}
-
-	/* register the USB driver */
-	result = usb_register(&usb_serial_driver);
-	if (result < 0) {
-		tty_unregister_driver(&serial_tty_driver);
-		err("usb_register failed for the usb-serial driver. Error number %d", result);
-		return -1;
-	}
-
-#ifdef CONFIG_USB_SERIAL_GENERIC
-	generic_device_ids[0].idVendor = vendor;
-	generic_device_ids[0].idProduct = product;
-	generic_device_ids[0].match_flags = USB_DEVICE_ID_MATCH_VENDOR | USB_DEVICE_ID_MATCH_PRODUCT;
-	/* register our generic driver with ourselves */
-	usb_serial_register (&generic_device);
-#endif
-
-	info(DRIVER_DESC " " DRIVER_VERSION);
-
-	return 0;
-}
-
-
-static void __exit usb_serial_exit(void)
-{
-#ifdef CONFIG_USB_SERIAL_CONSOLE
-	unregister_console(&usbcons);
-#endif
-
-#ifdef CONFIG_USB_SERIAL_GENERIC
-	/* remove our generic driver */
-	usb_serial_deregister (&generic_device);
-#endif
-	
-	usb_deregister(&usb_serial_driver);
-	tty_unregister_driver(&serial_tty_driver);
-}
-
-
-module_init(usb_serial_init);
-module_exit(usb_serial_exit);
-
-
-int usb_serial_register(struct usb_serial_device_type *new_device)
-{
-	/* Add this device to our list of devices */
-	list_add(&new_device->driver_list, &usb_serial_driver_list);
-
-	info ("USB Serial support registered for %s", new_device->name);
-
-	return 0;
-}
-
-
-void usb_serial_deregister(struct usb_serial_device_type *device)
-{
-	struct usb_serial *serial;
-	int i;
-
-	info("USB Serial deregistering driver %s", device->name);
-
-	/* clear out the serial_table if the device is attached to a port */
-	for(i = 0; i < SERIAL_TTY_MINORS; ++i) {
-		serial = serial_table[i];
-		if ((serial != NULL) && (serial->type == device)) {
-			usb_driver_release_interface (&usb_serial_driver, serial->interface);
-			usb_serial_disconnect (serial->interface);
-		}
-	}
-
-	list_del(&device->driver_list);
-}
-
-
-
-/* If the usb-serial core is built into the core, the usb-serial drivers
-   need these symbols to load properly as modules. */
-EXPORT_SYMBOL(usb_serial_register);
-EXPORT_SYMBOL(usb_serial_deregister);
-EXPORT_SYMBOL(usb_serial_probe);
-EXPORT_SYMBOL(usb_serial_disconnect);
-EXPORT_SYMBOL(usb_serial_port_softint);
-#ifdef USES_EZUSB_FUNCTIONS
-	EXPORT_SYMBOL(ezusb_writememory);
-	EXPORT_SYMBOL(ezusb_set_reset);
-#endif
-
-
-#ifdef CONFIG_USB_SERIAL_CONSOLE
-/*
- * ------------------------------------------------------------
- * USB Serial console driver
- *
- * Much of the code here is copied from drivers/char/serial.c
- * and implements a phony serial console in the same way that
- * serial.c does so that in case some software queries it,
- * it will get the same results.
- *
- * Things that are different from the way the serial port code
- * does things, is that we call the lower level usb-serial
- * driver code to initialize the device, and we set the initial
- * console speeds based on the command line arguments.
- * ------------------------------------------------------------
- */
-
-#if 0
-static kdev_t usb_console_device(struct console *co)
-{
-	return mk_kdev(SERIAL_TTY_MAJOR, co->index);	/* TBD */
-}
-#endif
-
-/*
- * The parsing of the command line works exactly like the
- * serial.c code, except that the specifier is "ttyUSB" instead
- * of "ttyS".
- */
-static int __init usb_console_setup(struct console *co, char *options)
-{
-	struct usbcons_info *info = &usbcons_info;
-	int baud = 9600;
-	int bits = 8;
-	int parity = 'n';
-	int doflow = 0;
-	int cflag = CREAD | HUPCL | CLOCAL;
-	char *s;
-	struct usb_serial *serial;
-	struct usb_serial_port *port;
-	int retval = 0;
-	struct tty_struct *tty;
-	struct termios *termios;
-
-	dbg ("%s", __FUNCTION__);
-
-	if (options) {
-		baud = simple_strtoul(options, NULL, 10);
-		s = options;
-		while (*s >= '0' && *s <= '9')
-			s++;
-		if (*s)	parity = *s++;
-		if (*s)	bits   = *s++ - '0';
-		if (*s)	doflow = (*s++ == 'r');
-	}
-
-	/* build a cflag setting */
-	switch (baud) {
-		case 1200:
-			cflag |= B1200;
-			break;
-		case 2400:
-			cflag |= B2400;
-			break;
-		case 4800:
-			cflag |= B4800;
-			break;
-		case 19200:
-			cflag |= B19200;
-			break;
-		case 38400:
-			cflag |= B38400;
-			break;
-		case 57600:
-			cflag |= B57600;
-			break;
-		case 115200:
-			cflag |= B115200;
-			break;
-		case 9600:
-		default:
-			cflag |= B9600;
-			/*
-			 * Set this to a sane value to prevent a divide error
-			 */
-			baud  = 9600;
-			break;
-	}
-	switch (bits) {
-		case 7:
-			cflag |= CS7;
-			break;
-		default:
-		case 8:
-			cflag |= CS8;
-			break;
-	}
-	switch (parity) {
-		case 'o': case 'O':
-			cflag |= PARODD;
-			break;
-		case 'e': case 'E':
-			cflag |= PARENB;
-			break;
-	}
-	co->cflag = cflag;
-
-	/* grab the first serial port that happens to be connected */
-	serial = get_serial_by_minor (0);
-	if (serial_paranoia_check (serial, __FUNCTION__)) {
-		/* no device is connected yet, sorry :( */
-		err ("No USB device connected to ttyUSB0");
-		return -ENODEV;
-	}
-
-	port = &serial->port[0];
-	down (&port->sem);
-	port->tty = NULL;
-
-	info->port = port;
-	 
-	++port->open_count;
-	if (port->open_count == 1) {
-		/* only call the device specific open if this 
-		 * is the first time the port is opened */
-		if (serial->type->open)
-			retval = serial->type->open(port, NULL);
-		else
-			retval = generic_open(port, NULL);
-		if (retval)
-			port->open_count = 0;
-	}
-
-	up (&port->sem);
-
-	if (retval) {
-		err ("could not open USB console port");
-		return retval;
-	}
-
-	if (serial->type->set_termios) {
-		/* build up a fake tty structure so that the open call has something
-		 * to look at to get the cflag value */
-		tty = kmalloc (sizeof (*tty), GFP_KERNEL);
-		if (!tty) {
-			err ("no more memory");
-			return -ENOMEM;
-		}
-		termios = kmalloc (sizeof (*termios), GFP_KERNEL);
-		if (!termios) {
-			err ("no more memory");
-			kfree (tty);
-			return -ENOMEM;
-		}
-		memset (tty, 0x00, sizeof(*tty));
-		memset (termios, 0x00, sizeof(*termios));
-		termios->c_cflag = cflag;
-		tty->termios = termios;
-		port->tty = tty;
-
-		/* set up the initial termios settings */
-		serial->type->set_termios(port, NULL);
-		port->tty = NULL;
-		kfree (termios);
-		kfree (tty);
-	}
-
-	return retval;
-}
-
-static void usb_console_write(struct console *co, const char *buf, unsigned count)
-{
-	static struct usbcons_info *info = &usbcons_info;
-	struct usb_serial_port *port = info->port;
-	struct usb_serial *serial = get_usb_serial (port, __FUNCTION__);
-	int retval = -ENODEV;
-
-	if (!serial || !port)
-		return;
-
-	if (count == 0)
-		return;
-
-	down (&port->sem);
-
-	dbg("%s - port %d, %d byte(s)", __FUNCTION__, port->number, count);
-
-	if (!port->open_count) {
-		dbg (__FUNCTION__ " - port not opened");
-		goto exit;
-	}
-
-	/* pass on to the driver specific version of this function if it is available */
-	if (serial->type->write)
-		retval = serial->type->write(port, 0, buf, count);
-	else
-		retval = generic_write(port, 0, buf, count);
-
-exit:
-	up (&port->sem);
-	dbg("%s - return value (if we had one): %d", __FUNCTION__, retval);
-}
-
-#if 0
-/*
- * Receive character from the serial port
- */
-static int usb_console_wait_key(struct console *co)
-{
-	static struct usbcons_info *info = &usbcons_info;
-	int c = 0;
-
-	dbg("%s", __FUNCTION__);
-
-	/* maybe use generic_read_bulk_callback ??? */
-
-	return c;
-}
-#endif
-
-static struct console usbcons = {
-	.name =		"ttyUSB",			/* only [8] */
-	.write =	usb_console_write,
-#if 0
-	.device =	usb_console_device,		/* TBD */
-	.wait_key =	usb_console_wait_key,		/* TBD */
-#endif
-	.setup =	usb_console_setup,
-	.flags =	CON_PRINTBUFFER,
-	.index =	-1,
-};
-
-#endif /* CONFIG_USB_SERIAL_CONSOLE */
-
-
-/* Module information */
-MODULE_AUTHOR( DRIVER_AUTHOR );
-MODULE_DESCRIPTION( DRIVER_DESC );
-MODULE_LICENSE("GPL");
-
-MODULE_PARM(debug, "i");
-MODULE_PARM_DESC(debug, "Debug enabled or not");
-
-#ifdef CONFIG_USB_SERIAL_GENERIC
-MODULE_PARM(vendor, "h");
-MODULE_PARM_DESC(vendor, "User specified USB idVendor");
-
-MODULE_PARM(product, "h");
-MODULE_PARM_DESC(product, "User specified USB idProduct");
-#endif
diff -Nru a/drivers/usb/serial/visor.c b/drivers/usb/serial/visor.c
--- a/drivers/usb/serial/visor.c	Mon Oct  7 11:25:29 2002
+++ b/drivers/usb/serial/visor.c	Mon Oct  7 11:25:29 2002
@@ -460,7 +460,7 @@
 	/* free up the transfer buffer, as usb_free_urb() does not do this */
 	kfree (urb->transfer_buffer);
 
-	schedule_task(&port->tqueue);
+	schedule_work(&port->work);
 }
 
 
diff -Nru a/drivers/usb/serial/whiteheat.c b/drivers/usb/serial/whiteheat.c
--- a/drivers/usb/serial/whiteheat.c	Mon Oct  7 11:25:30 2002
+++ b/drivers/usb/serial/whiteheat.c	Mon Oct  7 11:25:30 2002
@@ -918,7 +918,7 @@
 
 	usb_serial_port_softint((void *)port);
 
-	schedule_task(&port->tqueue);
+	schedule_work(&port->work);
 }
 
 
diff -Nru a/drivers/usb/storage/Makefile b/drivers/usb/storage/Makefile
--- a/drivers/usb/storage/Makefile	Mon Oct  7 11:25:36 2002
+++ b/drivers/usb/storage/Makefile	Mon Oct  7 11:25:36 2002
@@ -5,7 +5,7 @@
 # Rewritten to use lists instead of if-statements.
 #
 
-EXTRA_CFLAGS	:= -I../../scsi/
+EXTRA_CFLAGS	:= -Idrivers/scsi
 
 obj-$(CONFIG_USB_STORAGE)	+= usb-storage.o
 
diff -Nru a/drivers/usb/storage/transport.c b/drivers/usb/storage/transport.c
--- a/drivers/usb/storage/transport.c	Mon Oct  7 11:25:32 2002
+++ b/drivers/usb/storage/transport.c	Mon Oct  7 11:25:32 2002
@@ -350,7 +350,7 @@
 	 */
 	if (len != srb->request_bufflen) {
 		printk(KERN_ERR "USB len=%d, request_bufflen=%d\n", len, srb->request_bufflen);
-		printk(KERN_ERR "Fix this -- future 2.5 kernels will not work if this is not fixed!\n");
+		dump_stack();
 	}
 
 	return len;
diff -Nru a/drivers/usb/usb-skeleton.c b/drivers/usb/usb-skeleton.c
--- a/drivers/usb/usb-skeleton.c	Mon Oct  7 11:25:35 2002
+++ b/drivers/usb/usb-skeleton.c	Mon Oct  7 11:25:35 2002
@@ -117,7 +117,7 @@
 	struct urb *		write_urb;		/* the urb used to send data */
 	__u8			bulk_out_endpointAddr;	/* the address of the bulk out endpoint */
 
-	struct tq_struct	tqueue;			/* task queue for line discipline waking up */
+	struct work_struct			work;			/* work queue entry for line discipline waking up */
 	int			open_count;		/* number of times this port has been opened */
 	struct semaphore	sem;			/* locks this structure */
 };
diff -Nru a/drivers/video/Config.in b/drivers/video/Config.in
--- a/drivers/video/Config.in	Mon Oct  7 11:25:35 2002
+++ b/drivers/video/Config.in	Mon Oct  7 11:25:35 2002
@@ -435,6 +435,7 @@
 	 fi
 	 bool '    Pearl (old m68k) console 8x8 font' CONFIG_FONT_PEARL_8x8
 	 bool '    Acorn console 8x8 font' CONFIG_FONT_ACORN_8x8
+	 bool '    Mini 4x6 font' CONFIG_FONT_MINI_4x6
       else
 	 define_bool CONFIG_FONT_8x8 y
 	 define_bool CONFIG_FONT_8x16 y
diff -Nru a/drivers/video/Makefile b/drivers/video/Makefile
--- a/drivers/video/Makefile	Mon Oct  7 11:25:26 2002
+++ b/drivers/video/Makefile	Mon Oct  7 11:25:26 2002
@@ -28,6 +28,7 @@
 obj-$(CONFIG_FONT_6x11)           += font_6x11.o
 obj-$(CONFIG_FONT_PEARL_8x8)      += font_pearl_8x8.o
 obj-$(CONFIG_FONT_ACORN_8x8)      += font_acorn_8x8.o
+obj-$(CONFIG_FONT_MINI_4x6)       += font_mini_4x6.o
 
 # Add fbmon.o back into obj-$(CONFIG_FB) in 2.5.x
 obj-$(CONFIG_FB)                  += fbmem.o fbcmap.o modedb.o fbcon.o fonts.o fbgen.o
diff -Nru a/drivers/video/font_mini_4x6.c b/drivers/video/font_mini_4x6.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/drivers/video/font_mini_4x6.c	Mon Oct  7 11:25:41 2002
@@ -0,0 +1,2158 @@
+
+/* Hand composed "Miniscule" 4x6 font, with binary data generated using
+ * Perl stub.
+ *
+ * Use 'perl -x mini_4x6.c < mini_4x6.c > new_version.c' to regenerate
+ * binary data.
+ *
+ * Created by Kenneth Albanowski.
+ * No rights reserved, released to the public domain.
+ *
+ * Version 1.0
+ */
+
+/*
+
+#!/usr/bin/perl -pn
+
+s{((0x)?[0-9a-fA-F]+)(.*\[([\*\ ]{4})\])}{
+
+	($num,$pat,$bits) = ($1,$3,$4);
+	
+	$bits =~ s/([^\s0])|(.)/ defined($1) + 0 /ge;
+	
+	$num = ord(pack("B8", $bits));
+	$num |= $num >> 4;
+	$num = sprintf("0x%.2x", $num);
+	
+	#print "$num,$pat,$bits\n";
+	
+	$num . $pat;
+}ge;
+
+__END__;
+*/
+
+/* Note: binary data consists of one byte for each row of each character top
+   to bottom, character 0 to character 255, six bytes per character. Each
+   byte contains the same four character bits in both nybbles.
+   MSBit to LSBit = left to right.
+ */
+
+#include <video/font.h>
+
+#define FONTDATAMAX 1536
+
+static unsigned char fontdata_mini_4x6[FONTDATAMAX] = {
+
+	/*{*/
+	  	/*   Char 0: ' '  */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0x00,	/*=  [    ]       */
+	/*}*/
+	/*{*/
+	  	/*   Char 1: ' '  */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0x00,	/*=  [    ]       */
+	/*}*/
+	/*{*/
+	  	/*   Char 2: ' '  */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0x00,	/*=  [    ]       */
+	/*}*/
+	/*{*/
+	  	/*   Char 3: ' '  */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0x00,	/*=  [    ]       */
+	/*}*/
+	/*{*/
+	  	/*   Char 4: ' '  */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0x00,	/*=  [    ]       */
+	/*}*/
+	/*{*/
+	  	/*   Char 5: ' '  */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0x00,	/*=  [    ]       */
+	/*}*/
+	/*{*/
+	  	/*   Char 6: ' '  */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0x00,	/*=  [    ]       */
+	/*}*/
+	/*{*/
+	  	/*   Char 7: ' '  */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0x00,	/*=  [    ]       */
+	/*}*/
+	/*{*/
+	  	/*   Char 8: ' '  */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0x00,	/*=  [    ]       */
+	/*}*/
+	/*{*/
+	  	/*   Char 9: ' '  */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0x00,	/*=  [    ]       */
+	/*}*/
+	/*{*/
+	  	/*   Char 10: '' */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0x00,	/*=  [    ]       */
+	/*}*/
+	/*{*/
+	  	/*   Char 11: ' ' */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0x00,	/*=  [    ]       */
+	/*}*/
+	/*{*/
+	  	/*   Char 12: ' ' */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0x00,	/*=  [    ]       */
+	/*}*/
+	/*{*/
+	  	/*   Char 13: ' ' */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0x00,	/*=  [    ]       */
+	/*}*/
+	/*{*/
+	  	/*   Char 14: ' ' */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0x00,	/*=  [    ]       */
+	/*}*/
+	/*{*/
+	  	/*   Char 15: ' ' */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0x00,	/*=  [    ]       */
+	/*}*/
+	/*{*/
+	  	/*   Char 16: ' ' */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0x00,	/*=  [    ]       */
+	/*}*/
+	/*{*/
+	  	/*   Char 17: ' ' */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0x00,	/*=  [    ]       */
+	/*}*/
+	/*{*/
+	  	/*   Char 18: ' ' */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0x00,	/*=  [    ]       */
+	/*}*/
+	/*{*/
+	  	/*   Char 19: ' ' */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0x00,	/*=  [    ]       */
+	/*}*/
+	/*{*/
+	  	/*   Char 20: ' ' */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0x00,	/*=  [    ]       */
+	/*}*/
+	/*{*/
+	  	/*   Char 21: ' ' */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0x00,	/*=  [    ]       */
+	/*}*/
+	/*{*/
+	  	/*   Char 22: ' ' */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0x00,	/*=  [    ]       */
+	/*}*/
+	/*{*/
+	  	/*   Char 23: ' ' */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0x00,	/*=  [    ]       */
+	/*}*/
+	/*{*/
+	  	/*   Char 24: ' ' */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0x00,	/*=  [    ]       */
+	/*}*/
+	/*{*/
+	  	/*   Char 25: ' ' */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0x00,	/*=  [    ]       */
+	/*}*/
+	/*{*/
+	  	/*   Char 26: ' ' */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0x00,	/*=  [    ]       */
+	/*}*/
+	/*{*/
+	  	/*   Char 27: ' ' */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0x00,	/*=  [    ]       */
+	/*}*/
+	/*{*/
+	  	/*   Char 28: ' ' */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0x00,	/*=  [    ]       */
+	/*}*/
+	/*{*/
+	  	/*   Char 29: ' ' */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0x00,	/*=  [    ]       */
+	/*}*/
+	/*{*/
+	  	/*   Char 30: ' ' */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0x00,	/*=  [    ]       */
+	/*}*/
+	/*{*/
+	  	/*   Char 31: ' ' */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0x00,	/*=  [    ]       */
+	/*}*/
+	/*{*/
+	  	/*   Char 32: ' ' */
+	0x00,	/*=  [    ]       */
+	0x00,	/*=  [    ]       */
+	0x00,	/*=  [    ]       */
+	0x00,	/*=  [    ]       */
+	0x00,	/*=  [    ]       */
+	0x00,	/*=  [    ]       */
+	/*}*/
+	/*{*/
+	  	/*   Char 33: '!' */
+	0x44,	/*=  [ *  ]       */
+	0x44,	/*=  [ *  ]       */
+	0x44,	/*=  [ *  ]       */
+	0x00,	/*=  [    ]       */
+	0x44,	/*=  [ *  ]       */
+	0x00,	/*=  [    ]       */
+	/*}*/
+	/*{*/
+	  	/*   Char 34: '"' */
+	0xaa,	/*=  [* * ]       */
+	0xaa,	/*=  [* * ]       */
+	0x00,	/*=  [    ]       */
+	0x00,	/*=  [    ]       */
+	0x00,	/*=  [    ]       */
+	0x00,	/*=  [    ]       */
+	/*}*/
+	/*{*/
+	  	/*   Char 35: '#' */
+	0xaa,	/*=  [* * ]       */
+	0xff,	/*=  [****]       */
+	0xff,	/*=  [****]       */
+	0xaa,	/*=  [* * ]       */
+	0x00,	/*=  [    ]       */
+	0x00,	/*=  [    ]       */
+	/*}*/
+	/*{*/
+	  	/*   Char 36: '$' */
+	0x44,	/*=  [ *  ]       */
+	0x66,	/*=  [ ** ]       */
+	0xee,	/*=  [*** ]       */
+	0xcc,	/*=  [**  ]       */
+	0x44,	/*=  [ *  ]       */
+	0x00,	/*=  [    ]       */
+	/*}*/
+	/*{*/
+	  	/*   Char 37: '%' */
+	0xaa,	/*=  [* * ]       */
+	0x22,	/*=  [  * ]       */
+	0x44,	/*=  [ *  ]       */
+	0x88,	/*=  [*   ]       */
+	0xaa,	/*=  [* * ]       */
+	0x00,	/*=  [    ]       */
+	/*}*/
+	/*{*/
+	  	/*   Char 38: '&' */
+	0x66,	/*=  [ ** ]       */
+	0x99,	/*=  [*  *]       */
+	0x66,	/*=  [ ** ]       */
+	0xaa,	/*=  [* * ]       */
+	0xdd,	/*=  [** *]       */
+	0x00,	/*=  [    ]       */
+	/*}*/
+	/*{*/
+	  	/*   Char 39: ''' */
+	0x22,	/*=  [  * ]       */
+	0x44,	/*=  [ *  ]       */
+	0x00,	/*=  [    ]       */
+	0x00,	/*=  [    ]       */
+	0x00,	/*=  [    ]       */
+	0x00,	/*=  [    ]       */
+	/*}*/
+	/*{*/
+	  	/*   Char 40: '(' */
+	0x22,	/*=  [  * ]       */
+	0x44,	/*=  [ *  ]       */
+	0x44,	/*=  [ *  ]       */
+	0x44,	/*=  [ *  ]       */
+	0x22,	/*=  [  * ]       */
+	0x00,	/*=  [    ]       */
+	/*}*/
+	/*{*/
+	  	/*   Char 41: ')' */
+	0x44,	/*=  [ *  ]       */
+	0x22,	/*=  [  * ]       */
+	0x22,	/*=  [  * ]       */
+	0x22,	/*=  [  * ]       */
+	0x44,	/*=  [ *  ]       */
+	0x00,	/*=  [    ]       */
+	/*}*/
+	/*{*/
+	  	/*   Char 42: '*' */
+	0x00,	/*=  [    ]       */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0xee,	/*=  [*** ]       */
+	0x00,	/*=  [    ]       */
+	0x00,	/*=  [    ]       */
+	/*}*/
+	/*{*/
+	  	/*   Char 43: '+' */
+	0x00,	/*=  [    ]       */
+	0x44,	/*=  [ *  ]       */
+	0xee,	/*=  [*** ]       */
+	0x44,	/*=  [ *  ]       */
+	0x00,	/*=  [    ]       */
+	0x00,	/*=  [    ]       */
+	/*}*/
+	/*{*/
+	  	/*   Char 44: ',' */
+	0x00,	/*=  [    ]       */
+	0x00,	/*=  [    ]       */
+	0x00,	/*=  [    ]       */
+	0x44,	/*=  [ *  ]       */
+	0x88,	/*=  [*   ]       */
+	0x00,	/*=  [    ]       */
+	/*}*/
+	/*{*/
+	  	/*   Char 45: '-' */
+	0x00,	/*=  [    ]       */
+	0x00,	/*=  [    ]       */
+	0xee,	/*=  [*** ]       */
+	0x00,	/*=  [    ]       */
+	0x00,	/*=  [    ]       */
+	0x00,	/*=  [    ]       */
+	/*}*/
+	/*{*/
+	  	/*   Char 46: '.' */
+	0x00,	/*=  [    ]       */
+	0x00,	/*=  [    ]       */
+	0x00,	/*=  [    ]       */
+	0x00,	/*=  [    ]       */
+	0x44,	/*=  [ *  ]       */
+	0x00,	/*=  [    ]       */
+	/*}*/
+	/*{*/
+	  	/*   Char 47: '/' */
+	0x00,	/*=  [    ]       */
+	0x22,	/*=  [  * ]       */
+	0x44,	/*=  [ *  ]       */
+	0x88,	/*=  [*   ]       */
+	0x00,	/*=  [    ]       */
+	0x00,	/*=  [    ]       */
+	/*}*/
+	/*{*/
+	  	/*   Char 48: '0'   */
+	0x44,	/*=   [ *  ]        */
+	0xaa,	/*=   [* * ]        */
+	0xaa,	/*=   [* * ]        */
+	0xaa,	/*=   [* * ]        */
+	0x44,	/*=   [ *  ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/
+	  	/*   Char 49: '1'   */
+	0x44,	/*=   [ *  ]        */
+	0xcc,	/*=   [**  ]        */
+	0x44,	/*=   [ *  ]        */
+	0x44,	/*=   [ *  ]        */
+	0xee,	/*=   [*** ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/
+	  	/*   Char 50: '2'   */
+	0xcc,	/*=   [**  ]        */
+	0x22,	/*=   [  * ]        */
+	0x44,	/*=   [ *  ]        */
+	0x88,	/*=   [*   ]        */
+	0xee,	/*=   [*** ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/
+	  	/*   Char 51: '3'   */
+	0xee,	/*=   [*** ]        */
+	0x22,	/*=   [  * ]        */
+	0x66,	/*=   [ ** ]        */
+	0x22,	/*=   [  * ]        */
+	0xee,	/*=   [*** ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 52: '4'   */
+	0xaa,	/*=   [* * ]        */
+	0xaa,	/*=   [* * ]        */
+	0xee,	/*=   [*** ]        */
+	0x22,	/*=   [  * ]        */
+	0x22,	/*=   [  * ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 53: '5'   */
+	0xee,	/*=   [*** ]        */
+	0x88,	/*=   [*   ]        */
+	0xee,	/*=   [*** ]        */
+	0x22,	/*=   [  * ]        */
+	0xee,	/*=   [*** ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 54: '6'   */
+	0xee,	/*=   [*** ]        */
+	0x88,	/*=   [*   ]        */
+	0xee,	/*=   [*** ]        */
+	0xaa,	/*=   [* * ]        */
+	0xee,	/*=   [*** ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 55: '7'   */
+	0xee,	/*=   [*** ]        */
+	0x22,	/*=   [  * ]        */
+	0x22,	/*=   [  * ]        */
+	0x22,	/*=   [  * ]        */
+	0x22,	/*=   [  * ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 56: '8'   */
+	0xee,	/*=   [*** ]        */
+	0xaa,	/*=   [* * ]        */
+	0xee,	/*=   [*** ]        */
+	0xaa,	/*=   [* * ]        */
+	0xee,	/*=   [*** ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 57: '9'   */
+	0xee,	/*=   [*** ]        */
+	0xaa,	/*=   [* * ]        */
+	0xee,	/*=   [*** ]        */
+	0x22,	/*=   [  * ]        */
+	0x22,	/*=   [  * ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 58: ':'   */
+	0x00,	/*=   [    ]        */
+	0x00,	/*=   [    ]        */
+	0x44,	/*=   [ *  ]        */
+	0x00,	/*=   [    ]        */
+	0x44,	/*=   [ *  ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 59: ';'   */
+	0x00,	/*=   [    ]        */
+	0x00,	/*=   [    ]        */
+	0x44,	/*=   [ *  ]        */
+	0x00,	/*=   [    ]        */
+	0x44,	/*=   [ *  ]        */
+	0x88,	/*=   [*   ]        */
+	/*}*/
+	/*{*/ 	/*   Char 60: '<'   */
+	0x22,	/*=   [  * ]        */
+	0x44,	/*=   [ *  ]        */
+	0x88,	/*=   [*   ]        */
+	0x44,	/*=   [ *  ]        */
+	0x22,	/*=   [  * ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 61: '='   */
+	0x00,	/*=   [    ]        */
+	0xee,	/*=   [*** ]        */
+	0x00,	/*=   [    ]        */
+	0xee,	/*=   [*** ]        */
+	0x00,	/*=   [    ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 62: '>'   */
+	0x88,	/*=   [*   ]        */
+	0x44,	/*=   [ *  ]        */
+	0x22,	/*=   [  * ]        */
+	0x44,	/*=   [ *  ]        */
+	0x88,	/*=   [*   ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 63: '?'   */
+	0xee,	/*=   [*** ]        */
+	0x22,	/*=   [  * ]        */
+	0x66,	/*=   [ ** ]        */
+	0x00,	/*=   [    ]        */
+	0x44,	/*=   [ *  ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 64: '@'   */
+	0x44,	/*=   [ *  ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0x88,	/*=   [*   ]        */
+	0x44,	/*=   [ *  ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 65: 'A'   */
+	0x44,	/*=   [ *  ]        */
+	0xaa,	/*=   [* * ]        */
+	0xee,	/*=   [*** ]        */
+	0xaa,	/*=   [* * ]        */
+	0xaa,	/*=   [* * ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 66: 'B'   */
+	0xcc,	/*=   [**  ]        */
+	0xaa,	/*=   [* * ]        */
+	0xcc,	/*=   [**  ]        */
+	0xaa,	/*=   [* * ]        */
+	0xcc,	/*=   [**  ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 67: 'C'   */
+	0x66,	/*=   [ ** ]        */
+	0x88,	/*=   [*   ]        */
+	0x88,	/*=   [*   ]        */
+	0x88,	/*=   [*   ]        */
+	0x66,	/*=   [ ** ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 68: 'D'   */
+	0xcc,	/*=   [**  ]        */
+	0xaa,	/*=   [* * ]        */
+	0xaa,	/*=   [* * ]        */
+	0xaa,	/*=   [* * ]        */
+	0xcc,	/*=   [**  ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 69: 'E'   */
+	0xee,	/*=   [*** ]        */
+	0x88,	/*=   [*   ]        */
+	0xee,	/*=   [*** ]        */
+	0x88,	/*=   [*   ]        */
+	0xee,	/*=   [*** ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 70: 'F'   */
+	0xee,	/*=   [*** ]        */
+	0x88,	/*=   [*   ]        */
+	0xee,	/*=   [*** ]        */
+	0x88,	/*=   [*   ]        */
+	0x88,	/*=   [*   ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 71: 'G'   */
+	0x66,	/*=   [ ** ]        */
+	0x88,	/*=   [*   ]        */
+	0xee,	/*=   [*** ]        */
+	0xaa,	/*=   [* * ]        */
+	0x66,	/*=   [ ** ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 72: 'H'   */
+	0xaa,	/*=   [* * ]        */
+	0xaa,	/*=   [* * ]        */
+	0xee,	/*=   [*** ]        */
+	0xaa,	/*=   [* * ]        */
+	0xaa,	/*=   [* * ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 73: 'I'   */
+	0xee,	/*=   [*** ]        */
+	0x44,	/*=   [ *  ]        */
+	0x44,	/*=   [ *  ]        */
+	0x44,	/*=   [ *  ]        */
+	0xee,	/*=   [*** ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 74: 'J'   */
+	0x22,	/*=   [  * ]        */
+	0x22,	/*=   [  * ]        */
+	0x22,	/*=   [  * ]        */
+	0xaa,	/*=   [* * ]        */
+	0x44,	/*=   [ *  ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 75: 'K'   */
+	0xaa,	/*=   [* * ]        */
+	0xaa,	/*=   [* * ]        */
+	0xcc,	/*=   [**  ]        */
+	0xaa,	/*=   [* * ]        */
+	0xaa,	/*=   [* * ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 76: 'L'   */
+	0x88,	/*=   [*   ]        */
+	0x88,	/*=   [*   ]        */
+	0x88,	/*=   [*   ]        */
+	0x88,	/*=   [*   ]        */
+	0xee,	/*=   [*** ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 77: 'M'   */
+	0xaa,	/*=   [* * ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xaa,	/*=   [* * ]        */
+	0xaa,	/*=   [* * ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 78: 'N'   */
+	0xaa,	/*=   [* * ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xaa,	/*=   [* * ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 79: 'O'   */
+	0x44,	/*=   [ *  ]        */
+	0xaa,	/*=   [* * ]        */
+	0xaa,	/*=   [* * ]        */
+	0xaa,	/*=   [* * ]        */
+	0x44,	/*=   [ *  ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 80: 'P'   */
+	0xcc,	/*=   [**  ]        */
+	0xaa,	/*=   [* * ]        */
+	0xcc,	/*=   [**  ]        */
+	0x88,	/*=   [*   ]        */
+	0x88,	/*=   [*   ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 81: 'Q'   */
+	0x44,	/*=   [ *  ]        */
+	0xaa,	/*=   [* * ]        */
+	0xaa,	/*=   [* * ]        */
+	0xee,	/*=   [*** ]        */
+	0x66,	/*=   [ ** ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 82: 'R'   */
+	0xcc,	/*=   [**  ]        */
+	0xaa,	/*=   [* * ]        */
+	0xee,	/*=   [*** ]        */
+	0xcc,	/*=   [**  ]        */
+	0xaa,	/*=   [* * ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 83: 'S'   */
+	0x66,	/*=   [ ** ]        */
+	0x88,	/*=   [*   ]        */
+	0x44,	/*=   [ *  ]        */
+	0x22,	/*=   [  * ]        */
+	0xcc,	/*=   [**  ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 84: 'T'   */
+	0xee,	/*=   [*** ]        */
+	0x44,	/*=   [ *  ]        */
+	0x44,	/*=   [ *  ]        */
+	0x44,	/*=   [ *  ]        */
+	0x44,	/*=   [ *  ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 85: 'U'   */
+	0xaa,	/*=   [* * ]        */
+	0xaa,	/*=   [* * ]        */
+	0xaa,	/*=   [* * ]        */
+	0xaa,	/*=   [* * ]        */
+	0x66,	/*=   [ ** ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 86: 'V'   */
+	0xaa,	/*=   [* * ]        */
+	0xaa,	/*=   [* * ]        */
+	0xaa,	/*=   [* * ]        */
+	0x44,	/*=   [ *  ]        */
+	0x44,	/*=   [ *  ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 87: 'W'   */
+	0xaa,	/*=   [* * ]        */
+	0xaa,	/*=   [* * ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xaa,	/*=   [* * ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 88: 'X'   */
+	0xaa,	/*=   [* * ]        */
+	0xaa,	/*=   [* * ]        */
+	0x44,	/*=   [ *  ]        */
+	0xaa,	/*=   [* * ]        */
+	0xaa,	/*=   [* * ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 89: 'Y'   */
+	0xaa,	/*=   [* * ]        */
+	0xaa,	/*=   [* * ]        */
+	0x44,	/*=   [ *  ]        */
+	0x44,	/*=   [ *  ]        */
+	0x44,	/*=   [ *  ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 90: 'Z'   */
+	0xee,	/*=   [*** ]        */
+	0x22,	/*=   [  * ]        */
+	0x44,	/*=   [ *  ]        */
+	0x88,	/*=   [*   ]        */
+	0xee,	/*=   [*** ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 91: '['   */
+	0x66,	/*=   [ ** ]        */
+	0x44,	/*=   [ *  ]        */
+	0x44,	/*=   [ *  ]        */
+	0x44,	/*=   [ *  ]        */
+	0x66,	/*=   [ ** ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 92: '\'   */
+	0x00,	/*=   [    ]        */
+	0x88,	/*=   [*   ]        */
+	0x44,	/*=   [ *  ]        */
+	0x22,	/*=   [  * ]        */
+	0x00,	/*=   [    ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 93: ']'   */
+	0x66,	/*=   [ ** ]        */
+	0x22,	/*=   [  * ]        */
+	0x22,	/*=   [  * ]        */
+	0x22,	/*=   [  * ]        */
+	0x66,	/*=   [ ** ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 94: '^'   */
+	0x44,	/*=   [ *  ]        */
+	0xaa,	/*=   [* * ]        */
+	0x00,	/*=   [    ]        */
+	0x00,	/*=   [    ]        */
+	0x00,	/*=   [    ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 95: '_'   */
+	0x00,	/*=   [    ]        */
+	0x00,	/*=   [    ]        */
+	0x00,	/*=   [    ]        */
+	0x00,	/*=   [    ]        */
+	0x00,	/*=   [    ]        */
+	0xff,	/*=   [****]        */
+	/*}*/
+	/*{*/ 	/*   Char 96: '`'   */
+	0x88,	/*=   [*   ]        */
+	0x44,	/*=   [ *  ]        */
+	0x00,	/*=   [    ]        */
+	0x00,	/*=   [    ]        */
+	0x00,	/*=   [    ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 97: 'a'   */
+	0x00,	/*=   [    ]        */
+	0x00,	/*=   [    ]        */
+	0x66,	/*=   [ ** ]        */
+	0xaa,	/*=   [* * ]        */
+	0xee,	/*=   [*** ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 98: 'b'   */
+	0x88,	/*=   [*   ]        */
+	0x88,	/*=   [*   ]        */
+	0xcc,	/*=   [**  ]        */
+	0xaa,	/*=   [* * ]        */
+	0xcc,	/*=   [**  ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 99: 'c'   */
+	0x00,	/*=   [    ]        */
+	0x00,	/*=   [    ]        */
+	0x66,	/*=   [ ** ]        */
+	0x88,	/*=   [*   ]        */
+	0x66,	/*=   [ ** ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 100: 'd'   */
+	0x22,	/*=   [  * ]        */
+	0x22,	/*=   [  * ]        */
+	0x66,	/*=   [ ** ]        */
+	0xaa,	/*=   [* * ]        */
+	0x66,	/*=   [ ** ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 101: 'e'   */
+	0x00,	/*=   [    ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0x88,	/*=   [*   ]        */
+	0x66,	/*=   [ ** ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 102: 'f'   */
+	0x22,	/*=   [  * ]        */
+	0x44,	/*=   [ *  ]        */
+	0xee,	/*=   [*** ]        */
+	0x44,	/*=   [ *  ]        */
+	0x44,	/*=   [ *  ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 103: 'g'   */
+	0x00,	/*=   [    ]        */
+	0x66,	/*=   [ ** ]        */
+	0xaa,	/*=   [* * ]        */
+	0x66,	/*=   [ ** ]        */
+	0xee,	/*=   [*** ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 104: 'h'   */
+	0x88,	/*=   [*   ]        */
+	0x88,	/*=   [*   ]        */
+	0xcc,	/*=   [**  ]        */
+	0xaa,	/*=   [* * ]        */
+	0xaa,	/*=   [* * ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 105: 'i'   */
+	0x44,	/*=   [ *  ]        */
+	0x00,	/*=   [    ]        */
+	0x44,	/*=   [ *  ]        */
+	0x44,	/*=   [ *  ]        */
+	0x44,	/*=   [ *  ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 106: 'j'   */
+	0x44,	/*=   [ *  ]        */
+	0x00,	/*=   [    ]        */
+	0x44,	/*=   [ *  ]        */
+	0x44,	/*=   [ *  ]        */
+	0x88,	/*=   [*   ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 107: 'k'   */
+	0x00,	/*=   [    ]        */
+	0x88,	/*=   [*   ]        */
+	0xaa,	/*=   [* * ]        */
+	0xcc,	/*=   [**  ]        */
+	0xaa,	/*=   [* * ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 108: 'l'   */
+	0x00,	/*=   [    ]        */
+	0xcc,	/*=   [**  ]        */
+	0x44,	/*=   [ *  ]        */
+	0x44,	/*=   [ *  ]        */
+	0xee,	/*=   [*** ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 109: 'm'   */
+	0x00,	/*=   [    ]        */
+	0x00,	/*=   [    ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xaa,	/*=   [* * ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 110: 'n'   */
+	0x00,	/*=   [    ]        */
+	0x00,	/*=   [    ]        */
+	0xcc,	/*=   [**  ]        */
+	0xaa,	/*=   [* * ]        */
+	0xaa,	/*=   [* * ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 111: 'o'   */
+	0x00,	/*=   [    ]        */
+	0x44,	/*=   [ *  ]        */
+	0xaa,	/*=   [* * ]        */
+	0xaa,	/*=   [* * ]        */
+	0x44,	/*=   [ *  ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 112: 'p'   */
+	0x00,	/*=   [    ]        */
+	0x00,	/*=   [    ]        */
+	0xcc,	/*=   [**  ]        */
+	0xaa,	/*=   [* * ]        */
+	0xcc,	/*=   [**  ]        */
+	0x88,	/*=   [*   ]        */
+	/*}*/
+	/*{*/ 	/*   Char 113: 'q'   */
+	0x00,	/*=   [    ]        */
+	0x00,	/*=   [    ]        */
+	0x66,	/*=   [ ** ]        */
+	0xaa,	/*=   [* * ]        */
+	0x66,	/*=   [ ** ]        */
+	0x22,	/*=   [  * ]        */
+	/*}*/
+	/*{*/ 	/*   Char 114: 'r'   */
+	0x00,	/*=   [    ]        */
+	0xcc,	/*=   [**  ]        */
+	0xaa,	/*=   [* * ]        */
+	0x88,	/*=   [*   ]        */
+	0x88,	/*=   [*   ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 115: 's'   */
+	0x00,	/*=   [    ]        */
+	0x66,	/*=   [ ** ]        */
+	0xcc,	/*=   [**  ]        */
+	0x22,	/*=   [  * ]        */
+	0xcc,	/*=   [**  ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 116: 't'   */
+	0x00,	/*=   [    ]        */
+	0x44,	/*=   [ *  ]        */
+	0xee,	/*=   [*** ]        */
+	0x44,	/*=   [ *  ]        */
+	0x44,	/*=   [ *  ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 117: 'u'   */
+	0x00,	/*=   [    ]        */
+	0x00,	/*=   [    ]        */
+	0xaa,	/*=   [* * ]        */
+	0xaa,	/*=   [* * ]        */
+	0x66,	/*=   [ ** ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 118: 'v'   */
+	0x00,	/*=   [    ]        */
+	0x00,	/*=   [    ]        */
+	0xaa,	/*=   [* * ]        */
+	0xee,	/*=   [*** ]        */
+	0x44,	/*=   [ *  ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 119: 'w'   */
+	0x00,	/*=   [    ]        */
+	0x00,	/*=   [    ]        */
+	0xaa,	/*=   [* * ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 120: 'x'   */
+	0x00,	/*=   [    ]        */
+	0x00,	/*=   [    ]        */
+	0xaa,	/*=   [* * ]        */
+	0x44,	/*=   [ *  ]        */
+	0xaa,	/*=   [* * ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 121: 'y'   */
+	0x00,	/*=   [    ]        */
+	0x00,	/*=   [    ]        */
+	0xaa,	/*=   [* * ]        */
+	0xee,	/*=   [*** ]        */
+	0x22,	/*=   [  * ]        */
+	0xcc,	/*=   [**  ]        */
+	/*}*/
+	/*{*/ 	/*   Char 122: 'z' */
+	0x00,	/*=   [    ]        */
+	0xee,	/*=   [*** ]        */
+	0x66,	/*=   [ ** ]        */
+	0xcc,	/*=   [**  ]        */
+	0xee,	/*=   [*** ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 123: '{' */
+	0x22,	/*=   [  * ]        */
+	0x44,	/*=   [ *  ]        */
+	0xcc,	/*=   [**  ]        */
+	0x44,	/*=   [ *  ]        */
+	0x22,	/*=   [  * ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 124: '|' */
+	0x44,	/*=   [ *  ]        */
+	0x44,	/*=   [ *  ]        */
+	0x00,	/*=   [    ]        */
+	0x44,	/*=   [ *  ]        */
+	0x44,	/*=   [ *  ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 125: '}' */
+	0x88,	/*=   [*   ]        */
+	0x44,	/*=   [ *  ]        */
+	0x66,	/*=   [ ** ]        */
+	0x44,	/*=   [ *  ]        */
+	0x88,	/*=   [*   ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 126: '~' */
+	0x55,	/*=   [ * *]        */
+	0xaa,	/*=   [* * ]        */
+	0x00,	/*=   [    ]        */
+	0x00,	/*=   [    ]        */
+	0x00,	/*=   [    ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 127: '' */
+	0x44,	/*=   [ *  ]        */
+	0xaa,	/*=   [* * ]        */
+	0xaa,	/*=   [* * ]        */
+	0xee,	/*=   [*** ]        */
+	0x00,	/*=   [    ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 128:  */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 129:  */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 130:  */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 131:  */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 132:  */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 133:  */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 134:  */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 135:  */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 136:  */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 137:  */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 138:  */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 139:  */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 140:  */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 141:  */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 142:  */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 143:  */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 144:  */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 145:  */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 146:  */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 147:  */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 148:  */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 149:  */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 150:  */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 151:  */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 152:  */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 153:  */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 154:  */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 155:  */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 156:  */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 157:  */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 158:  */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 159:  */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 160:  */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 161:  */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 162:  */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 163:  */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 164:  */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 165:  */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 166:  */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 167:  */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 168:  */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 169:  */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 170:  */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 171:  */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 172:  */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 173:  */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 174:  */
+	0x00,	/*=   [    ]        */
+	0x66,	/*=   [ ** ]        */
+	0xcc,	/*=   [**  ]        */
+	0x66,	/*=   [ ** ]        */
+	0x00,	/*=   [    ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 175:  */
+	0x00,	/*=   [    ]        */
+	0xcc,	/*=   [**  ]        */
+	0x66,	/*=   [ ** ]        */
+	0xcc,	/*=   [**  ]        */
+	0x00,	/*=   [    ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 176:  */
+	0x88,	/*=   [*   ]        */
+	0x22,	/*=   [  * ]        */
+	0x88,	/*=   [*   ]        */
+	0x22,	/*=   [  * ]        */
+	0x88,	/*=   [*   ]        */
+	0x22,	/*=   [  * ]        */
+	/*}*/
+	/*{*/ 	/*   Char 177:  */
+	0xaa,	/*=   [* * ]        */
+	0x55,	/*=   [ * *]        */
+	0xaa,	/*=   [* * ]        */
+	0x55,	/*=   [ * *]        */
+	0xaa,	/*=   [* * ]        */
+	0x55,	/*=   [ * *]        */
+	/*}*/
+	/*{*/ 	/*   Char 178:  */
+	0xdd,	/*=   [** *]        */
+	0xbb,	/*=   [* **]        */
+	0xdd,	/*=   [** *]        */
+	0xbb,	/*=   [* **]        */
+	0xdd,	/*=   [** *]        */
+	0xbb,	/*=   [* **]        */
+	/*}*/
+	/*{*/ 	/*   Char 179:  */
+	0x44,	/*=   [ *  ]        */
+	0x44,	/*=   [ *  ]        */
+	0x44,	/*=   [ *  ]        */
+	0x44,	/*=   [ *  ]        */
+	0x44,	/*=   [ *  ]        */
+	0x44,	/*=   [ *  ]        */
+	/*}*/
+	/*{*/ 	/*   Char 180:  */
+	0x44,	/*=   [ *  ]        */
+	0x44,	/*=   [ *  ]        */
+	0xcc,	/*=   [**  ]        */
+	0x44,	/*=   [ *  ]        */
+	0x44,	/*=   [ *  ]        */
+	0x44,	/*=   [ *  ]        */
+	/*}*/
+	/*{*/ 	/*   Char 181:  */
+	0x44,	/*=   [ *  ]        */
+	0x44,	/*=   [ *  ]        */
+	0xcc,	/*=   [**  ]        */
+	0xcc,	/*=   [**  ]        */
+	0x44,	/*=   [ *  ]        */
+	0x44,	/*=   [ *  ]        */
+	/*}*/
+	/*{*/ 	/*   Char 182:  */
+	0x66,	/*=   [ ** ]        */
+	0x66,	/*=   [ ** ]        */
+	0xee,	/*=   [*** ]        */
+	0x66,	/*=   [ ** ]        */
+	0x66,	/*=   [ ** ]        */
+	0x66,	/*=   [ ** ]        */
+	/*}*/
+	/*{*/ 	/*   Char 183:  */
+	0x00,	/*=   [    ]        */
+	0x00,	/*=   [    ]        */
+	0xee,	/*=   [*** ]        */
+	0x66,	/*=   [ ** ]        */
+	0x66,	/*=   [ ** ]        */
+	0x66,	/*=   [ ** ]        */
+	/*}*/
+	/*{*/ 	/*   Char 184:  */
+	0x00,	/*=   [    ]        */
+	0x00,	/*=   [    ]        */
+	0xcc,	/*=   [**  ]        */
+	0xcc,	/*=   [**  ]        */
+	0x44,	/*=   [ *  ]        */
+	0x44,	/*=   [ *  ]        */
+	/*}*/
+	/*{*/ 	/*   Char 185:  */
+	0x66,	/*=   [ ** ]        */
+	0x66,	/*=   [ ** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0x66,	/*=   [ ** ]        */
+	0x66,	/*=   [ ** ]        */
+	/*}*/
+	/*{*/ 	/*   Char 186:  */
+	0x66,	/*=   [ ** ]        */
+	0x66,	/*=   [ ** ]        */
+	0x66,	/*=   [ ** ]        */
+	0x66,	/*=   [ ** ]        */
+	0x66,	/*=   [ ** ]        */
+	0x66,	/*=   [ ** ]        */
+	/*}*/
+	/*{*/ 	/*   Char 187:  */
+	0x00,	/*=   [    ]        */
+	0x00,	/*=   [    ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0x66,	/*=   [ ** ]        */
+	0x66,	/*=   [ ** ]        */
+	/*}*/
+	/*{*/ 	/*   Char 188:  */
+	0x66,	/*=   [ ** ]        */
+	0x66,	/*=   [ ** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0x00,	/*=   [    ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 189:  */
+	0x66,	/*=   [ ** ]        */
+	0x66,	/*=   [ ** ]        */
+	0xee,	/*=   [*** ]        */
+	0x00,	/*=   [    ]        */
+	0x00,	/*=   [    ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 190:  */
+	0x44,	/*=   [ *  ]        */
+	0x44,	/*=   [ *  ]        */
+	0xcc,	/*=   [**  ]        */
+	0xcc,	/*=   [**  ]        */
+	0x00,	/*=   [    ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 191:  */
+	0x00,	/*=   [    ]        */
+	0x00,	/*=   [    ]        */
+	0xcc,	/*=   [**  ]        */
+	0x44,	/*=   [ *  ]        */
+	0x44,	/*=   [ *  ]        */
+	0x44,	/*=   [ *  ]        */
+	/*}*/
+	/*{*/ 	/*   Char 192:  */
+	0x44,	/*=   [ *  ]        */
+	0x44,	/*=   [ *  ]        */
+	0x77,	/*=   [ ***]        */
+	0x00,	/*=   [    ]        */
+	0x00,	/*=   [    ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 193:  */
+	0x44,	/*=   [ *  ]        */
+	0x44,	/*=   [ *  ]        */
+	0xff,	/*=   [****]        */
+	0x00,	/*=   [    ]        */
+	0x00,	/*=   [    ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 194:  */
+	0x00,	/*=   [    ]        */
+	0x00,	/*=   [    ]        */
+	0xff,	/*=   [****]        */
+	0x44,	/*=   [ *  ]        */
+	0x44,	/*=   [ *  ]        */
+	0x44,	/*=   [ *  ]        */
+	/*}*/
+	/*{*/ 	/*   Char 195:  */
+	0x44,	/*=   [ *  ]        */
+	0x44,	/*=   [ *  ]        */
+	0x77,	/*=   [ ***]        */
+	0x44,	/*=   [ *  ]        */
+	0x44,	/*=   [ *  ]        */
+	0x44,	/*=   [ *  ]        */
+	/*}*/
+	/*{*/ 	/*   Char 196:  */
+	0x00,	/*=   [    ]        */
+	0x00,	/*=   [    ]        */
+	0xff,	/*=   [****]        */
+	0x00,	/*=   [    ]        */
+	0x00,	/*=   [    ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 197:  */
+	0x44,	/*=   [ *  ]        */
+	0x44,	/*=   [ *  ]        */
+	0xff,	/*=   [****]        */
+	0x44,	/*=   [ *  ]        */
+	0x44,	/*=   [ *  ]        */
+	0x44,	/*=   [ *  ]        */
+	/*}*/
+	/*{*/ 	/*   Char 198:  */
+	0x44,	/*=   [ *  ]        */
+	0x44,	/*=   [ *  ]        */
+	0x77,	/*=   [ ***]        */
+	0x77,	/*=   [ ***]        */
+	0x44,	/*=   [ *  ]        */
+	0x44,	/*=   [ *  ]        */
+	/*}*/
+	/*{*/ 	/*   Char 199:  */
+	0x66,	/*=   [ ** ]        */
+	0x66,	/*=   [ ** ]        */
+	0x77,	/*=   [ ***]        */
+	0x66,	/*=   [ ** ]        */
+	0x66,	/*=   [ ** ]        */
+	0x66,	/*=   [ ** ]        */
+	/*}*/
+	/*{*/ 	/*   Char 200:  */
+	0x66,	/*=   [ ** ]        */
+	0x66,	/*=   [ ** ]        */
+	0x77,	/*=   [ ***]        */
+	0x77,	/*=   [ ***]        */
+	0x00,	/*=   [    ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 201:  */
+	0x00,	/*=   [    ]        */
+	0x00,	/*=   [    ]        */
+	0x77,	/*=   [ ***]        */
+	0x77,	/*=   [ ***]        */
+	0x66,	/*=   [ ** ]        */
+	0x66,	/*=   [ ** ]        */
+	/*}*/
+	/*{*/ 	/*   Char 202:  */
+	0x66,	/*=   [ ** ]        */
+	0x66,	/*=   [ ** ]        */
+	0xff,	/*=   [****]        */
+	0xff,	/*=   [****]        */
+	0x00,	/*=   [    ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 203:  */
+	0x00,	/*=   [    ]        */
+	0x00,	/*=   [    ]        */
+	0xff,	/*=   [****]        */
+	0xff,	/*=   [****]        */
+	0x66,	/*=   [ ** ]        */
+	0x66,	/*=   [ ** ]        */
+	/*}*/
+	/*{*/ 	/*   Char 204:  */
+	0x66,	/*=   [ ** ]        */
+	0x66,	/*=   [ ** ]        */
+	0x77,	/*=   [ ***]        */
+	0x77,	/*=   [ ***]        */
+	0x66,	/*=   [ ** ]        */
+	0x66,	/*=   [ ** ]        */
+	/*}*/
+	/*{*/ 	/*   Char 205:  */
+	0x00,	/*=   [    ]        */
+	0x00,	/*=   [    ]        */
+	0xff,	/*=   [****]        */
+	0xff,	/*=   [****]        */
+	0x00,	/*=   [    ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 206:  */
+	0x66,	/*=   [ ** ]        */
+	0x66,	/*=   [ ** ]        */
+	0xff,	/*=   [****]        */
+	0xff,	/*=   [****]        */
+	0x66,	/*=   [ ** ]        */
+	0x66,	/*=   [ ** ]        */
+	/*}*/
+	/*{*/ 	/*   Char 207:  */
+	0x44,	/*=   [ *  ]        */
+	0x44,	/*=   [ *  ]        */
+	0xff,	/*=   [****]        */
+	0xff,	/*=   [****]        */
+	0x00,	/*=   [    ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 208:  */
+	0x66,	/*=   [ ** ]        */
+	0x66,	/*=   [ ** ]        */
+	0xff,	/*=   [****]        */
+	0x00,	/*=   [    ]        */
+	0x00,	/*=   [    ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 209:  */
+	0x00,	/*=   [    ]        */
+	0x00,	/*=   [    ]        */
+	0xff,	/*=   [****]        */
+	0xff,	/*=   [****]        */
+	0x44,	/*=   [ *  ]        */
+	0x44,	/*=   [ *  ]        */
+	/*}*/
+	/*{*/ 	/*   Char 210:  */
+	0x00,	/*=   [    ]        */
+	0x00,	/*=   [    ]        */
+	0xff,	/*=   [****]        */
+	0x66,	/*=   [ ** ]        */
+	0x66,	/*=   [ ** ]        */
+	0x66,	/*=   [ ** ]        */
+	/*}*/
+	/*{*/ 	/*   Char 211:  */
+	0x66,	/*=   [ ** ]        */
+	0x66,	/*=   [ ** ]        */
+	0x77,	/*=   [ ***]        */
+	0x00,	/*=   [    ]        */
+	0x00,	/*=   [    ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 212:  */
+	0x44,	/*=   [ *  ]        */
+	0x44,	/*=   [ *  ]        */
+	0x77,	/*=   [ ***]        */
+	0x77,	/*=   [ ***]        */
+	0x00,	/*=   [    ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 213:  */
+	0x00,	/*=   [    ]        */
+	0x00,	/*=   [    ]        */
+	0x77,	/*=   [ ***]        */
+	0x77,	/*=   [ ***]        */
+	0x44,	/*=   [ *  ]        */
+	0x44,	/*=   [ *  ]        */
+	/*}*/
+	/*{*/ 	/*   Char 214:  */
+	0x00,	/*=   [    ]        */
+	0x00,	/*=   [    ]        */
+	0x77,	/*=   [ ***]        */
+	0x66,	/*=   [ ** ]        */
+	0x66,	/*=   [ ** ]        */
+	0x66,	/*=   [ ** ]        */
+	/*}*/
+	/*{*/ 	/*   Char 215:  */
+	0x66,	/*=   [ ** ]        */
+	0x66,	/*=   [ ** ]        */
+	0xff,	/*=   [****]        */
+	0x66,	/*=   [ ** ]        */
+	0x66,	/*=   [ ** ]        */
+	0x66,	/*=   [ ** ]        */
+	/*}*/
+	/*{*/ 	/*   Char 216:  */
+	0x44,	/*=   [ *  ]        */
+	0x44,	/*=   [ *  ]        */
+	0xff,	/*=   [****]        */
+	0xff,	/*=   [****]        */
+	0x44,	/*=   [ *  ]        */
+	0x44,	/*=   [ *  ]        */
+	/*}*/
+	/*{*/ 	/*   Char 217:  */
+	0x44,	/*=   [ *  ]        */
+	0x44,	/*=   [ *  ]        */
+	0xcc,	/*=   [**  ]        */
+	0x00,	/*=   [    ]        */
+	0x00,	/*=   [    ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 218:  */
+	0x00,	/*=   [    ]        */
+	0x00,	/*=   [    ]        */
+	0x77,	/*=   [ ***]        */
+	0x44,	/*=   [ *  ]        */
+	0x44,	/*=   [ *  ]        */
+	0x44,	/*=   [ *  ]        */
+	/*}*/
+	/*{*/ 	/*   Char 219:  */
+	0xff,	/*=   [****]        */
+	0xff,	/*=   [****]        */
+	0xff,	/*=   [****]        */
+	0xff,	/*=   [****]        */
+	0xff,	/*=   [****]        */
+	0xff,	/*=   [****]        */
+	/*}*/
+	/*{*/ 	/*   Char 220:  */
+	0x00,	/*=   [    ]        */
+	0x00,	/*=   [    ]        */
+	0x00,	/*=   [    ]        */
+	0xff,	/*=   [****]        */
+	0xff,	/*=   [****]        */
+	0xff,	/*=   [****]        */
+	/*}*/
+	/*{*/ 	/*   Char 221:  */
+	0xcc,	/*=   [**  ]        */
+	0xcc,	/*=   [**  ]        */
+	0xcc,	/*=   [**  ]        */
+	0xcc,	/*=   [**  ]        */
+	0xcc,	/*=   [**  ]        */
+	0xcc,	/*=   [**  ]        */
+	/*}*/
+	/*{*/ 	/*   Char 222:  */
+	0x33,	/*=   [  **]        */
+	0x33,	/*=   [  **]        */
+	0x33,	/*=   [  **]        */
+	0x33,	/*=   [  **]        */
+	0x33,	/*=   [  **]        */
+	0x33,	/*=   [  **]        */
+	/*}*/
+	/*{*/ 	/*   Char 223:  */
+	0xff,	/*=   [****]        */
+	0xff,	/*=   [****]        */
+	0xff,	/*=   [****]        */
+	0x00,	/*=   [    ]        */
+	0x00,	/*=   [    ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 224:  */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 225:  */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 226:  */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 227:  */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 228:  */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 229:  */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 230:  */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 231:  */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 232:  */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 233:  */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 234:  */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 235:  */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 236:  */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 237:  */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 238:  */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 239:  */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 240:  */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 241:  */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 242:  */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 243:  */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 244:  */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 245:  */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 246:  */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 247:  */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 248:  */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 249:  */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 250:  */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 251:  */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 252:  */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 253:  */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 254:  */
+	0x00,	/*=   [    ]        */
+	0x00,	/*=   [    ]        */
+	0x66,	/*=   [ ** ]        */
+	0x66,	/*=   [ ** ]        */
+	0x00,	/*=   [    ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+	/*{*/ 	/*   Char 255:  */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0xee,	/*=   [*** ]        */
+	0x00,	/*=   [    ]        */
+	/*}*/
+};
+
+struct fbcon_font_desc font_mini_4x6 = {
+	MINI4x6_IDX,
+	"MINI4x6",
+	4,
+	6,
+	fontdata_mini_4x6,
+	3
+};
+
diff -Nru a/drivers/video/fonts.c b/drivers/video/fonts.c
--- a/drivers/video/fonts.c	Mon Oct  7 11:25:28 2002
+++ b/drivers/video/fonts.c	Mon Oct  7 11:25:28 2002
@@ -56,6 +56,10 @@
 #undef NO_FONTS
     &font_pearl_8x8,
 #endif
+#ifdef CONFIG_FONT_MINI_4x6
+#undef NO_FONTS
+    &font_mini_4x6,
+#endif
 };
 
 #define num_fonts (sizeof(fbcon_fonts)/sizeof(*fbcon_fonts))
diff -Nru a/drivers/video/sa1100fb.c b/drivers/video/sa1100fb.c
--- a/drivers/video/sa1100fb.c	Mon Oct  7 11:25:28 2002
+++ b/drivers/video/sa1100fb.c	Mon Oct  7 11:25:28 2002
@@ -767,7 +767,7 @@
 static int sa1100fb_activate_var(struct fb_var_screeninfo *var, struct sa1100fb_info *);
 static void set_ctrlr_state(struct sa1100fb_info *fbi, u_int state);
 
-static inline void sa1100fb_schedule_task(struct sa1100fb_info *fbi, u_int state)
+static inline void sa1100fb_schedule_work(struct sa1100fb_info *fbi, u_int state)
 {
 	unsigned long flags;
 
@@ -787,7 +787,7 @@
 
 	if (state != (u_int)-1) {
 		fbi->task_state = state;
-		schedule_task(&fbi->task);
+		schedule_work(&fbi->task);
 	}
 	local_irq_restore(flags);
 }
@@ -1140,14 +1140,14 @@
 		    fbi->fb.fix.visual == FB_VISUAL_STATIC_PSEUDOCOLOR)
 			for (i = 0; i < fbi->palette_size; i++)
 				sa1100fb_setpalettereg(i, 0, 0, 0, 0, info);
-		sa1100fb_schedule_task(fbi, C_DISABLE);
+		sa1100fb_schedule_work(fbi, C_DISABLE);
 		break;
 
 	case VESA_NO_BLANKING:
 		if (fbi->fb.fix.visual == FB_VISUAL_PSEUDOCOLOR ||
 		    fbi->fb.fix.visual == FB_VISUAL_STATIC_PSEUDOCOLOR)
 			fb_set_cmap(&fbi->fb.cmap, 1, info);
-		sa1100fb_schedule_task(fbi, C_ENABLE);
+		sa1100fb_schedule_work(fbi, C_ENABLE);
 	}
 	return 0;
 }
@@ -1285,7 +1285,7 @@
 	if ((LCCR0 != fbi->reg_lccr0)       || (LCCR1 != fbi->reg_lccr1) ||
 	    (LCCR2 != fbi->reg_lccr2)       || (LCCR3 != fbi->reg_lccr3) ||
 	    (DBAR1 != fbi->dbar1) || (DBAR2 != fbi->dbar2))
-		sa1100fb_schedule_task(fbi, C_REENABLE);
+		sa1100fb_schedule_work(fbi, C_REENABLE);
 
 	return 0;
 }
@@ -1778,7 +1778,7 @@
 	fbi->fb.disp->inverse		= inf->cmap_inverse;
 
 	init_waitqueue_head(&fbi->ctrlr_wait);
-	INIT_TQUEUE(&fbi->task, sa1100fb_task, fbi);
+	INIT_WORK(&fbi->task, sa1100fb_task, fbi);
 	init_MUTEX(&fbi->ctrlr_sem);
 
 	return fbi;
diff -Nru a/drivers/video/sa1100fb.h b/drivers/video/sa1100fb.h
--- a/drivers/video/sa1100fb.h	Mon Oct  7 11:25:25 2002
+++ b/drivers/video/sa1100fb.h	Mon Oct  7 11:25:25 2002
@@ -101,7 +101,7 @@
 	volatile u_char		task_state;
 	struct semaphore	ctrlr_sem;
 	wait_queue_head_t	ctrlr_wait;
-	struct tq_struct	task;
+	struct work_struct	task;
 
 #ifdef CONFIG_PM
 	struct pm_dev		*pm;
diff -Nru a/drivers/zorro/Makefile b/drivers/zorro/Makefile
--- a/drivers/zorro/Makefile	Mon Oct  7 11:25:29 2002
+++ b/drivers/zorro/Makefile	Mon Oct  7 11:25:29 2002
@@ -18,4 +18,4 @@
 # And that's how to generate them
 
 $(obj)/devlist.h: $(src)/zorro.ids $(obj)/gen-devlist
-	$(obj)/gen-devlist < $<
+	( cd $(obj); ./gen-devlist ) < $<
diff -Nru a/drivers/zorro/zorro.c b/drivers/zorro/zorro.c
--- a/drivers/zorro/zorro.c	Mon Oct  7 11:25:25 2002
+++ b/drivers/zorro/zorro.c	Mon Oct  7 11:25:25 2002
@@ -80,7 +80,7 @@
      *  FIXME: use the normal resource management
      */
 
-bitmap_member(zorro_unused_z2ram, 128);
+DECLARE_BITMAP(zorro_unused_z2ram, 128);
 
 
 static void __init mark_region(unsigned long start, unsigned long end,
diff -Nru a/fs/Config.help b/fs/Config.help
--- a/fs/Config.help	Mon Oct  7 11:25:36 2002
+++ b/fs/Config.help	Mon Oct  7 11:25:36 2002
@@ -514,6 +514,27 @@
 
   If unsure, say N.
 
+CONFIG_NFS_DIRECTIO
+  This option enables applications to perform uncached I/O on files
+  in NFS file systems using the O_DIRECT open() flag.  When O_DIRECT
+  is set for a file, its data is not cached in the system's page
+  cache.  Data is moved to and from user-level application buffers
+  directly.  Unlike local disk-based file systems, NFS O_DIRECT has
+  no alignment restrictions.
+
+  Unless your program is designed to use O_DIRECT properly, you are
+  much better off allowing the NFS client to manage data caching for
+  you.  Misusing O_DIRECT can cause poor server performance or network
+  storms.  This kernel build option defaults OFF to avoid exposing
+  system administrators unwittingly to a potentially hazardous
+  feature.
+
+  For more details on NFS O_DIRECT, see fs/nfs/direct.c.
+
+  If unsure, say N.  This reduces the size of the NFS client, and
+  causes open() to return EINVAL if a file residing in NFS is
+  opened with the O_DIRECT flag.
+
 CONFIG_ROOT_NFS
   If you want your Linux box to mount its whole root file system (the
   one containing the directory /) from some other computer over the
diff -Nru a/fs/aio.c b/fs/aio.c
--- a/fs/aio.c	Mon Oct  7 11:25:35 2002
+++ b/fs/aio.c	Mon Oct  7 11:25:35 2002
@@ -30,8 +30,8 @@
 #include <linux/compiler.h>
 #include <linux/brlock.h>
 #include <linux/module.h>
-#include <linux/tqueue.h>
 #include <linux/highmem.h>
+#include <linux/workqueue.h>
 
 #include <asm/kmap_types.h>
 #include <asm/uaccess.h>
@@ -50,11 +50,11 @@
 static kmem_cache_t	*kiocb_cachep;
 static kmem_cache_t	*kioctx_cachep;
 
+static struct workqueue_struct *aio_wq;
+
 /* Used for rare fput completion. */
 static void aio_fput_routine(void *);
-static struct tq_struct	fput_tqueue = {
-	.routine	= aio_fput_routine,
-};
+static DECLARE_WORK(fput_work, aio_fput_routine, NULL);
 
 static spinlock_t	fput_lock = SPIN_LOCK_UNLOCKED;
 LIST_HEAD(fput_head);
@@ -75,6 +75,8 @@
 	if (!kioctx_cachep)
 		panic("unable to create kioctx cache");
 
+	aio_wq = create_workqueue("aio");
+
 	printk(KERN_NOTICE "aio_setup: sizeof(struct page) = %d\n", (int)sizeof(struct page));
 
 	return 0;
@@ -196,8 +198,7 @@
 
 static inline void put_aio_ring_event(struct io_event *event, enum km_type km)
 {
-	void *p = (void *)((unsigned long)event & PAGE_MASK);
-	kunmap_atomic(p, km);
+	kunmap_atomic((void *)((unsigned long)event & PAGE_MASK), km);
 }
 
 /* ioctx_alloc
@@ -399,8 +400,10 @@
 		req->ki_user_obj = NULL;
 		req->ki_ctx = ctx;
 		req->ki_users = 1;
-	} else
+	} else {
 		kmem_cache_free(kiocb_cachep, req);
+		req = NULL;
+	}
 	kunmap_atomic(ring, KM_USER0);
 	spin_unlock_irq(&ctx->ctx_lock);
 
@@ -475,14 +478,14 @@
 	req->ki_cancel = NULL;
 
 	/* Must be done under the lock to serialise against cancellation.
-	 * Call this aio_fput as it duplicates fput via the fput_tqueue.
+	 * Call this aio_fput as it duplicates fput via the fput_work.
 	 */
 	if (unlikely(atomic_dec_and_test(&req->ki_filp->f_count))) {
 		get_ioctx(ctx);
 		spin_lock(&fput_lock);
 		list_add(&req->ki_list, &fput_head);
 		spin_unlock(&fput_lock);
-		schedule_task(&fput_tqueue);
+		queue_work(aio_wq, &fput_work);
 	} else
 		really_put_req(ctx, req);
 	return 1;
diff -Nru a/fs/block_dev.c b/fs/block_dev.c
--- a/fs/block_dev.c	Mon Oct  7 11:25:40 2002
+++ b/fs/block_dev.c	Mon Oct  7 11:25:40 2002
@@ -116,9 +116,11 @@
 }
 
 static int
-blkdev_direct_IO(int rw, struct inode *inode, const struct iovec *iov,
+blkdev_direct_IO(int rw, struct file *file, const struct iovec *iov,
 			loff_t offset, unsigned long nr_segs)
 {
+	struct inode *inode = file->f_dentry->d_inode->i_mapping->host;
+
 	return generic_direct_IO(rw, inode, iov, offset,
 				nr_segs, blkdev_get_blocks);
 }
diff -Nru a/fs/buffer.c b/fs/buffer.c
--- a/fs/buffer.c	Mon Oct  7 11:25:35 2002
+++ b/fs/buffer.c	Mon Oct  7 11:25:35 2002
@@ -443,7 +443,7 @@
 	 * We really want to use invalidate_inode_pages2() for
 	 * that, but not until that's cleaned up.
 	 */
-	invalidate_inode_pages(bdev->bd_inode);
+	invalidate_inode_pages(bdev->bd_inode->i_mapping);
 }
 
 void __invalidate_buffers(kdev_t dev, int destroy_dirty_buffers)
@@ -735,80 +735,22 @@
 }
 EXPORT_SYMBOL(sync_mapping_buffers);
 
-/**
- * write_mapping_buffers - Start writeout of a mapping's "associated" buffers.
- * @mapping - the mapping which wants those buffers written.
- *
- * Starts I/O against dirty buffers which are on @mapping->private_list.
- * Those buffers must be backed by @mapping->assoc_mapping.
- *
- * The private_list buffers generally contain filesystem indirect blocks.
- * The idea is that the filesystem can start I/O against the indirects at
- * the same time as running generic_writepages(), so the indirect's
- * I/O will be merged with the data.
- *
- * We sneakliy write the buffers in probable tail-to-head order.  This is
- * because generic_writepages() writes in probable head-to-tail
- * order.  If the file is so huge that the data or the indirects overflow
- * the request queue we will at least get some merging this way.
- *
- * Any clean+unlocked buffers are de-listed.  clean/locked buffers must be
- * left on the list for an fsync() to wait on.
- *
- * Couldn't think of a smart way of avoiding livelock, so chose the dumb
- * way instead.
- *
- * FIXME: duplicates fsync_inode_buffers() functionality a bit.
+/*
+ * Called when we've recently written block `bblock', and it is known that
+ * `bblock' was for a buffer_boundary() buffer.  This means that the block at
+ * `bblock + 1' is probably a dirty indirect block.  Hunt it down and, if it's
+ * dirty, schedule it for IO.  So that indirects merge nicely with their data.
  */
-int write_mapping_buffers(struct address_space *mapping)
+void write_boundary_block(struct block_device *bdev,
+			sector_t bblock, unsigned blocksize)
 {
-	spinlock_t *lock;
-	struct address_space *buffer_mapping;
-	unsigned nr_to_write;	/* livelock avoidance */
-	struct list_head *lh;
-	int ret = 0;
-
-	if (list_empty(&mapping->private_list))
-		goto out;
-
-	buffer_mapping = mapping->assoc_mapping;
-	lock = &buffer_mapping->private_lock;
-	spin_lock(lock);
-	nr_to_write = 0;
-	lh = mapping->private_list.next;
-	while (lh != &mapping->private_list) {
-		lh = lh->next;
-		nr_to_write++;
-	}
-	nr_to_write *= 2;	/* Allow for some late additions */
-
-	while (nr_to_write-- && !list_empty(&mapping->private_list)) {
-		struct buffer_head *bh;
-
-		bh = BH_ENTRY(mapping->private_list.prev);
-		list_del_init(&bh->b_assoc_buffers);
-		if (!buffer_dirty(bh) && !buffer_locked(bh))
-			continue;
-		/* Stick it on the far end of the list. Order is preserved. */
-		list_add(&bh->b_assoc_buffers, &mapping->private_list);
-		if (test_set_buffer_locked(bh))
-			continue;
-		get_bh(bh);
-		spin_unlock(lock);
-		if (test_clear_buffer_dirty(bh)) {
-			bh->b_end_io = end_buffer_io_sync;
-			submit_bh(WRITE, bh);
-		} else {
-			unlock_buffer(bh);
-			put_bh(bh);
-		}
-		spin_lock(lock);
+	struct buffer_head *bh = __find_get_block(bdev, bblock + 1, blocksize);
+	if (bh) {
+		if (buffer_dirty(bh))
+			ll_rw_block(WRITE, 1, &bh);
+		put_bh(bh);
 	}
-	spin_unlock(lock);
-out:
-	return ret;
 }
-EXPORT_SYMBOL(write_mapping_buffers);
 
 void mark_buffer_dirty_inode(struct buffer_head *bh, struct inode *inode)
 {
@@ -2487,77 +2429,32 @@
  * All of the buffers must be for the same device, and must also be a
  * multiple of the current approved size for the device.
  */
-void ll_rw_block(int rw, int nr, struct buffer_head * bhs[])
+void ll_rw_block(int rw, int nr, struct buffer_head *bhs[])
 {
-	unsigned int major;
-	int correct_size;
 	int i;
 
-	if (!nr)
-		return;
-
-	major = major(to_kdev_t(bhs[0]->b_bdev->bd_dev));
-
-	/* Determine correct block size for this device. */
-	correct_size = bdev_hardsect_size(bhs[0]->b_bdev);
-
-	/* Verify requested block sizes. */
-	for (i = 0; i < nr; i++) {
-		struct buffer_head *bh = bhs[i];
-		if (bh->b_size & (correct_size - 1)) {
-			printk(KERN_NOTICE "ll_rw_block: device %s: "
-			       "only %d-char blocks implemented (%u)\n",
-			       bdevname(bhs[0]->b_bdev),
-			       correct_size, bh->b_size);
-			goto sorry;
-		}
-	}
-
-	if ((rw & WRITE) && bdev_read_only(bhs[0]->b_bdev)) {
-		printk(KERN_NOTICE "Can't write to read-only device %s\n",
-		       bdevname(bhs[0]->b_bdev));
-		goto sorry;
-	}
-
 	for (i = 0; i < nr; i++) {
 		struct buffer_head *bh = bhs[i];
 
-		/* Only one thread can actually submit the I/O. */
 		if (test_set_buffer_locked(bh))
 			continue;
 
-		/* We have the buffer lock */
-		atomic_inc(&bh->b_count);
+		get_bh(bh);
 		bh->b_end_io = end_buffer_io_sync;
-
-		switch(rw) {
-		case WRITE:
-			if (!test_clear_buffer_dirty(bh))
-				/* Hmmph! Nothing to write */
-				goto end_io;
-			break;
-
-		case READA:
-		case READ:
-			if (buffer_uptodate(bh))
-				/* Hmmph! Already have it */
-				goto end_io;
-			break;
-		default:
-			BUG();
-	end_io:
-			bh->b_end_io(bh, buffer_uptodate(bh));
-			continue;
+		if (rw == WRITE) {
+			if (test_clear_buffer_dirty(bh)) {
+				submit_bh(WRITE, bh);
+				continue;
+			}
+		} else {
+			if (!buffer_uptodate(bh)) {
+				submit_bh(READ, bh);
+				continue;
+			}
 		}
-
-		submit_bh(rw, bh);
+		unlock_buffer(bh);
+		put_bh(bh);
 	}
-	return;
-
-sorry:
-	/* Make sure we don't get infinite dirty retries.. */
-	for (i = 0; i < nr; i++)
-		clear_buffer_dirty(bhs[i]);
 }
 
 /*
diff -Nru a/fs/direct-io.c b/fs/direct-io.c
--- a/fs/direct-io.c	Mon Oct  7 11:25:35 2002
+++ b/fs/direct-io.c	Mon Oct  7 11:25:35 2002
@@ -18,17 +18,12 @@
 #include <linux/bio.h>
 #include <linux/wait.h>
 #include <linux/err.h>
+#include <linux/blkdev.h>
 #include <linux/buffer_head.h>
 #include <linux/rwsem.h>
 #include <asm/atomic.h>
 
 /*
- * The largest-sized BIO which this code will assemble, in bytes.  Set this
- * to PAGE_SIZE if your drivers are broken.
- */
-#define DIO_BIO_MAX_SIZE (16*1024)
-
-/*
  * How many user pages to map in one call to get_user_pages().  This determines
  * the size of a structure on the stack.
  */
@@ -37,7 +32,6 @@
 struct dio {
 	/* BIO submission state */
 	struct bio *bio;		/* bio under assembly */
-	struct bio_vec *bvec;		/* current bvec in that bio */
 	struct inode *inode;
 	int rw;
 	unsigned blkbits;		/* doesn't change */
@@ -55,6 +49,7 @@
 	/* Page fetching state */
 	int curr_page;			/* changes */
 	int total_pages;		/* doesn't change */
+	int pages_left;			/* approximate total IO pages */
 	unsigned long curr_user_address;/* changes */
 
 	/* Page queue */
@@ -179,15 +174,10 @@
 		return -ENOMEM;
 
 	bio->bi_bdev = bdev;
-	bio->bi_vcnt = nr_vecs;
-	bio->bi_idx = 0;
-	bio->bi_size = 0;
 	bio->bi_sector = first_sector;
-	bio->bi_io_vec[0].bv_page = NULL;
 	bio->bi_end_io = dio_bio_end_io;
 
 	dio->bio = bio;
-	dio->bvec = NULL;		/* debug */
 	return 0;
 }
 
@@ -195,14 +185,11 @@
 {
 	struct bio *bio = dio->bio;
 
-	bio->bi_vcnt = bio->bi_idx;
-	bio->bi_idx = 0;
 	bio->bi_private = dio;
 	atomic_inc(&dio->bio_count);
 	submit_bio(dio->rw, bio);
 
 	dio->bio = NULL;
-	dio->bvec = NULL;
 	dio->boundary = 0;
 }
 
@@ -230,7 +217,7 @@
 			dio->waiter = current;
 			spin_unlock_irqrestore(&dio->bio_list_lock, flags);
 			blk_run_queues();
-			schedule();
+			io_schedule();
 			spin_lock_irqsave(&dio->bio_list_lock, flags);
 			dio->waiter = NULL;
 		}
@@ -393,8 +380,7 @@
 	if (dio->bio == NULL)
 		return;
 
-	if (dio->bio->bi_idx == dio->bio->bi_vcnt ||
-			dio->boundary ||
+	if (dio->boundary ||
 			dio->last_block_in_bio != dio->next_block_in_bio - 1)
 		dio_bio_submit(dio);
 }
@@ -405,19 +391,46 @@
 static int dio_new_bio(struct dio *dio)
 {
 	sector_t sector;
-	int ret;
+	int ret, nr_pages;
 
 	ret = dio_bio_reap(dio);
 	if (ret)
 		goto out;
 	sector = dio->next_block_in_bio << (dio->blkbits - 9);
-	ret = dio_bio_alloc(dio, dio->map_bh.b_bdev, sector,
-				DIO_BIO_MAX_SIZE / PAGE_SIZE);
+	nr_pages = min(dio->pages_left, bio_get_nr_vecs(dio->map_bh.b_bdev));
+	BUG_ON(nr_pages <= 0);
+	ret = dio_bio_alloc(dio, dio->map_bh.b_bdev, sector, nr_pages);
 	dio->boundary = 0;
 out:
 	return ret;
 }
 
+
+static int
+dio_bio_add_page(struct dio *dio, struct page *page,
+		unsigned int bv_len, unsigned int bv_offset)
+{
+	int ret = 0;
+
+	if (bv_len == 0) 
+		goto out;
+
+	page_cache_get(page);
+	if (bio_add_page(dio->bio, page, bv_len, bv_offset)) {
+		dio_bio_submit(dio);
+		ret = dio_new_bio(dio);
+		if (ret == 0) {
+			ret = bio_add_page(dio->bio, page, bv_len, bv_offset);
+			BUG_ON(ret != 0);
+		}
+	}
+	page_cache_release(page);
+	dio->pages_left--;
+out:
+	return ret;
+}
+
+
 /*
  * Walk the user pages, and the file, mapping blocks to disk and emitting BIOs.
  *
@@ -438,13 +451,15 @@
 	const unsigned blocks_per_page = PAGE_SIZE >> blkbits;
 	struct page *page;
 	unsigned block_in_page;
-	int ret;
+	int ret = 0;
 
 	/* The I/O can start at any block offset within the first page */
 	block_in_page = dio->first_block_in_page;
 
 	while (dio->block_in_file < dio->final_block_in_request) {
 		int new_page;	/* Need to insert this page into the BIO? */
+		unsigned int bv_offset;
+		unsigned int bv_len;
 
 		page = dio_get_page(dio);
 		if (IS_ERR(page)) {
@@ -453,15 +468,16 @@
 		}
 
 		new_page = 1;
+		bv_offset = 0;
+		bv_len = 0;
 		while (block_in_page < blocks_per_page) {
-			struct bio *bio;
 			unsigned this_chunk_bytes;	/* # of bytes mapped */
 			unsigned this_chunk_blocks;	/* # of blocks */
 			unsigned u;
 
 			ret = get_more_blocks(dio);
 			if (ret)
-				goto fail_release;
+				goto out;
 
 			/* Handle holes */
 			if (!buffer_mapped(&dio->map_bh)) {
@@ -480,24 +496,19 @@
 			if (dio->bio == NULL) {
 				ret = dio_new_bio(dio);
 				if (ret)
-					goto fail_release;
+					goto out;
 				new_page = 1;
 			}
 
-			bio = dio->bio;
 			if (new_page) {
-				dio->bvec = &bio->bi_io_vec[bio->bi_idx];
-				page_cache_get(page);
-				dio->bvec->bv_page = page;
-				dio->bvec->bv_len = 0;
-				dio->bvec->bv_offset = block_in_page << blkbits;
-				bio->bi_idx++;
+				bv_len = 0;
+				bv_offset = block_in_page << blkbits;
 				new_page = 0;
 			}
 
 			/* Work out how much disk we can add to this page */
 			this_chunk_blocks = dio->blocks_available;
-			u = (PAGE_SIZE - (dio->bvec->bv_offset + dio->bvec->bv_len)) >> blkbits;
+			u = (PAGE_SIZE - (bv_len + bv_offset)) >> blkbits;
 			if (this_chunk_blocks > u)
 				this_chunk_blocks = u;
 			u = dio->final_block_in_request - dio->block_in_file;
@@ -506,8 +517,7 @@
 			this_chunk_bytes = this_chunk_blocks << blkbits;
 			BUG_ON(this_chunk_bytes == 0);
 
-			dio->bvec->bv_len += this_chunk_bytes;
-			bio->bi_size += this_chunk_bytes;
+			bv_len += this_chunk_bytes;
 			dio->next_block_in_bio += this_chunk_blocks;
 			dio->last_block_in_bio = dio->next_block_in_bio - 1;
 			dio->boundary = buffer_boundary(&dio->map_bh);
@@ -520,13 +530,11 @@
 			if (dio->block_in_file == dio->final_block_in_request)
 				break;
 		}
+		ret = dio_bio_add_page(dio, page, bv_len, bv_offset);
+		if (ret)
+			goto out;
 		block_in_page = 0;
-		page_cache_release(page);
 	}
-	ret = 0;
-	goto out;
-fail_release:
-	page_cache_release(page);
 out:
 	return ret;
 }
@@ -542,7 +550,6 @@
 	size_t bytes, tot_bytes = 0;
 
 	dio.bio = NULL;
-	dio.bvec = NULL;
 	dio.inode = inode;
 	dio.rw = rw;
 	dio.blkbits = blkbits;
@@ -562,6 +569,10 @@
 	spin_lock_init(&dio.bio_list_lock);
 	dio.bio_list = NULL;
 	dio.waiter = NULL;
+	dio.pages_left = 0;
+
+	for (seg = 0; seg < nr_segs; seg++) 
+		dio.pages_left += (iov[seg].iov_len / PAGE_SIZE) + 2; 
 
 	for (seg = 0; seg < nr_segs; seg++) {
 		user_addr = (unsigned long)iov[seg].iov_base;
@@ -616,13 +627,11 @@
 	int seg;
 	size_t size;
 	unsigned long addr;
-	struct address_space *mapping = inode->i_mapping;
 	unsigned blocksize_mask = (1 << inode->i_blkbits) - 1;
 	ssize_t retval = -EINVAL;
 
-	if (offset & blocksize_mask) {
+	if (offset & blocksize_mask)
 		goto out;
-	}
 
 	/* Check the memory alignment.  Blocks cannot straddle pages */
 	for (seg = 0; seg < nr_segs; seg++) {
@@ -632,28 +641,29 @@
 			goto out;	
 	}
 
-	if (mapping->nrpages) {
-		retval = filemap_fdatawrite(mapping);
-		if (retval == 0)
-			retval = filemap_fdatawait(mapping);
-		if (retval)
-			goto out;
-	}
-
 	retval = direct_io_worker(rw, inode, iov, offset, nr_segs, get_blocks);
 out:
 	return retval;
 }
 
 ssize_t
-generic_file_direct_IO(int rw, struct inode *inode, const struct iovec *iov, 
+generic_file_direct_IO(int rw, struct file *file, const struct iovec *iov,
 	loff_t offset, unsigned long nr_segs)
 {
-	struct address_space *mapping = inode->i_mapping;
+	struct address_space *mapping = file->f_dentry->d_inode->i_mapping;
 	ssize_t retval;
 
-	retval = mapping->a_ops->direct_IO(rw, inode, iov, offset, nr_segs);
-	if (inode->i_mapping->nrpages)
-		invalidate_inode_pages2(inode->i_mapping);
+	if (mapping->nrpages) {
+		retval = filemap_fdatawrite(mapping);
+		if (retval == 0)
+			retval = filemap_fdatawait(mapping);
+		if (retval)
+			goto out;
+	}
+
+	retval = mapping->a_ops->direct_IO(rw, file, iov, offset, nr_segs);
+	if (mapping->nrpages)
+		invalidate_inode_pages2(mapping);
+out:
 	return retval;
 }
diff -Nru a/fs/ext2/inode.c b/fs/ext2/inode.c
--- a/fs/ext2/inode.c	Mon Oct  7 11:25:25 2002
+++ b/fs/ext2/inode.c	Mon Oct  7 11:25:25 2002
@@ -619,9 +619,11 @@
 }
 
 static int
-ext2_direct_IO(int rw, struct inode *inode, const struct iovec *iov,
+ext2_direct_IO(int rw, struct file *file, const struct iovec *iov,
 			loff_t offset, unsigned long nr_segs)
 {
+	struct inode *inode = file->f_dentry->d_inode->i_mapping->host;
+
 	return generic_direct_IO(rw, inode, iov,
 				offset, nr_segs, ext2_get_blocks);
 }
@@ -629,14 +631,7 @@
 static int
 ext2_writepages(struct address_space *mapping, struct writeback_control *wbc)
 {
-	int ret;
-	int err;
-
-	ret = write_mapping_buffers(mapping);
-	err = mpage_writepages(mapping, wbc, ext2_get_block);
-	if (!ret)
-		ret = err;
-	return ret;
+	return mpage_writepages(mapping, wbc, ext2_get_block);
 }
 
 struct address_space_operations ext2_aops = {
diff -Nru a/fs/ext3/inode.c b/fs/ext3/inode.c
--- a/fs/ext3/inode.c	Mon Oct  7 11:25:36 2002
+++ b/fs/ext3/inode.c	Mon Oct  7 11:25:36 2002
@@ -1399,10 +1399,11 @@
  * If the O_DIRECT write is intantiating holes inside i_size and the machine
  * crashes then stale disk data _may_ be exposed inside the file.
  */
-static int ext3_direct_IO(int rw, struct inode *inode,
+static int ext3_direct_IO(int rw, struct file *file,
 			const struct iovec *iov, loff_t offset,
 			unsigned long nr_segs)
 {
+	struct inode *inode = file->f_dentry->d_inode->i_mapping->host;
 	struct ext3_inode_info *ei = EXT3_I(inode);
 	handle_t *handle = NULL;
 	int ret;
@@ -1477,14 +1478,7 @@
 static int
 ext3_writepages(struct address_space *mapping, struct writeback_control *wbc)
 {
-	int ret;
-	int err;
-
-	ret = write_mapping_buffers(mapping);
-	err = mpage_writepages(mapping, wbc, ext3_get_block);
-	if (!ret)
-		ret = err;
-	return ret;
+	return mpage_writepages(mapping, wbc, ext3_get_block);
 }
 #endif
 
diff -Nru a/fs/fat/inode.c b/fs/fat/inode.c
--- a/fs/fat/inode.c	Mon Oct  7 11:25:34 2002
+++ b/fs/fat/inode.c	Mon Oct  7 11:25:34 2002
@@ -228,8 +228,6 @@
 	save = 0;
 	savep = NULL;
 	while ((this_char = strsep(&options,",")) != NULL) {
-		if (!*this_char)
-			continue;
 		if ((value = strchr(this_char,'=')) != NULL) {
 			save = *value;
 			savep = value;
@@ -351,7 +349,7 @@
 			strncpy(cvf_options,value,100);
 		}
 
-		if (this_char != options) *(this_char-1) = ',';
+		if (options) *(options-1) = ',';
 		if (value) *savep = save;
 		if (ret == 0)
 			break;
diff -Nru a/fs/fcntl.c b/fs/fcntl.c
--- a/fs/fcntl.c	Mon Oct  7 11:25:25 2002
+++ b/fs/fcntl.c	Mon Oct  7 11:25:25 2002
@@ -481,8 +481,6 @@
 void send_sigio(struct fown_struct *fown, int fd, int band)
 {
 	struct task_struct *p;
-	struct list_head *l;
-	struct pid *pidptr;
 	int pid;
 	
 	read_lock(&fown->lock);
@@ -491,15 +489,20 @@
 		goto out_unlock_fown;
 	
 	read_lock(&tasklist_lock);
-	if ( (pid > 0) && (p = find_task_by_pid(pid)) ) {
-		send_sigio_to_task(p, fown, fd, band);
-		goto out_unlock_task;
+	if (pid > 0) {
+		p = find_task_by_pid(pid);
+		if (p) {
+			send_sigio_to_task(p, fown, fd, band);
+		}
+	} else {
+		struct list_head *l;
+		struct pid *pidptr;
+		for_each_task_pid(-pid, PIDTYPE_PGID, p, l, pidptr) {
+			send_sigio_to_task(p, fown, fd, band);
+		}
 	}
-	for_each_task_pid(-pid, PIDTYPE_PGID, p, l, pidptr)
-		send_sigio_to_task(p, fown,fd,band);
-out_unlock_task:
 	read_unlock(&tasklist_lock);
-out_unlock_fown:
+ out_unlock_fown:
 	read_unlock(&fown->lock);
 }
 
@@ -523,21 +526,20 @@
 	ret = 1;
 	
 	read_lock(&tasklist_lock);
-	if ((pid > 0) && (p = find_task_by_pid(pid))) {
-		send_sigurg_to_task(p, fown);
-		goto out_unlock_task;
+	if (pid > 0) {
+		p = find_task_by_pid(pid);
+		if (p) {
+			send_sigurg_to_task(p, fown);
+		}
+	} else {
+		struct list_head *l;
+		struct pid *pidptr;
+		for_each_task_pid(-pid, PIDTYPE_PGID, p, l, pidptr) {
+			send_sigurg_to_task(p, fown);
+		}
 	}
-	for_each_process(p) {
-		int match = p->pid;
-		if (pid < 0)
-			match = -p->pgrp;
-		if (pid != match)
-			continue;
-		send_sigurg_to_task(p, fown);
-	}
-out_unlock_task:
 	read_unlock(&tasklist_lock);
-out_unlock_fown:
+ out_unlock_fown:
 	read_unlock(&fown->lock);
 	return ret;
 }
diff -Nru a/fs/hpfs/alloc.c b/fs/hpfs/alloc.c
--- a/fs/hpfs/alloc.c	Mon Oct  7 11:25:26 2002
+++ b/fs/hpfs/alloc.c	Mon Oct  7 11:25:26 2002
@@ -24,8 +24,8 @@
 		goto fail1;
 	}
 	hpfs_brelse4(&qbh);
-	if (sec >= s->s_hpfs_dirband_start && sec < s->s_hpfs_dirband_start + s->s_hpfs_dirband_size) {
-		unsigned ssec = (sec - s->s_hpfs_dirband_start) / 4;
+	if (sec >= hpfs_sb(s)->sb_dirband_start && sec < hpfs_sb(s)->sb_dirband_start + hpfs_sb(s)->sb_dirband_size) {
+		unsigned ssec = (sec - hpfs_sb(s)->sb_dirband_start) / 4;
 		if (!(bmp = hpfs_map_dnode_bitmap(s, &qbh))) goto fail;
 		if ((bmp[ssec >> 5] >> (ssec & 0x1f)) & 1) {
 			hpfs_error(s, "sector '%s' - %08x not allocated in directory bitmap", msg, sec);
@@ -48,11 +48,11 @@
 int hpfs_chk_sectors(struct super_block *s, secno start, int len, char *msg)
 {
 	if (start + len < start || start < 0x12 ||
-	    start + len > s->s_hpfs_fs_size) {
+	    start + len > hpfs_sb(s)->sb_fs_size) {
 	    	hpfs_error(s, "sector(s) '%s' badly placed at %08x", msg, start);
 		return 1;
 	}
-	if (s->s_hpfs_chk>=2) {
+	if (hpfs_sb(s)->sb_chk>=2) {
 		int i;
 		for (i = 0; i < len; i++)
 			if (chk_if_allocated(s, start + i, msg)) return 1;
@@ -127,7 +127,7 @@
 	}
 	rt:
 	if (ret) {
-		if (s->s_hpfs_chk && ((ret >> 14) != (bs >> 14) || (bmp[(ret & 0x3fff) >> 5] | ~(((1 << n) - 1) << (ret & 0x1f))) != 0xffffffff)) {
+		if (hpfs_sb(s)->sb_chk && ((ret >> 14) != (bs >> 14) || (bmp[(ret & 0x3fff) >> 5] | ~(((1 << n) - 1) << (ret & 0x1f))) != 0xffffffff)) {
 			hpfs_error(s, "Allocation doesn't work! Wanted %d, allocated at %08x", n, ret);
 			ret = 0;
 			goto b;
@@ -155,14 +155,15 @@
 	secno sec;
 	unsigned i;
 	unsigned n_bmps;
-	int b = s->s_hpfs_c_bitmap;
+	struct hpfs_sb_info *sbi = hpfs_sb(s);
+	int b = sbi->sb_c_bitmap;
 	int f_p = 0;
 	if (forward < 0) {
 		forward = -forward;
 		f_p = 1;
 	}
 	if (lock) hpfs_lock_creation(s);
-	if (near && near < s->s_hpfs_fs_size)
+	if (near && near < sbi->sb_fs_size)
 		if ((sec = alloc_in_bmp(s, near, n, f_p ? forward : forward/4))) goto ret;
 	if (b != -1) {
 		if ((sec = alloc_in_bmp(s, b<<14, n, f_p ? forward : forward/2))) {
@@ -171,25 +172,25 @@
 		}
 		if (b > 0x10000000) if ((sec = alloc_in_bmp(s, (b&0xfffffff)<<14, n, f_p ? forward : 0))) goto ret;
 	}	
-	n_bmps = (s->s_hpfs_fs_size + 0x4000 - 1) >> 14;
+	n_bmps = (sbi->sb_fs_size + 0x4000 - 1) >> 14;
 	for (i = 0; i < n_bmps / 2; i++) {
 		if ((sec = alloc_in_bmp(s, (n_bmps/2+i) << 14, n, forward))) {
-			s->s_hpfs_c_bitmap = n_bmps/2+i;
+			sbi->sb_c_bitmap = n_bmps/2+i;
 			goto ret;
 		}	
 		if ((sec = alloc_in_bmp(s, (n_bmps/2-i-1) << 14, n, forward))) {
-			s->s_hpfs_c_bitmap = n_bmps/2-i-1;
+			sbi->sb_c_bitmap = n_bmps/2-i-1;
 			goto ret;
 		}
 	}
 	if ((sec = alloc_in_bmp(s, (n_bmps-1) << 14, n, forward))) {
-		s->s_hpfs_c_bitmap = n_bmps-1;
+		sbi->sb_c_bitmap = n_bmps-1;
 		goto ret;
 	}
 	if (!f_p) {
 		for (i = 0; i < n_bmps; i++)
 			if ((sec = alloc_in_bmp(s, i << 14, n, 0))) {
-				s->s_hpfs_c_bitmap = 0x10000000 + i;
+				sbi->sb_c_bitmap = 0x10000000 + i;
 				goto ret;
 			}
 	}
@@ -212,17 +213,18 @@
 {
 	unsigned nr = near;
 	secno sec;
-	if (nr < s->s_hpfs_dirband_start)
-		nr = s->s_hpfs_dirband_start;
-	if (nr >= s->s_hpfs_dirband_start + s->s_hpfs_dirband_size)
-		nr = s->s_hpfs_dirband_start + s->s_hpfs_dirband_size - 4;
-	nr -= s->s_hpfs_dirband_start;
+	struct hpfs_sb_info *sbi = hpfs_sb(s);
+	if (nr < sbi->sb_dirband_start)
+		nr = sbi->sb_dirband_start;
+	if (nr >= sbi->sb_dirband_start + sbi->sb_dirband_size)
+		nr = sbi->sb_dirband_start + sbi->sb_dirband_size - 4;
+	nr -= sbi->sb_dirband_start;
 	nr >>= 2;
 	if (lock) hpfs_lock_creation(s);
 	sec = alloc_in_bmp(s, (~0x3fff) | nr, 1, 0);
 	if (lock) hpfs_unlock_creation(s);
 	if (!sec) return 0;
-	return ((sec & 0x3fff) << 2) + s->s_hpfs_dirband_start;
+	return ((sec & 0x3fff) << 2) + sbi->sb_dirband_start;
 }
 
 /* Alloc sector if it's free */
@@ -303,8 +305,8 @@
 
 int hpfs_check_free_dnodes(struct super_block *s, int n)
 {
-	int n_bmps = (s->s_hpfs_fs_size + 0x4000 - 1) >> 14;
-	int b = s->s_hpfs_c_bitmap & 0x0fffffff;
+	int n_bmps = (hpfs_sb(s)->sb_fs_size + 0x4000 - 1) >> 14;
+	int b = hpfs_sb(s)->sb_c_bitmap & 0x0fffffff;
 	int i, j;
 	unsigned *bmp;
 	struct quad_buffer_head qbh;
@@ -320,7 +322,7 @@
 	}
 	hpfs_brelse4(&qbh);
 	i = 0;
-	if (s->s_hpfs_c_bitmap != -1 ) {
+	if (hpfs_sb(s)->sb_c_bitmap != -1 ) {
 		bmp = hpfs_map_bitmap(s, b, &qbh, "chkdn1");
 		goto chk_bmp;
 	}
@@ -349,17 +351,17 @@
 
 void hpfs_free_dnode(struct super_block *s, dnode_secno dno)
 {
-	if (s->s_hpfs_chk) if (dno & 3) {
+	if (hpfs_sb(s)->sb_chk) if (dno & 3) {
 		hpfs_error(s, "hpfs_free_dnode: dnode %08x not aligned", dno);
 		return;
 	}
-	if (dno < s->s_hpfs_dirband_start ||
-	    dno >= s->s_hpfs_dirband_start + s->s_hpfs_dirband_size) {
+	if (dno < hpfs_sb(s)->sb_dirband_start ||
+	    dno >= hpfs_sb(s)->sb_dirband_start + hpfs_sb(s)->sb_dirband_size) {
 		hpfs_free_sectors(s, dno, 4);
 	} else {
 		struct quad_buffer_head qbh;
 		unsigned *bmp;
-		unsigned ssec = (dno - s->s_hpfs_dirband_start) / 4;
+		unsigned ssec = (dno - hpfs_sb(s)->sb_dirband_start) / 4;
 		lock_super(s);
 		if (!(bmp = hpfs_map_dnode_bitmap(s, &qbh))) {
 			unlock_super(s);
@@ -377,7 +379,7 @@
 			 int lock)
 {
 	struct dnode *d;
-	if (hpfs_count_one_bitmap(s, s->s_hpfs_dmap) > FREE_DNODES_ADD) {
+	if (hpfs_count_one_bitmap(s, hpfs_sb(s)->sb_dmap) > FREE_DNODES_ADD) {
 		if (!(*dno = alloc_in_dirband(s, near, lock)))
 			if (!(*dno = hpfs_alloc_sector(s, near, 4, 0, lock))) return NULL;
 	} else {
diff -Nru a/fs/hpfs/anode.c b/fs/hpfs/anode.c
--- a/fs/hpfs/anode.c	Mon Oct  7 11:25:25 2002
+++ b/fs/hpfs/anode.c	Mon Oct  7 11:25:25 2002
@@ -20,7 +20,7 @@
 	int i;
 	int c1, c2 = 0;
 	go_down:
-	if (s->s_hpfs_chk) if (hpfs_stop_cycles(s, a, &c1, &c2, "hpfs_bplus_lookup")) return -1;
+	if (hpfs_sb(s)->sb_chk) if (hpfs_stop_cycles(s, a, &c1, &c2, "hpfs_bplus_lookup")) return -1;
 	if (btree->internal) {
 		for (i = 0; i < btree->n_used_nodes; i++)
 			if (btree->u.internal[i].file_secno > sec) {
@@ -38,7 +38,7 @@
 		if (btree->u.external[i].file_secno <= sec &&
 		    btree->u.external[i].file_secno + btree->u.external[i].length > sec) {
 			a = btree->u.external[i].disk_secno + sec - btree->u.external[i].file_secno;
-			if (s->s_hpfs_chk) if (hpfs_chk_sectors(s, a, 1, "data")) {
+			if (hpfs_sb(s)->sb_chk) if (hpfs_chk_sectors(s, a, 1, "data")) {
 				brelse(bh);
 				return -1;
 			}
@@ -88,7 +88,7 @@
 		btree->u.internal[n].file_secno = -1;
 		mark_buffer_dirty(bh);
 		brelse(bh);
-		if (s->s_hpfs_chk)
+		if (hpfs_sb(s)->sb_chk)
 			if (hpfs_stop_cycles(s, a, &c1, &c2, "hpfs_add_sector_to_btree #1")) return -1;
 		if (!(anode = hpfs_map_anode(s, a, &bh))) return -1;
 		btree = &anode->btree;
@@ -164,7 +164,7 @@
 	c2 = 0;
 	while (up != -1) {
 		struct anode *new_anode;
-		if (s->s_hpfs_chk)
+		if (hpfs_sb(s)->sb_chk)
 			if (hpfs_stop_cycles(s, up, &c1, &c2, "hpfs_add_sector_to_btree #2")) return -1;
 		if (up != node || !fnod) {
 			if (!(anode = hpfs_map_anode(s, up, &bh))) return -1;
@@ -283,7 +283,7 @@
 	while (btree1->internal) {
 		ano = btree1->u.internal[pos].down;
 		if (level) brelse(bh);
-		if (s->s_hpfs_chk)
+		if (hpfs_sb(s)->sb_chk)
 			if (hpfs_stop_cycles(s, ano, &d1, &d2, "hpfs_remove_btree #1"))
 				return;
 		if (!(anode = hpfs_map_anode(s, ano, &bh))) return;
@@ -296,7 +296,7 @@
 	go_up:
 	if (!level) return;
 	brelse(bh);
-	if (s->s_hpfs_chk)
+	if (hpfs_sb(s)->sb_chk)
 		if (hpfs_stop_cycles(s, ano, &c1, &c2, "hpfs_remove_btree #2")) return;
 	hpfs_free_sectors(s, ano, 1);
 	oano = ano;
@@ -343,7 +343,7 @@
 			if ((sec = anode_lookup(s, a, pos >> 9)) == -1)
 				return -1;
 		} else sec = a + (pos >> 9);
-		if (s->s_hpfs_chk) if (hpfs_chk_sectors(s, sec, 1, "ea #1")) return -1;
+		if (hpfs_sb(s)->sb_chk) if (hpfs_chk_sectors(s, sec, 1, "ea #1")) return -1;
 		if (!(data = hpfs_map_sector(s, sec, &bh, (len - 1) >> 9)))
 			return -1;
 		l = 0x200 - (pos & 0x1ff); if (l > len) l = len;
@@ -366,7 +366,7 @@
 			if ((sec = anode_lookup(s, a, pos >> 9)) == -1)
 				return -1;
 		} else sec = a + (pos >> 9);
-		if (s->s_hpfs_chk) if (hpfs_chk_sectors(s, sec, 1, "ea #2")) return -1;
+		if (hpfs_sb(s)->sb_chk) if (hpfs_chk_sectors(s, sec, 1, "ea #2")) return -1;
 		if (!(data = hpfs_map_sector(s, sec, &bh, (len - 1) >> 9)))
 			return -1;
 		l = 0x200 - (pos & 0x1ff); if (l > len) l = len;
@@ -440,7 +440,7 @@
 		}
 		node = btree->u.internal[i].down;
 		brelse(bh);
-		if (s->s_hpfs_chk)
+		if (hpfs_sb(s)->sb_chk)
 			if (hpfs_stop_cycles(s, node, &c1, &c2, "hpfs_truncate_btree"))
 				return;
 		if (!(anode = hpfs_map_anode(s, node, &bh))) return;
diff -Nru a/fs/hpfs/buffer.c b/fs/hpfs/buffer.c
--- a/fs/hpfs/buffer.c	Mon Oct  7 11:25:35 2002
+++ b/fs/hpfs/buffer.c	Mon Oct  7 11:25:35 2002
@@ -15,7 +15,7 @@
 #ifdef DEBUG_LOCKS
 	printk("lock creation\n");
 #endif
-	down(&s->u.hpfs_sb.hpfs_creation_de);
+	down(&hpfs_sb(s)->hpfs_creation_de);
 }
 
 void hpfs_unlock_creation(struct super_block *s)
@@ -23,7 +23,7 @@
 #ifdef DEBUG_LOCKS
 	printk("unlock creation\n");
 #endif
-	up(&s->u.hpfs_sb.hpfs_creation_de);
+	up(&hpfs_sb(s)->hpfs_creation_de);
 }
 
 void hpfs_lock_iget(struct super_block *s, int mode)
@@ -31,8 +31,8 @@
 #ifdef DEBUG_LOCKS
 	printk("lock iget\n");
 #endif
-	while (s->s_hpfs_rd_inode) sleep_on(&s->s_hpfs_iget_q);
-	s->s_hpfs_rd_inode = mode;
+	while (hpfs_sb(s)->sb_rd_inode) sleep_on(&hpfs_sb(s)->sb_iget_q);
+	hpfs_sb(s)->sb_rd_inode = mode;
 }
 
 void hpfs_unlock_iget(struct super_block *s)
@@ -40,8 +40,8 @@
 #ifdef DEBUG_LOCKS
 	printk("unlock iget\n");
 #endif
-	s->s_hpfs_rd_inode = 0;
-	wake_up(&s->s_hpfs_iget_q);
+	hpfs_sb(s)->sb_rd_inode = 0;
+	wake_up(&hpfs_sb(s)->sb_iget_q);
 }
 
 void hpfs_lock_inode(struct inode *i)
diff -Nru a/fs/hpfs/dentry.c b/fs/hpfs/dentry.c
--- a/fs/hpfs/dentry.c	Mon Oct  7 11:25:39 2002
+++ b/fs/hpfs/dentry.c	Mon Oct  7 11:25:39 2002
@@ -28,7 +28,7 @@
 
 	hash = init_name_hash();
 	for (i = 0; i < l; i++)
-		hash = partial_name_hash(hpfs_upcase(dentry->d_sb->s_hpfs_cp_table,qstr->name[i]), hash);
+		hash = partial_name_hash(hpfs_upcase(hpfs_sb(dentry->d_sb)->sb_cp_table,qstr->name[i]), hash);
 	qstr->hash = end_name_hash(hash);
 
 	return 0;
diff -Nru a/fs/hpfs/dir.c b/fs/hpfs/dir.c
--- a/fs/hpfs/dir.c	Mon Oct  7 11:25:28 2002
+++ b/fs/hpfs/dir.c	Mon Oct  7 11:25:28 2002
@@ -65,7 +65,7 @@
 	int c1, c2 = 0;
 	int ret = 0;
 
-	if (inode->i_sb->s_hpfs_chk) {
+	if (hpfs_sb(inode->i_sb)->sb_chk) {
 		if (hpfs_chk_sectors(inode->i_sb, inode->i_ino, 1, "dir_fnode")) {
 			ret = -EFSERROR;
 			goto out;
@@ -75,7 +75,7 @@
 			goto out;
 		}
 	}
-	if (inode->i_sb->s_hpfs_chk >= 2) {
+	if (hpfs_sb(inode->i_sb)->sb_chk >= 2) {
 		struct buffer_head *bh;
 		struct fnode *fno;
 		int e = 0;
@@ -97,7 +97,7 @@
 			goto out;
 		}
 	}
-	lc = inode->i_sb->s_hpfs_lowercase;
+	lc = hpfs_sb(inode->i_sb)->sb_lowercase;
 	if (filp->f_pos == 12) { /* diff -r requires this (note, that diff -r */
 		filp->f_pos = 13; /* also fails on msdos filesystem in 2.0) */
 		goto out;
@@ -114,7 +114,7 @@
 		/* This won't work when cycle is longer than number of dirents
 		   accepted by filldir, but what can I do?
 		   maybe killall -9 ls helps */
-		if (inode->i_sb->s_hpfs_chk)
+		if (hpfs_sb(inode->i_sb)->sb_chk)
 			if (hpfs_stop_cycles(inode->i_sb, filp->f_pos, &c1, &c2, "hpfs_readdir")) {
 				hpfs_unlock_inode(inode);
 				ret = -EFSERROR;
@@ -160,7 +160,7 @@
 				goto out;
 			}
 			if (de->first || de->last) {
-				if (inode->i_sb->s_hpfs_chk) {
+				if (hpfs_sb(inode->i_sb)->sb_chk) {
 					if (de->first && !de->last && (de->namelen != 2 || de ->name[0] != 1 || de->name[1] != 1)) hpfs_error(inode->i_sb, "hpfs_readdir: bad ^A^A entry; pos = %08x", old_pos);
 					if (de->last && (de->namelen != 1 || de ->name[0] != 255)) hpfs_error(inode->i_sb, "hpfs_readdir: bad \\377 entry; pos = %08x", old_pos);
 				}
@@ -241,7 +241,7 @@
 	 * Go find or make an inode.
 	 */
 
-	hpfs_lock_iget(dir->i_sb, de->directory || (de->ea_size && dir->i_sb->s_hpfs_eas) ? 1 : 2);
+	hpfs_lock_iget(dir->i_sb, de->directory || (de->ea_size && hpfs_sb(dir->i_sb)->sb_eas) ? 1 : 2);
 	if (!(result = iget(dir->i_sb, ino))) {
 		hpfs_unlock_iget(dir->i_sb);
 		hpfs_error(dir->i_sb, "hpfs_lookup: can't get inode");
diff -Nru a/fs/hpfs/dnode.c b/fs/hpfs/dnode.c
--- a/fs/hpfs/dnode.c	Mon Oct  7 11:25:35 2002
+++ b/fs/hpfs/dnode.c	Mon Oct  7 11:25:35 2002
@@ -134,7 +134,7 @@
 		hpfs_error(s, "set_last_pointer: empty dnode %08x", d->self);
 		return;
 	}
-	if (s->s_hpfs_chk) {
+	if (hpfs_sb(s)->sb_chk) {
 		if (de->down) {
 			hpfs_error(s, "set_last_pointer: dnode %08x has already last pointer %08x",
 				d->self, de_down_pointer(de));
@@ -253,7 +253,7 @@
 		return 1;
 	}
 	go_up_a:
-	if (i->i_sb->s_hpfs_chk)
+	if (hpfs_sb(i->i_sb)->sb_chk)
 		if (hpfs_stop_cycles(i->i_sb, dno, &c1, &c2, "hpfs_add_to_dnode")) {
 			hpfs_brelse4(&qbh);
 			if (nd) kfree(nd);
@@ -379,7 +379,7 @@
 	int c1, c2 = 0;
 	dno = hpfs_inode->i_dno;
 	down:
-	if (i->i_sb->s_hpfs_chk)
+	if (hpfs_sb(i->i_sb)->sb_chk)
 		if (hpfs_stop_cycles(i->i_sb, dno, &c1, &c2, "hpfs_add_dirent")) return 1;
 	if (!(d = hpfs_map_dnode(i->i_sb, dno, &qbh))) return 1;
 	de_end = dnode_end_de(d);
@@ -427,11 +427,11 @@
 	int c1, c2 = 0;
 	dno = from;
 	while (1) {
-		if (i->i_sb->s_hpfs_chk)
+		if (hpfs_sb(i->i_sb)->sb_chk)
 			if (hpfs_stop_cycles(i->i_sb, dno, &c1, &c2, "move_to_top"))
 				return 0;
 		if (!(dnode = hpfs_map_dnode(i->i_sb, dno, &qbh))) return 0;
-		if (i->i_sb->s_hpfs_chk) {
+		if (hpfs_sb(i->i_sb)->sb_chk) {
 			if (dnode->up != chk_up) {
 				hpfs_error(i->i_sb, "move_to_top: up pointer from %08x should be %08x, is %08x",
 					dno, chk_up, dnode->up);
@@ -519,7 +519,7 @@
 		up = dnode->up;
 		de = dnode_first_de(dnode);
 		down = de->down ? de_down_pointer(de) : 0;
-		if (i->i_sb->s_hpfs_chk) if (root && !down) {
+		if (hpfs_sb(i->i_sb)->sb_chk) if (root && !down) {
 			hpfs_error(i->i_sb, "delete_empty_dnode: root dnode %08x is empty", dno);
 			goto end;
 		}
@@ -532,7 +532,7 @@
 			struct buffer_head *bh;
 			struct dnode *d1;
 			struct quad_buffer_head qbh1;
-			if (i->i_sb->s_hpfs_chk) if (up != i->i_ino) {
+			if (hpfs_sb(i->i_sb)->sb_chk) if (up != i->i_ino) {
 				hpfs_error(i->i_sb, "bad pointer to fnode, dnode %08x, pointing to %08x, should be %08x", dno, up, i->i_ino);
 				return;
 			}
@@ -628,14 +628,14 @@
 			dlp = del->down ? de_down_pointer(del) : 0;
 			if (!dlp && down) {
 				if (d1->first_free > 2044) {
-					if (i->i_sb->s_hpfs_chk >= 2) {
+					if (hpfs_sb(i->i_sb)->sb_chk >= 2) {
 						printk("HPFS: warning: unbalanced dnode tree, see hpfs.txt 4 more info\n");
 						printk("HPFS: warning: terminating balancing operation\n");
 					}
 					hpfs_brelse4(&qbh1);
 					goto endm;
 				}
-				if (i->i_sb->s_hpfs_chk >= 2) {
+				if (hpfs_sb(i->i_sb)->sb_chk >= 2) {
 					printk("HPFS: warning: unbalanced dnode tree, see hpfs.txt 4 more info\n");
 					printk("HPFS: warning: goin'on\n");
 				}
@@ -738,12 +738,12 @@
 	int d1, d2 = 0;
 	go_down:
 	if (n_dnodes) (*n_dnodes)++;
-	if (s->s_hpfs_chk)
+	if (hpfs_sb(s)->sb_chk)
 		if (hpfs_stop_cycles(s, dno, &c1, &c2, "hpfs_count_dnodes #1")) return;
 	ptr = 0;
 	go_up:
 	if (!(dnode = hpfs_map_dnode(s, dno, &qbh))) return;
-	if (s->s_hpfs_chk) if (odno && odno != -1 && dnode->up != odno)
+	if (hpfs_sb(s)->sb_chk) if (odno && odno != -1 && dnode->up != odno)
 		hpfs_error(s, "hpfs_count_dnodes: bad up pointer; dnode %08x, down %08x points to %08x", odno, dno, dnode->up);
 	de = dnode_first_de(dnode);
 	if (ptr) while(1) {
@@ -774,7 +774,7 @@
 		return;
 	}
 	hpfs_brelse4(&qbh);
-	if (s->s_hpfs_chk)
+	if (hpfs_sb(s)->sb_chk)
 		if (hpfs_stop_cycles(s, ptr, &d1, &d2, "hpfs_count_dnodes #2")) return;
 	odno = -1;
 	goto go_up;
@@ -811,11 +811,11 @@
 	int c1, c2 = 0;
 
 	again:
-	if (s->s_hpfs_chk)
+	if (hpfs_sb(s)->sb_chk)
 		if (hpfs_stop_cycles(s, d, &c1, &c2, "hpfs_de_as_down_as_possible"))
 			return d;
 	if (!(de = map_nth_dirent(s, d, 1, &qbh, NULL))) return dno;
-	if (s->s_hpfs_chk)
+	if (hpfs_sb(s)->sb_chk)
 		if (up && ((struct dnode *)qbh.data)->up != up)
 			hpfs_error(s, "hpfs_de_as_down_as_possible: bad up pointer; dnode %08x, down %08x points to %08x", up, d, ((struct dnode *)qbh.data)->up);
 	if (!de->down) {
@@ -901,7 +901,7 @@
 
 	if (!S_ISDIR(inode->i_mode)) hpfs_error(inode->i_sb, "map_dirent: not a directory\n");
 	again:
-	if (inode->i_sb->s_hpfs_chk)
+	if (hpfs_sb(inode->i_sb)->sb_chk)
 		if (hpfs_stop_cycles(inode->i_sb, dno, &c1, &c2, "map_dirent")) return NULL;
 	if (!(dnode = hpfs_map_dnode(inode->i_sb, dno, qbh))) return NULL;
 	
@@ -1046,7 +1046,7 @@
 	if (c < 0 && de->down) {
 		dno = de_down_pointer(de);
 		hpfs_brelse4(qbh);
-		if (s->s_hpfs_chk)
+		if (hpfs_sb(s)->sb_chk)
 			if (hpfs_stop_cycles(s, dno, &c1, &c2, "map_fnode_dirent #1")) {
 			kfree(name2);
 			return NULL;
@@ -1065,7 +1065,7 @@
 	downd = dno;
 	dno = d->up;
 	hpfs_brelse4(qbh);
-	if (s->s_hpfs_chk)
+	if (hpfs_sb(s)->sb_chk)
 		if (hpfs_stop_cycles(s, downd, &d1, &d2, "map_fnode_dirent #2")) {
 			kfree(name2);
 			return NULL;
diff -Nru a/fs/hpfs/hpfs_fn.h b/fs/hpfs/hpfs_fn.h
--- a/fs/hpfs/hpfs_fn.h	Mon Oct  7 11:25:35 2002
+++ b/fs/hpfs/hpfs_fn.h	Mon Oct  7 11:25:35 2002
@@ -13,6 +13,7 @@
 #include <linux/fs.h>
 #include <linux/hpfs_fs.h>
 #include <linux/hpfs_fs_i.h>
+#include <linux/hpfs_fs_sb.h>
 #include <linux/errno.h>
 #include <linux/slab.h>
 #include <linux/kernel.h>
@@ -58,22 +59,6 @@
 typedef void nonconst; /* What this is for ? */
 
 /*
- * local time (HPFS) to GMT (Unix)
- */
-
-extern inline time_t local_to_gmt(struct super_block *s, time_t t)
-{
-	extern struct timezone sys_tz;
-	return t + sys_tz.tz_minuteswest * 60 + s->s_hpfs_timeshift;
-}
-
-extern inline time_t gmt_to_local(struct super_block *s, time_t t)
-{
-	extern struct timezone sys_tz;
-	return t - sys_tz.tz_minuteswest * 60 - s->s_hpfs_timeshift;
-}
-
-/*
  * conv= options
  */
 
@@ -309,6 +294,11 @@
 	return list_entry(inode, struct hpfs_inode_info, vfs_inode);
 }
 
+static inline struct hpfs_sb_info *hpfs_sb(struct super_block *sb)
+{
+	return sb->u.generic_sbp;
+}
+
 /* super.c */
 
 void hpfs_error(struct super_block *, char *, ...);
@@ -319,3 +309,20 @@
 int hpfs_statfs(struct super_block *, struct statfs *);
 
 extern struct address_space_operations hpfs_aops;
+
+/*
+ * local time (HPFS) to GMT (Unix)
+ */
+
+extern inline time_t local_to_gmt(struct super_block *s, time_t t)
+{
+	extern struct timezone sys_tz;
+	return t + sys_tz.tz_minuteswest * 60 + hpfs_sb(s)->sb_timeshift;
+}
+
+extern inline time_t gmt_to_local(struct super_block *s, time_t t)
+{
+	extern struct timezone sys_tz;
+	return t - sys_tz.tz_minuteswest * 60 - hpfs_sb(s)->sb_timeshift;
+}
+
diff -Nru a/fs/hpfs/inode.c b/fs/hpfs/inode.c
--- a/fs/hpfs/inode.c	Mon Oct  7 11:25:29 2002
+++ b/fs/hpfs/inode.c	Mon Oct  7 11:25:29 2002
@@ -66,10 +66,10 @@
 	unsigned char *ea;
 	int ea_size;
 
-	i->i_uid = sb->s_hpfs_uid;
-	i->i_gid = sb->s_hpfs_gid;
-	i->i_mode = sb->s_hpfs_mode;
-	hpfs_inode->i_conv = sb->s_hpfs_conv;
+	i->i_uid = hpfs_sb(sb)->sb_uid;
+	i->i_gid = hpfs_sb(sb)->sb_gid;
+	i->i_mode = hpfs_sb(sb)->sb_mode;
+	hpfs_inode->i_conv = hpfs_sb(sb)->sb_conv;
 	i->i_blksize = 512;
 	i->i_size = -1;
 	i->i_blocks = -1;
@@ -93,9 +93,9 @@
 	i->i_mtime = 0;
 	i->i_ctime = 0;
 
-	if (!i->i_sb->s_hpfs_rd_inode)
-		hpfs_error(i->i_sb, "read_inode: s_hpfs_rd_inode == 0");
-	if (i->i_sb->s_hpfs_rd_inode == 2) {
+	if (!hpfs_sb(i->i_sb)->sb_rd_inode)
+		hpfs_error(i->i_sb, "read_inode: sb_rd_inode == 0");
+	if (hpfs_sb(i->i_sb)->sb_rd_inode == 2) {
 		i->i_mode |= S_IFREG;
 		i->i_mode &= ~0111;
 		i->i_op = &hpfs_file_iops;
@@ -112,7 +112,7 @@
 		make_bad_inode(i);
 		return;
 	}
-	if (i->i_sb->s_hpfs_eas) {
+	if (hpfs_sb(i->i_sb)->sb_eas) {
 		if ((ea = hpfs_get_ea(i->i_sb, fnode, "UID", &ea_size))) {
 			if (ea_size == 2) {
 				i->i_uid = ea[0] + (ea[1] << 8);
@@ -140,7 +140,7 @@
 		}
 		if ((ea = hpfs_get_ea(i->i_sb, fnode, "MODE", &ea_size))) {
 			int rdev = 0;
-			umode_t mode = sb->s_hpfs_mode;
+			umode_t mode = hpfs_sb(sb)->sb_mode;
 			if (ea_size == 2) {
 				mode = ea[0] + (ea[1] << 8);
 				hpfs_inode->i_ea_mode = 1;
@@ -171,7 +171,7 @@
 		i->i_fop = &hpfs_dir_ops;
 		hpfs_inode->i_parent_dir = fnode->up;
 		hpfs_inode->i_dno = fnode->u.external[0].disk_secno;
-		if (sb->s_hpfs_chk >= 2) {
+		if (hpfs_sb(sb)->sb_chk >= 2) {
 			struct buffer_head *bh0;
 			if (hpfs_map_fnode(sb, hpfs_inode->i_parent_dir, &bh0)) brelse(bh0);
 		}
@@ -201,24 +201,24 @@
 		/* Some unknown structures like ACL may be in fnode,
 		   we'd better not overwrite them */
 		hpfs_error(i->i_sb, "fnode %08x has some unknown HPFS386 stuctures", i->i_ino);
-	} else if (i->i_sb->s_hpfs_eas >= 2) {
+	} else if (hpfs_sb(i->i_sb)->sb_eas >= 2) {
 		unsigned char ea[4];
-		if ((i->i_uid != i->i_sb->s_hpfs_uid) || hpfs_inode->i_ea_uid) {
+		if ((i->i_uid != hpfs_sb(i->i_sb)->sb_uid) || hpfs_inode->i_ea_uid) {
 			ea[0] = i->i_uid & 0xff;
 			ea[1] = i->i_uid >> 8;
 			hpfs_set_ea(i, fnode, "UID", ea, 2);
 			hpfs_inode->i_ea_uid = 1;
 		}
-		if ((i->i_gid != i->i_sb->s_hpfs_gid) || hpfs_inode->i_ea_gid) {
+		if ((i->i_gid != hpfs_sb(i->i_sb)->sb_gid) || hpfs_inode->i_ea_gid) {
 			ea[0] = i->i_gid & 0xff;
 			ea[1] = i->i_gid >> 8;
 			hpfs_set_ea(i, fnode, "GID", ea, 2);
 			hpfs_inode->i_ea_gid = 1;
 		}
 		if (!S_ISLNK(i->i_mode))
-			if ((i->i_mode != ((i->i_sb->s_hpfs_mode & ~(S_ISDIR(i->i_mode) ? 0 : 0111))
+			if ((i->i_mode != ((hpfs_sb(i->i_sb)->sb_mode & ~(S_ISDIR(i->i_mode) ? 0 : 0111))
 			  | (S_ISDIR(i->i_mode) ? S_IFDIR : S_IFREG))
-			  && i->i_mode != ((i->i_sb->s_hpfs_mode & ~(S_ISDIR(i->i_mode) ? 0222 : 0333))
+			  && i->i_mode != ((hpfs_sb(i->i_sb)->sb_mode & ~(S_ISDIR(i->i_mode) ? 0222 : 0333))
 			  | (S_ISDIR(i->i_mode) ? S_IFDIR : S_IFREG))) || hpfs_inode->i_ea_mode) {
 				ea[0] = i->i_mode & 0xff;
 				ea[1] = i->i_mode >> 8;
@@ -241,7 +241,7 @@
 	struct hpfs_inode_info *hpfs_inode = hpfs_i(i);
 	struct inode *parent;
 	if (!i->i_nlink) return;
-	if (i->i_ino == i->i_sb->s_hpfs_root) return;
+	if (i->i_ino == hpfs_sb(i->i_sb)->sb_root) return;
 	if (hpfs_inode->i_rddir_off && !atomic_read(&i->i_count)) {
 		if (*hpfs_inode->i_rddir_off) printk("HPFS: write_inode: some position still there\n");
 		kfree(hpfs_inode->i_rddir_off);
@@ -264,9 +264,9 @@
 	struct fnode *fnode;
 	struct quad_buffer_head qbh;
 	struct hpfs_dirent *de;
-	if (i->i_ino == i->i_sb->s_hpfs_root) return;
+	if (i->i_ino == hpfs_sb(i->i_sb)->sb_root) return;
 	if (!(fnode = hpfs_map_fnode(i->i_sb, i->i_ino, &bh))) return;
-	if (i->i_ino != i->i_sb->s_hpfs_root) {
+	if (i->i_ino != hpfs_sb(i->i_sb)->sb_root) {
 		if (!(de = map_fnode_dirent(i->i_sb, i->i_ino, fnode, &qbh))) {
 			brelse(bh);
 			return;
@@ -309,7 +309,7 @@
 	int error=0;
 	lock_kernel();
 	if ( ((attr->ia_valid & ATTR_SIZE) && attr->ia_size > inode->i_size) ||
-	     (inode->i_sb->s_hpfs_root == inode->i_ino) ) {
+	     (hpfs_sb(inode->i_sb)->sb_root == inode->i_ino) ) {
 		error = -EINVAL;
 	} else if ((error = inode_change_ok(inode, attr))) {
 	} else if ((error = inode_setattr(inode, attr))) {
diff -Nru a/fs/hpfs/map.c b/fs/hpfs/map.c
--- a/fs/hpfs/map.c	Mon Oct  7 11:25:28 2002
+++ b/fs/hpfs/map.c	Mon Oct  7 11:25:28 2002
@@ -11,19 +11,19 @@
 
 unsigned *hpfs_map_dnode_bitmap(struct super_block *s, struct quad_buffer_head *qbh)
 {
-	return hpfs_map_4sectors(s, s->s_hpfs_dmap, qbh, 0);
+	return hpfs_map_4sectors(s, hpfs_sb(s)->sb_dmap, qbh, 0);
 }
 
 unsigned int *hpfs_map_bitmap(struct super_block *s, unsigned bmp_block,
 			 struct quad_buffer_head *qbh, char *id)
 {
 	secno sec;
-	if (s->s_hpfs_chk) if (bmp_block * 16384 > s->s_hpfs_fs_size) {
+	if (hpfs_sb(s)->sb_chk) if (bmp_block * 16384 > hpfs_sb(s)->sb_fs_size) {
 		hpfs_error(s, "hpfs_map_bitmap called with bad parameter: %08x at %s", bmp_block, id);
 		return NULL;
 	}
-	sec = s->s_hpfs_bmp_dir[bmp_block];
-	if (!sec || sec > s->s_hpfs_fs_size-4) {
+	sec = hpfs_sb(s)->sb_bmp_dir[bmp_block];
+	if (!sec || sec > hpfs_sb(s)->sb_fs_size-4) {
 		hpfs_error(s, "invalid bitmap block pointer %08x -> %08x at %s", bmp_block, sec, id);
 		return NULL;
 	}
@@ -93,7 +93,7 @@
 secno *hpfs_load_bitmap_directory(struct super_block *s, secno bmp)
 {
 	struct buffer_head *bh;
-	int n = (s->s_hpfs_fs_size + 0x200000 - 1) >> 21;
+	int n = (hpfs_sb(s)->sb_fs_size + 0x200000 - 1) >> 21;
 	int i;
 	secno *b;
 	if (!(b = kmalloc(n * 512, GFP_KERNEL))) {
@@ -119,11 +119,11 @@
 struct fnode *hpfs_map_fnode(struct super_block *s, ino_t ino, struct buffer_head **bhp)
 {
 	struct fnode *fnode;
-	if (s->s_hpfs_chk) if (hpfs_chk_sectors(s, ino, 1, "fnode")) {
+	if (hpfs_sb(s)->sb_chk) if (hpfs_chk_sectors(s, ino, 1, "fnode")) {
 		return NULL;
 	}
 	if ((fnode = hpfs_map_sector(s, ino, bhp, FNODE_RD_AHEAD))) {
-		if (s->s_hpfs_chk) {
+		if (hpfs_sb(s)->sb_chk) {
 			struct extended_attribute *ea;
 			struct extended_attribute *ea_end;
 			if (fnode->magic != FNODE_MAGIC) {
@@ -168,9 +168,9 @@
 struct anode *hpfs_map_anode(struct super_block *s, anode_secno ano, struct buffer_head **bhp)
 {
 	struct anode *anode;
-	if (s->s_hpfs_chk) if (hpfs_chk_sectors(s, ano, 1, "anode")) return NULL;
+	if (hpfs_sb(s)->sb_chk) if (hpfs_chk_sectors(s, ano, 1, "anode")) return NULL;
 	if ((anode = hpfs_map_sector(s, ano, bhp, ANODE_RD_AHEAD)))
-		if (s->s_hpfs_chk) {
+		if (hpfs_sb(s)->sb_chk) {
 			if (anode->magic != ANODE_MAGIC || anode->self != ano) {
 				hpfs_error(s, "bad magic on anode %08x", ano);
 				goto bail;
@@ -200,7 +200,7 @@
 			     struct quad_buffer_head *qbh)
 {
 	struct dnode *dnode;
-	if (s->s_hpfs_chk) {
+	if (hpfs_sb(s)->sb_chk) {
 		if (hpfs_chk_sectors(s, secno, 4, "dnode")) return NULL;
 		if (secno & 3) {
 			hpfs_error(s, "dnode %08x not byte-aligned", secno);
@@ -208,7 +208,7 @@
 		}	
 	}
 	if ((dnode = hpfs_map_4sectors(s, secno, qbh, DNODE_RD_AHEAD)))
-		if (s->s_hpfs_chk) {
+		if (hpfs_sb(s)->sb_chk) {
 			unsigned p, pp = 0;
 			unsigned char *d = (char *)dnode;
 			int b = 0;
@@ -234,7 +234,7 @@
 					hpfs_error(s, "namelen does not match dirent size in dnode %08x, dirent %03x, last %03x", secno, p, pp);
 					goto bail;
 				}
-				if (s->s_hpfs_chk >= 2) b |= 1 << de->down;
+				if (hpfs_sb(s)->sb_chk >= 2) b |= 1 << de->down;
 				if (de->down) if (de_down_pointer(de) < 0x10) {
 					hpfs_error(s, "bad down pointer in dnode %08x, dirent %03x, last %03x", secno, p, pp);
 					goto bail;
diff -Nru a/fs/hpfs/name.c b/fs/hpfs/name.c
--- a/fs/hpfs/name.c	Mon Oct  7 11:25:37 2002
+++ b/fs/hpfs/name.c	Mon Oct  7 11:25:37 2002
@@ -89,7 +89,7 @@
 {
 	char *to;
 	int i;
-	if (s->s_hpfs_chk >= 2) if (hpfs_is_name_long(from, len) != lng) {
+	if (hpfs_sb(s)->sb_chk >= 2) if (hpfs_is_name_long(from, len) != lng) {
 		printk("HPFS: Long name flag mismatch - name ");
 		for (i=0; i<len; i++) printk("%c", from[i]);
 		printk(" misidentified as %s.\n", lng ? "short" : "long");
@@ -100,7 +100,7 @@
 		printk("HPFS: can't allocate memory for name conversion buffer\n");
 		return from;
 	}
-	for (i = 0; i < len; i++) to[i] = locase(s->s_hpfs_cp_table,from[i]);
+	for (i = 0; i < len; i++) to[i] = locase(hpfs_sb(s)->sb_cp_table,from[i]);
 	return to;
 }
 
@@ -111,8 +111,8 @@
 	unsigned i;
 	if (last) return -1;
 	for (i = 0; i < l; i++) {
-		unsigned char c1 = upcase(s->s_hpfs_cp_table,n1[i]);
-		unsigned char c2 = upcase(s->s_hpfs_cp_table,n2[i]);
+		unsigned char c1 = upcase(hpfs_sb(s)->sb_cp_table,n1[i]);
+		unsigned char c2 = upcase(hpfs_sb(s)->sb_cp_table,n2[i]);
 		if (c1 < c2) return -1;
 		if (c1 > c2) return 1;
 	}
diff -Nru a/fs/hpfs/namei.c b/fs/hpfs/namei.c
--- a/fs/hpfs/namei.c	Mon Oct  7 11:25:33 2002
+++ b/fs/hpfs/namei.c	Mon Oct  7 11:25:33 2002
@@ -187,7 +187,7 @@
 	struct inode *result = NULL;
 	int err;
 	if ((err = hpfs_chk_name((char *)name, &len))) return err==-ENOENT ? -EINVAL : err;
-	if (dir->i_sb->s_hpfs_eas < 2) return -EPERM;
+	if (hpfs_sb(dir->i_sb)->sb_eas < 2) return -EPERM;
 	lock_kernel();
 	if (!(fnode = hpfs_alloc_fnode(dir->i_sb, hpfs_i(dir)->i_dno, &fno, &bh))) goto bail;
 	memset(&dee, 0, sizeof dee);
@@ -255,7 +255,7 @@
 	int err;
 	if ((err = hpfs_chk_name((char *)name, &len))) return err==-ENOENT ? -EINVAL : err;
 	lock_kernel();
-	if (dir->i_sb->s_hpfs_eas < 2) {
+	if (hpfs_sb(dir->i_sb)->sb_eas < 2) {
 		unlock_kernel();
 		return -EPERM;
 	}
@@ -559,7 +559,7 @@
 		mark_buffer_dirty(bh);
 		brelse(bh);
 	}
-	hpfs_i(i)->i_conv = i->i_sb->s_hpfs_conv;
+	hpfs_i(i)->i_conv = hpfs_sb(i->i_sb)->sb_conv;
 	hpfs_decide_conv(i, (char *)new_name, new_len);
 	end1:
 	hpfs_unlock_3inodes(old_dir, new_dir, i);
diff -Nru a/fs/hpfs/super.c b/fs/hpfs/super.c
--- a/fs/hpfs/super.c	Mon Oct  7 11:25:35 2002
+++ b/fs/hpfs/super.c	Mon Oct  7 11:25:35 2002
@@ -16,7 +16,7 @@
 
 static void mark_dirty(struct super_block *s)
 {
-	if (s->s_hpfs_chkdsk && !(s->s_flags & MS_RDONLY)) {
+	if (hpfs_sb(s)->sb_chkdsk && !(s->s_flags & MS_RDONLY)) {
 		struct buffer_head *bh;
 		struct hpfs_spare_block *sb;
 		if ((sb = hpfs_map_sector(s, 17, &bh, 0))) {
@@ -37,8 +37,8 @@
 	struct hpfs_spare_block *sb;
 	if (s->s_flags & MS_RDONLY) return;
 	if ((sb = hpfs_map_sector(s, 17, &bh, 0))) {
-		sb->dirty = s->s_hpfs_chkdsk > 1 - s->s_hpfs_was_error;
-		sb->old_wrote = s->s_hpfs_chkdsk >= 2 && !s->s_hpfs_was_error;
+		sb->dirty = hpfs_sb(s)->sb_chkdsk > 1 - hpfs_sb(s)->sb_was_error;
+		sb->old_wrote = hpfs_sb(s)->sb_chkdsk >= 2 && !hpfs_sb(s)->sb_was_error;
 		mark_buffer_dirty(bh);
 		brelse(bh);
 	}
@@ -60,12 +60,12 @@
 	printk("HPFS: filesystem error: ");
 	if (buf) printk("%s", buf);
 	else printk("%s\n",m);
-	if (!s->s_hpfs_was_error) {
-		if (s->s_hpfs_err == 2) {
+	if (!hpfs_sb(s)->sb_was_error) {
+		if (hpfs_sb(s)->sb_err == 2) {
 			printk("; crashing the system because you wanted it\n");
 			mark_dirty(s);
 			panic("HPFS panic");
-		} else if (s->s_hpfs_err == 1) {
+		} else if (hpfs_sb(s)->sb_err == 1) {
 			if (s->s_flags & MS_RDONLY) printk("; already mounted read-only\n");
 			else {
 				printk("; remounting read-only\n");
@@ -76,7 +76,7 @@
 		else printk("; corrupted filesystem mounted read/write - your computer will explode within 20 seconds ... but you wanted it so!\n");
 	} else printk("\n");
 	if (buf) kfree(buf);
-	s->s_hpfs_was_error = 1;
+	hpfs_sb(s)->sb_was_error = 1;
 }
 
 /* 
@@ -101,9 +101,12 @@
 
 void hpfs_put_super(struct super_block *s)
 {
-	if (s->s_hpfs_cp_table) kfree(s->s_hpfs_cp_table);
-	if (s->s_hpfs_bmp_dir) kfree(s->s_hpfs_bmp_dir);
+	struct hpfs_sb_info *sbi = hpfs_sb(s);
+	if (sbi->sb_cp_table) kfree(sbi->sb_cp_table);
+	if (sbi->sb_bmp_dir) kfree(sbi->sb_bmp_dir);
 	unmark_dirty(s);
+	s->u.generic_sbp = NULL;
+	kfree(sbi);
 }
 
 unsigned hpfs_count_one_bitmap(struct super_block *s, secno secno)
@@ -125,28 +128,29 @@
 static unsigned count_bitmaps(struct super_block *s)
 {
 	unsigned n, count, n_bands;
-	n_bands = (s->s_hpfs_fs_size + 0x3fff) >> 14;
+	n_bands = (hpfs_sb(s)->sb_fs_size + 0x3fff) >> 14;
 	count = 0;
 	for (n = 0; n < n_bands; n++)
-		count += hpfs_count_one_bitmap(s, s->s_hpfs_bmp_dir[n]);
+		count += hpfs_count_one_bitmap(s, hpfs_sb(s)->sb_bmp_dir[n]);
 	return count;
 }
 
 int hpfs_statfs(struct super_block *s, struct statfs *buf)
 {
+	struct hpfs_sb_info *sbi = hpfs_sb(s);
 	lock_kernel();
 
-	/*if (s->s_hpfs_n_free == -1) {*/
-		s->s_hpfs_n_free = count_bitmaps(s);
-		s->s_hpfs_n_free_dnodes = hpfs_count_one_bitmap(s, s->s_hpfs_dmap);
+	/*if (sbi->sb_n_free == -1) {*/
+		sbi->sb_n_free = count_bitmaps(s);
+		sbi->sb_n_free_dnodes = hpfs_count_one_bitmap(s, sbi->sb_dmap);
 	/*}*/
 	buf->f_type = s->s_magic;
 	buf->f_bsize = 512;
-	buf->f_blocks = s->s_hpfs_fs_size;
-	buf->f_bfree = s->s_hpfs_n_free;
-	buf->f_bavail = s->s_hpfs_n_free;
-	buf->f_files = s->s_hpfs_dirband_size / 4;
-	buf->f_ffree = s->s_hpfs_n_free_dnodes;
+	buf->f_blocks = sbi->sb_fs_size;
+	buf->f_bfree = sbi->sb_n_free;
+	buf->f_bavail = sbi->sb_n_free;
+	buf->f_files = sbi->sb_dirband_size / 4;
+	buf->f_ffree = sbi->sb_n_free_dnodes;
 	buf->f_namelen = 254;
 
 	unlock_kernel();
@@ -377,14 +381,15 @@
 	umode_t umask;
 	int lowercase, conv, eas, chk, errs, chkdsk, timeshift;
 	int o;
+	struct hpfs_sb_info *sbi = hpfs_sb(s);
 	
 	*flags |= MS_NOATIME;
 	
-	uid = s->s_hpfs_uid; gid = s->s_hpfs_gid;
-	umask = 0777 & ~s->s_hpfs_mode;
-	lowercase = s->s_hpfs_lowercase; conv = s->s_hpfs_conv;
-	eas = s->s_hpfs_eas; chk = s->s_hpfs_chk; chkdsk = s->s_hpfs_chkdsk;
-	errs = s->s_hpfs_err; timeshift = s->s_hpfs_timeshift;
+	uid = sbi->sb_uid; gid = sbi->sb_gid;
+	umask = 0777 & ~sbi->sb_mode;
+	lowercase = sbi->sb_lowercase; conv = sbi->sb_conv;
+	eas = sbi->sb_eas; chk = sbi->sb_chk; chkdsk = sbi->sb_chkdsk;
+	errs = sbi->sb_err; timeshift = sbi->sb_timeshift;
 
 	if (!(o = parse_opts(data, &uid, &gid, &umask, &lowercase, &conv,
 	    &eas, &chk, &errs, &chkdsk, &timeshift))) {
@@ -395,18 +400,18 @@
 		hpfs_help();
 		return 1;
 	}
-	if (timeshift != s->s_hpfs_timeshift) {
+	if (timeshift != sbi->sb_timeshift) {
 		printk("HPFS: timeshift can't be changed using remount.\n");
 		return 1;
 	}
 
 	unmark_dirty(s);
 
-	s->s_hpfs_uid = uid; s->s_hpfs_gid = gid;
-	s->s_hpfs_mode = 0777 & ~umask;
-	s->s_hpfs_lowercase = lowercase; s->s_hpfs_conv = conv;
-	s->s_hpfs_eas = eas; s->s_hpfs_chk = chk; s->s_hpfs_chkdsk = chkdsk;
-	s->s_hpfs_err = errs; s->s_hpfs_timeshift = timeshift;
+	sbi->sb_uid = uid; sbi->sb_gid = gid;
+	sbi->sb_mode = 0777 & ~umask;
+	sbi->sb_lowercase = lowercase; sbi->sb_conv = conv;
+	sbi->sb_eas = eas; sbi->sb_chk = chk; sbi->sb_chkdsk = chkdsk;
+	sbi->sb_err = errs; sbi->sb_timeshift = timeshift;
 
 	if (!(*flags & MS_RDONLY)) mark_dirty(s);
 
@@ -419,6 +424,7 @@
 	struct hpfs_boot_block *bootblock;
 	struct hpfs_super_block *superblock;
 	struct hpfs_spare_block *spareblock;
+	struct hpfs_sb_info *sbi;
 
 	uid_t uid;
 	gid_t gid;
@@ -431,12 +437,18 @@
 
 	int o;
 
-	s->s_hpfs_bmp_dir = NULL;
-	s->s_hpfs_cp_table = NULL;
+	sbi = kmalloc(sizeof(*sbi), GFP_KERNEL);
+	if (!sbi)
+		return -ENOMEM;
+	s->u.generic_sbp = sbi;
+	memset(sbi, 0, sizeof(*sbi));
+
+	sbi->sb_bmp_dir = NULL;
+	sbi->sb_cp_table = NULL;
 
-	s->s_hpfs_rd_inode = 0;
-	init_MUTEX(&s->u.hpfs_sb.hpfs_creation_de);
-	init_waitqueue_head(&s->s_hpfs_iget_q);
+	sbi->sb_rd_inode = 0;
+	init_MUTEX(&sbi->hpfs_creation_de);
+	init_waitqueue_head(&sbi->sb_iget_q);
 
 	uid = current->uid;
 	gid = current->gid;
@@ -459,9 +471,9 @@
 		goto bail0;
 	}
 
-	/*s->s_hpfs_mounting = 1;*/
+	/*sbi->sb_mounting = 1;*/
 	sb_set_blocksize(s, 512);
-	s->s_hpfs_fs_size = -1;
+	sbi->sb_fs_size = -1;
 	if (!(bootblock = hpfs_map_sector(s, 0, &bh0, 0))) goto bail1;
 	if (!(superblock = hpfs_map_sector(s, 16, &bh1, 1))) goto bail2;
 	if (!(spareblock = hpfs_map_sector(s, 17, &bh2, 0))) goto bail3;
@@ -489,30 +501,30 @@
 	s->s_magic = HPFS_SUPER_MAGIC;
 	s->s_op = &hpfs_sops;
 
-	s->s_hpfs_root = superblock->root;
-	s->s_hpfs_fs_size = superblock->n_sectors;
-	s->s_hpfs_bitmaps = superblock->bitmaps;
-	s->s_hpfs_dirband_start = superblock->dir_band_start;
-	s->s_hpfs_dirband_size = superblock->n_dir_band;
-	s->s_hpfs_dmap = superblock->dir_band_bitmap;
-	s->s_hpfs_uid = uid;
-	s->s_hpfs_gid = gid;
-	s->s_hpfs_mode = 0777 & ~umask;
-	s->s_hpfs_n_free = -1;
-	s->s_hpfs_n_free_dnodes = -1;
-	s->s_hpfs_lowercase = lowercase;
-	s->s_hpfs_conv = conv;
-	s->s_hpfs_eas = eas;
-	s->s_hpfs_chk = chk;
-	s->s_hpfs_chkdsk = chkdsk;
-	s->s_hpfs_err = errs;
-	s->s_hpfs_timeshift = timeshift;
-	s->s_hpfs_was_error = 0;
-	s->s_hpfs_cp_table = NULL;
-	s->s_hpfs_c_bitmap = -1;
+	sbi->sb_root = superblock->root;
+	sbi->sb_fs_size = superblock->n_sectors;
+	sbi->sb_bitmaps = superblock->bitmaps;
+	sbi->sb_dirband_start = superblock->dir_band_start;
+	sbi->sb_dirband_size = superblock->n_dir_band;
+	sbi->sb_dmap = superblock->dir_band_bitmap;
+	sbi->sb_uid = uid;
+	sbi->sb_gid = gid;
+	sbi->sb_mode = 0777 & ~umask;
+	sbi->sb_n_free = -1;
+	sbi->sb_n_free_dnodes = -1;
+	sbi->sb_lowercase = lowercase;
+	sbi->sb_conv = conv;
+	sbi->sb_eas = eas;
+	sbi->sb_chk = chk;
+	sbi->sb_chkdsk = chkdsk;
+	sbi->sb_err = errs;
+	sbi->sb_timeshift = timeshift;
+	sbi->sb_was_error = 0;
+	sbi->sb_cp_table = NULL;
+	sbi->sb_c_bitmap = -1;
 	
 	/* Load bitmap directory */
-	if (!(s->s_hpfs_bmp_dir = hpfs_load_bitmap_directory(s, superblock->bitmaps)))
+	if (!(sbi->sb_bmp_dir = hpfs_load_bitmap_directory(s, superblock->bitmaps)))
 		goto bail4;
 	
 	/* Check for general fs errors*/
@@ -557,20 +569,20 @@
 				superblock->dir_band_start, superblock->dir_band_end, superblock->n_dir_band);
 			goto bail4;
 		}
-		a = s->s_hpfs_dirband_size;
-		s->s_hpfs_dirband_size = 0;
+		a = sbi->sb_dirband_size;
+		sbi->sb_dirband_size = 0;
 		if (hpfs_chk_sectors(s, superblock->dir_band_start, superblock->n_dir_band, "dir_band") ||
 		    hpfs_chk_sectors(s, superblock->dir_band_bitmap, 4, "dir_band_bitmap") ||
 		    hpfs_chk_sectors(s, superblock->bitmaps, 4, "bitmaps")) {
 			mark_dirty(s);
 			goto bail4;
 		}
-		s->s_hpfs_dirband_size = a;
+		sbi->sb_dirband_size = a;
 	} else printk("HPFS: You really don't want any checks? You are crazy...\n");
 
 	/* Load code page table */
 	if (spareblock->n_code_pages)
-		if (!(s->s_hpfs_cp_table = hpfs_load_code_page(s, spareblock->code_page_dir)))
+		if (!(sbi->sb_cp_table = hpfs_load_code_page(s, spareblock->code_page_dir)))
 			printk("HPFS: Warning: code page support is disabled\n");
 
 	brelse(bh2);
@@ -578,7 +590,7 @@
 	brelse(bh0);
 
 	hpfs_lock_iget(s, 1);
-	s->s_root = d_alloc_root(iget(s, s->s_hpfs_root));
+	s->s_root = d_alloc_root(iget(s, sbi->sb_root));
 	hpfs_unlock_iget(s);
 	if (!s->s_root || !s->s_root->d_inode) {
 		printk("HPFS: iget failed. Why???\n");
@@ -590,7 +602,7 @@
 	 * find the root directory's . pointer & finish filling in the inode
 	 */
 
-	root_dno = hpfs_fnode_dno(s, s->s_hpfs_root);
+	root_dno = hpfs_fnode_dno(s, sbi->sb_root);
 	if (root_dno)
 		de = map_dirent(s->s_root->d_inode, root_dno, "\001\001", 2, NULL, &qbh);
 	if (!root_dno || !de) hpfs_error(s, "unable to find root dir");
@@ -612,8 +624,10 @@
 bail2:	brelse(bh0);
 bail1:
 bail0:
-	if (s->s_hpfs_bmp_dir) kfree(s->s_hpfs_bmp_dir);
-	if (s->s_hpfs_cp_table) kfree(s->s_hpfs_cp_table);
+	if (sbi->sb_bmp_dir) kfree(sbi->sb_bmp_dir);
+	if (sbi->sb_cp_table) kfree(sbi->sb_cp_table);
+	s->u.generic_sbp = NULL;
+	kfree(sbi);
 	return -EINVAL;
 }
 
diff -Nru a/fs/inode.c b/fs/inode.c
--- a/fs/inode.c	Mon Oct  7 11:25:39 2002
+++ b/fs/inode.c	Mon Oct  7 11:25:39 2002
@@ -147,10 +147,12 @@
 	if (inode_has_buffers(inode))
 		BUG();
 	security_ops->inode_free_security(inode);
-	if (inode->i_sb->s_op->destroy_inode)
+	if (inode->i_sb->s_op->destroy_inode) {
 		inode->i_sb->s_op->destroy_inode(inode);
-	else
+	} else {
+		BUG_ON(inode->i_data.page_tree.rnode != NULL);
 		kmem_cache_free(inode_cachep, (inode));
+	}
 }
 
 
diff -Nru a/fs/jffs/inode-v23.c b/fs/jffs/inode-v23.c
--- a/fs/jffs/inode-v23.c	Mon Oct  7 11:25:36 2002
+++ b/fs/jffs/inode-v23.c	Mon Oct  7 11:25:36 2002
@@ -301,7 +301,7 @@
 		inode->i_blocks = (inode->i_size + 511) >> 9;
 
 		if (len) {
-			invalidate_inode_pages(inode);
+			invalidate_inode_pages(inode->i_mapping);
 		}
 		inode->i_ctime = CURRENT_TIME;
 		inode->i_mtime = inode->i_ctime;
@@ -1520,7 +1520,7 @@
 	}
 	inode->i_ctime = inode->i_mtime = CURRENT_TIME;
 	mark_inode_dirty(inode);
-	invalidate_inode_pages(inode);
+	invalidate_inode_pages(inode->i_mapping);
 
  out_isem:
 	return err;
diff -Nru a/fs/jffs/intrep.c b/fs/jffs/intrep.c
--- a/fs/jffs/intrep.c	Mon Oct  7 11:25:35 2002
+++ b/fs/jffs/intrep.c	Mon Oct  7 11:25:35 2002
@@ -3376,10 +3376,13 @@
 		 */
 		while (signal_pending(current)) {
 			siginfo_t info;
-			unsigned long signr;
+			unsigned long signr = 0;
 
 			spin_lock_irq(&current->sig->siglock);
-			signr = dequeue_signal(&current->blocked, &info);
+			if (current->sig->shared_pending.head)
+				signr = dequeue_signal(&current->sig->shared_pending, &current->blocked, &info);
+			if (!signr)
+				signr = dequeue_signal(&current->pending, &current->blocked, &info);
 			spin_unlock_irq(&current->sig->siglock);
 
 			switch(signr) {
diff -Nru a/fs/jffs2/background.c b/fs/jffs2/background.c
--- a/fs/jffs2/background.c	Mon Oct  7 11:25:25 2002
+++ b/fs/jffs2/background.c	Mon Oct  7 11:25:25 2002
@@ -112,10 +112,13 @@
                  */
                 while (signal_pending(current)) {
                         siginfo_t info;
-                        unsigned long signr;
+                        unsigned long signr = 0 ;
 
                         spin_lock_irq(&current->sig->siglock);
-                        signr = dequeue_signal(&current->blocked, &info);
+			if (current->sig->shared_pending.head)
+				signr = dequeue_signal(&current->sig->shared_pending, &current->blocked, &info);
+			if (!signr)
+				signr = dequeue_signal(&current->pending, &current->blocked, &info);
                         spin_unlock_irq(&current->sig->siglock);
 
                         switch(signr) {
diff -Nru a/fs/jffs2/fs.c b/fs/jffs2/fs.c
--- a/fs/jffs2/fs.c	Mon Oct  7 11:25:34 2002
+++ b/fs/jffs2/fs.c	Mon Oct  7 11:25:34 2002
@@ -303,7 +303,7 @@
 			return -ENOMEM;
 
 		/* Initialize process for timed wbuf flush */
-		INIT_TQUEUE(&c->wbuf_task,(void*) jffs2_wbuf_process, (void *)c);
+		INIT_WORK(&c->wbuf_task,(void*) jffs2_wbuf_process, (void *)c);
 		/* Initialize timer for timed wbuf flush */
 		init_timer(&c->wbuf_timer);
 		c->wbuf_timer.function = jffs2_wbuf_timeout;
diff -Nru a/fs/jffs2/wbuf.c b/fs/jffs2/wbuf.c
--- a/fs/jffs2/wbuf.c	Mon Oct  7 11:25:28 2002
+++ b/fs/jffs2/wbuf.c	Mon Oct  7 11:25:28 2002
@@ -75,7 +75,7 @@
 	* to sleep on flash write
 	*/
 	D1(printk(KERN_DEBUG "jffs2_wbuf_timeout(): timer expired\n"));
-	schedule_task(&c->wbuf_task);
+	schedule_work(&c->wbuf_task);
 }
 
 /*
diff -Nru a/fs/jfs/inode.c b/fs/jfs/inode.c
--- a/fs/jfs/inode.c	Mon Oct  7 11:25:25 2002
+++ b/fs/jfs/inode.c	Mon Oct  7 11:25:25 2002
@@ -310,9 +310,11 @@
 	return generic_block_bmap(mapping, block, jfs_get_block);
 }
 
-static int jfs_direct_IO(int rw, struct inode *inode, const struct iovec *iov, 
+static int jfs_direct_IO(int rw, struct file *file, const struct iovec *iov,
 			loff_t offset, unsigned long nr_segs)
 {
+	struct inode *inode = file->f_dentry->d_inode->i_mapping->host;
+
 	return generic_direct_IO(rw, inode, iov,
 				offset, nr_segs, jfs_get_blocks);
 }
diff -Nru a/fs/jfs/jfs_txnmgr.c b/fs/jfs/jfs_txnmgr.c
--- a/fs/jfs/jfs_txnmgr.c	Mon Oct  7 11:25:35 2002
+++ b/fs/jfs/jfs_txnmgr.c	Mon Oct  7 11:25:35 2002
@@ -2746,13 +2746,16 @@
 	if (tblk->flag & tblkGC_READY)
 		wake_up(&tblk->gcwait);	// LOGGC_WAKEUP
 
-	spin_unlock_irq(&log->gclock);	// LOGGC_UNLOCK
-
+	/*
+	 * Can't release log->gclock until we've tested tblk->flag
+	 */
 	if (tblk->flag & tblkGC_LAZY) {
+		spin_unlock_irq(&log->gclock);	// LOGGC_UNLOCK
 		txUnlock(tblk);
 		tblk->flag &= ~tblkGC_LAZY;
 		txEnd(tblk - TxBlock);	/* Convert back to tid */
-	}
+	} else
+		spin_unlock_irq(&log->gclock);	// LOGGC_UNLOCK
 
 	jFYI(1, ("txLazyCommit: done: tblk = 0x%p\n", tblk));
 }
diff -Nru a/fs/mpage.c b/fs/mpage.c
--- a/fs/mpage.c	Mon Oct  7 11:25:27 2002
+++ b/fs/mpage.c	Mon Oct  7 11:25:27 2002
@@ -331,6 +331,8 @@
 	unsigned first_unmapped = blocks_per_page;
 	struct block_device *bdev = NULL;
 	int boundary = 0;
+	sector_t boundary_block = 0;
+	struct block_device *boundary_bdev = NULL;
 
 	if (page_has_buffers(page)) {
 		struct buffer_head *head = page_buffers(page);
@@ -363,6 +365,10 @@
 			}
 			blocks[page_block++] = bh->b_blocknr;
 			boundary = buffer_boundary(bh);
+			if (boundary) {
+				boundary_block = bh->b_blocknr;
+				boundary_bdev = bh->b_bdev;
+			}
 			bdev = bh->b_bdev;
 		} while ((bh = bh->b_this_page) != head);
 
@@ -393,6 +399,10 @@
 		if (buffer_new(&map_bh))
 			unmap_underlying_metadata(map_bh.b_bdev,
 						map_bh.b_blocknr);
+		if (buffer_boundary(&map_bh)) {
+			boundary_block = map_bh.b_blocknr;
+			boundary_bdev = map_bh.b_bdev;
+		}
 		if (page_block) {
 			if (map_bh.b_blocknr != blocks[page_block-1] + 1)
 				goto confused;
@@ -430,10 +440,8 @@
 
 alloc_new:
 	if (bio == NULL) {
-		const unsigned __nr_pages = 64;	/* FIXME */
-
 		bio = mpage_alloc(bdev, blocks[0] << (blkbits - 9),
-					__nr_pages, GFP_NOFS|__GFP_HIGH);
+				bio_get_nr_vecs(bdev), GFP_NOFS|__GFP_HIGH);
 		if (bio == NULL)
 			goto confused;
 	}
@@ -466,10 +474,15 @@
 	BUG_ON(PageWriteback(page));
 	SetPageWriteback(page);
 	unlock_page(page);
-	if (boundary || (first_unmapped != blocks_per_page))
+	if (boundary || (first_unmapped != blocks_per_page)) {
 		bio = mpage_bio_submit(WRITE, bio);
-	else
+		if (boundary_block) {
+			write_boundary_block(boundary_bdev,
+					boundary_block, 1 << blkbits);
+		}
+	} else {
 		*last_block_in_bio = blocks[blocks_per_page - 1];
+	}
 	goto out;
 
 confused:
diff -Nru a/fs/ncpfs/inode.c b/fs/ncpfs/inode.c
--- a/fs/ncpfs/inode.c	Mon Oct  7 11:25:35 2002
+++ b/fs/ncpfs/inode.c	Mon Oct  7 11:25:35 2002
@@ -293,7 +293,7 @@
 	sk->data_ready = server->data_ready;
 	sk->write_space = server->write_space;
 	del_timer_sync(&server->timeout_tm);
-	flush_scheduled_tasks();
+	flush_scheduled_work();
 }
 
 static const struct ncp_option ncp_opts[] = {
@@ -566,12 +566,12 @@
 		server->rcv.ptr = (unsigned char*)&server->rcv.buf;
 		server->rcv.len = 10;
 		server->rcv.state = 0;
-		INIT_TQUEUE(&server->rcv.tq, ncp_tcp_rcv_proc, server);
-		INIT_TQUEUE(&server->tx.tq, ncp_tcp_tx_proc, server);
+		INIT_WORK(&server->rcv.tq, ncp_tcp_rcv_proc, server);
+		INIT_WORK(&server->tx.tq, ncp_tcp_tx_proc, server);
 		sock->sk->write_space = ncp_tcp_write_space;
 	} else {
-		INIT_TQUEUE(&server->rcv.tq, ncpdgram_rcv_proc, server);
-		INIT_TQUEUE(&server->timeout_tq, ncpdgram_timeout_proc, server);
+		INIT_WORK(&server->rcv.tq, ncpdgram_rcv_proc, server);
+		INIT_WORK(&server->timeout_tq, ncpdgram_timeout_proc, server);
 		server->timeout_tm.data = (unsigned long)server;
 		server->timeout_tm.function = ncpdgram_timeout_call;
 	}
diff -Nru a/fs/ncpfs/sock.c b/fs/ncpfs/sock.c
--- a/fs/ncpfs/sock.c	Mon Oct  7 11:25:31 2002
+++ b/fs/ncpfs/sock.c	Mon Oct  7 11:25:31 2002
@@ -86,14 +86,14 @@
 	struct ncp_server *server = sk->user_data;
 
 	server->data_ready(sk, len);
-	schedule_task(&server->rcv.tq);
+	schedule_work(&server->rcv.tq);
 }
 
 void ncp_tcp_error_report(struct sock *sk) {
 	struct ncp_server *server = sk->user_data;
 	
 	server->error_report(sk);
-	schedule_task(&server->rcv.tq);
+	schedule_work(&server->rcv.tq);
 }
 
 void ncp_tcp_write_space(struct sock *sk) {
@@ -103,14 +103,14 @@
 	   not vice versa... */
 	server->write_space(sk);
 	if (server->tx.creq) {
-		schedule_task(&server->tx.tq);
+		schedule_work(&server->tx.tq);
 	}
 }
 
 void ncpdgram_timeout_call(unsigned long v) {
 	struct ncp_server *server = (void*)v;
 	
-	schedule_task(&server->timeout_tq);
+	schedule_work(&server->timeout_tq);
 }
 
 static inline void ncp_finish_request(struct ncp_request_reply *req, int result) {
diff -Nru a/fs/nfs/Makefile b/fs/nfs/Makefile
--- a/fs/nfs/Makefile	Mon Oct  7 11:25:26 2002
+++ b/fs/nfs/Makefile	Mon Oct  7 11:25:26 2002
@@ -8,6 +8,7 @@
 			   proc.o read.o symlink.o unlink.o write.o
 nfs-$(CONFIG_ROOT_NFS)	+= nfsroot.o mount_clnt.o      
 nfs-$(CONFIG_NFS_V3)	+= nfs3proc.o nfs3xdr.o
+nfs-$(CONFIG_NFS_DIRECTIO) += direct.o
 nfs-objs		:= $(nfs-y)
 
 include $(TOPDIR)/Rules.make
diff -Nru a/fs/nfs/dir.c b/fs/nfs/dir.c
--- a/fs/nfs/dir.c	Mon Oct  7 11:25:35 2002
+++ b/fs/nfs/dir.c	Mon Oct  7 11:25:35 2002
@@ -125,14 +125,14 @@
 	 *	 throught inode->i_sem or some other mechanism.
 	 */
 	if (page->index == 0)
-		invalidate_inode_pages(inode);
+		invalidate_inode_pages(inode->i_mapping);
 	unlock_page(page);
 	return 0;
  error:
 	SetPageError(page);
 	kunmap(page);
 	unlock_page(page);
-	invalidate_inode_pages(inode);
+	invalidate_inode_pages(inode->i_mapping);
 	desc->error = error;
 	return -EIO;
 }
diff -Nru a/fs/nfs/direct.c b/fs/nfs/direct.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/fs/nfs/direct.c	Mon Oct  7 11:26:06 2002
@@ -0,0 +1,259 @@
+/*
+ * linux/fs/nfs/direct.c
+ *
+ * Copyright (C) 2001 by Chuck Lever <cel@netapp.com>
+ *
+ * High-performance uncached I/O for the Linux NFS client
+ *
+ * There are important applications whose performance or correctness
+ * depends on uncached access to file data.  Database clusters
+ * (multiple copies of the same instance running on separate hosts) 
+ * implement their own cache coherency protocol that subsumes file
+ * system cache protocols.  Applications that process datasets 
+ * considerably larger than the client's memory do not always benefit 
+ * from a local cache.  A streaming video server, for instance, has no 
+ * need to cache the contents of a file.
+ *
+ * When an application requests uncached I/O, all read and write requests
+ * are made directly to the server; data stored or fetched via these
+ * requests is not cached in the Linux page cache.  The client does not
+ * correct unaligned requests from applications.  All requested bytes are
+ * held on permanent storage before a direct write system call returns to
+ * an application.
+ *
+ * Solaris implements an uncached I/O facility called directio() that
+ * is used for backups and sequential I/O to very large files.  Solaris
+ * also supports uncaching whole NFS partitions with "-o forcedirectio,"
+ * an undocumented mount option.
+ *
+ * Designed by Jeff Kimmel, Chuck Lever, and Trond Myklebust.
+ *
+ * 18 Dec 2001	Initial implementation for 2.4  --cel
+ * 08 Jul 2002	Version for 2.4.19, with bug fixes --trondmy
+ * 24 Sep 2002	Rewrite to use asynchronous RPCs, port to 2.5  --cel
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/file.h>
+#include <linux/errno.h>
+#include <linux/nfs_fs.h>
+#include <linux/nfs_page.h>
+#include <linux/sunrpc/clnt.h>
+
+#include <asm/system.h>
+#include <asm/uaccess.h>
+
+#define NFSDBG_FACILITY		(NFSDBG_PAGECACHE | NFSDBG_VFS)
+#define VERF_SIZE		(2 * sizeof(__u32))
+
+
+/**
+ * nfs_get_user_pages - find and set up page representing user buffer
+ * addr: user-space address of target buffer
+ * size: total size in bytes of target buffer
+ * @pages: returned array of page struct pointers underlying target buffer
+ * write: whether or not buffer is target of a write operation
+ */
+static inline int
+nfs_get_user_pages(unsigned long addr, size_t size,
+		struct page ***pages, int rw)
+{
+	int result = -ENOMEM;
+	unsigned page_count = (unsigned) size >> PAGE_SHIFT;
+	unsigned array_size = (page_count * sizeof(struct page *)) + 2U;
+
+	*pages = (struct page **) kmalloc(array_size, GFP_KERNEL);
+	if (*pages) {
+		down_read(&current->mm->mmap_sem);
+		result = get_user_pages(current, current->mm, addr,
+					page_count, (rw == WRITE), 0,
+					*pages, NULL);
+		up_read(&current->mm->mmap_sem);
+		if (result < 0)
+			printk(KERN_ERR "%s: get_user_pages result %d\n",
+					__FUNCTION__, result);
+	}
+	return result;
+}
+
+/**
+ * nfs_free_user_pages - tear down page struct array
+ * @pages: array of page struct pointers underlying target buffer
+ */
+static inline void
+nfs_free_user_pages(struct page **pages, unsigned count)
+{
+	unsigned page = 0;
+
+	while (count--)
+		page_cache_release(pages[page++]);
+
+	kfree(pages);
+}
+
+/**
+ * nfs_iov2pagelist - convert an array of iovecs to a list of page requests
+ * @inode: inode of target file
+ * @cred: credentials of user who requested I/O
+ * @iov: array of vectors that define I/O buffer
+ * offset: where in file to begin the read
+ * nr_segs: size of iovec array
+ * @requests: append new page requests to this list head
+ */
+static int
+nfs_iov2pagelist(int rw, const struct inode *inode,
+		const struct rpc_cred *cred,
+		const struct iovec *iov, loff_t offset,
+		unsigned long nr_segs, struct list_head *requests)
+{
+	unsigned seg;
+	int tot_bytes = 0;
+	struct page **pages;
+
+	/* for each iovec in the array... */
+	for (seg = 0; seg < nr_segs; seg++) {
+		const unsigned long user_addr =
+					(unsigned long) iov[seg].iov_base;
+		size_t bytes = iov[seg].iov_len;
+		unsigned int pg_offset = (user_addr & ~PAGE_MASK);
+		int page_count, page = 0;
+
+		page_count = nfs_get_user_pages(user_addr, bytes, &pages, rw);
+		if (page_count < 0) {
+			nfs_release_list(requests);
+			return page_count;
+		}
+
+		/* ...build as many page requests as required */
+		while (bytes > 0) {
+			struct nfs_page *new;
+			const unsigned int pg_bytes = (bytes > PAGE_SIZE) ?
+							PAGE_SIZE : bytes;
+
+			new = nfs_create_request((struct rpc_cred *) cred,
+						 (struct inode *) inode,
+						 pages[page],
+						 pg_offset, pg_bytes);
+			if (IS_ERR(new)) {
+				nfs_free_user_pages(pages, page_count);
+				nfs_release_list(requests);
+				return PTR_ERR(new);
+			}
+			new->wb_index = offset;
+			nfs_list_add_request(new, requests);
+
+			/* after the first page */
+			pg_offset = 0;
+			offset += PAGE_SIZE;
+			tot_bytes += pg_bytes;
+			bytes -= pg_bytes;
+			page++;
+		}
+
+		/* don't release pages here -- I/O completion will do that */
+		nfs_free_user_pages(pages, 0);
+	}
+
+	return tot_bytes;
+}
+
+/**
+ * do_nfs_direct_IO - Read or write data without caching
+ * @inode: inode of target file
+ * @cred: credentials of user who requested I/O
+ * @iov: array of vectors that define I/O buffer
+ * offset: where in file to begin the read
+ * nr_segs: size of iovec array
+ *
+ * Break the passed-in iovec into a series of page-sized or smaller
+ * requests, where each page is mapped for direct user-land I/O.
+ *
+ * For each of these pages, create an NFS page request and
+ * append it to an automatic list of page requests.
+ *
+ * When all page requests have been queued, start the I/O on the
+ * whole list.  The underlying routines coalesce the pages on the
+ * list into a bunch of asynchronous "r/wsize" network requests.
+ *
+ * I/O completion automatically unmaps and releases the pages.
+ */
+static int
+do_nfs_direct_IO(int rw, const struct inode *inode,
+		const struct rpc_cred *cred, const struct iovec *iov,
+		loff_t offset, unsigned long nr_segs)
+{
+	LIST_HEAD(requests);
+	int result, tot_bytes;
+
+	result = nfs_iov2pagelist(rw, inode, cred, iov, offset, nr_segs,
+								&requests);
+	if (result < 0)
+		return result;
+	tot_bytes = result;
+
+	switch (rw) {
+	case READ:
+		if (IS_SYNC(inode) || (NFS_SERVER(inode)->rsize < PAGE_SIZE)) {
+			result = nfs_direct_read_sync(inode, cred, iov, offset, nr_segs);
+			break;
+		}
+		result = nfs_pagein_list(&requests, NFS_SERVER(inode)->rpages);
+		nfs_wait_for_reads(&requests);
+		break;
+	case WRITE:
+		if (IS_SYNC(inode) || (NFS_SERVER(inode)->wsize < PAGE_SIZE))
+			result = nfs_direct_write_sync(inode, cred, iov, offset, nr_segs);
+		else
+			result = nfs_flush_list(&requests,
+					NFS_SERVER(inode)->wpages, FLUSH_WAIT);
+
+		/* invalidate cache so non-direct readers pick up changes */
+		invalidate_inode_pages((struct inode *) inode);
+		break;
+	default:
+		result = -EINVAL;
+		break;
+	}
+
+	if (result < 0)
+		return result;
+	return tot_bytes;
+}
+
+/**
+ * nfs_direct_IO - NFS address space operation for direct I/O
+ * rw: direction (read or write)
+ * @file: file struct of target file
+ * @iov: array of vectors that define I/O buffer
+ * offset: offset in file to begin the operation
+ * nr_segs: size of iovec array
+ *
+ * The inode's i_sem is no longer held by the VFS layer before it calls
+ * this function to do a write.
+ */
+int
+nfs_direct_IO(int rw, struct file *file, const struct iovec *iov,
+		loff_t offset, unsigned long nr_segs)
+{
+	/* None of this works yet, so prevent it from compiling. */
+#if 0
+	int result;
+	struct dentry *dentry = file->f_dentry;
+	const struct inode *inode = dentry->d_inode->i_mapping->host;
+	const struct rpc_cred *cred = nfs_file_cred(file);
+#endif
+
+	dfprintk(VFS, "NFS: direct_IO(%s) (%s/%s) off/no(%Lu/%lu)\n",
+				((rw == READ) ? "READ" : "WRITE"),
+				dentry->d_parent->d_name.name,
+				dentry->d_name.name, offset, nr_segs);
+
+	result = do_nfs_direct_IO(rw, inode, cred, iov, offset, nr_segs);
+
+	dfprintk(VFS, "NFS: direct_IO result = %d\n", result);
+
+	return result;
+}
diff -Nru a/fs/nfs/file.c b/fs/nfs/file.c
--- a/fs/nfs/file.c	Mon Oct  7 11:25:36 2002
+++ b/fs/nfs/file.c	Mon Oct  7 11:25:36 2002
@@ -176,7 +176,7 @@
 {
 	struct address_space *mapping;
 	struct inode	*inode;
-	unsigned long	index = page_index(page);
+	unsigned long	index = page->index;
 	unsigned int	rpages;
 	int		result;
 
@@ -199,7 +199,10 @@
 	.sync_page = nfs_sync_page,
 	.writepage = nfs_writepage,
 	.prepare_write = nfs_prepare_write,
-	.commit_write = nfs_commit_write
+	.commit_write = nfs_commit_write,
+#ifdef CONFIG_NFS_DIRECTIO
+	.direct_IO = nfs_direct_IO,
+#endif
 };
 
 /* 
diff -Nru a/fs/nfs/inode.c b/fs/nfs/inode.c
--- a/fs/nfs/inode.c	Mon Oct  7 11:25:36 2002
+++ b/fs/nfs/inode.c	Mon Oct  7 11:25:36 2002
@@ -564,7 +564,7 @@
 	NFS_ATTRTIMEO(inode) = NFS_MINATTRTIMEO(inode);
 	NFS_ATTRTIMEO_UPDATE(inode) = jiffies;
 
-	invalidate_inode_pages(inode);
+	invalidate_inode_pages(inode->i_mapping);
 
 	memset(NFS_COOKIEVERF(inode), 0, sizeof(NFS_COOKIEVERF(inode)));
 	NFS_CACHEINV(inode);
@@ -1130,7 +1130,7 @@
 	if (invalid) {
 		NFS_ATTRTIMEO(inode) = NFS_MINATTRTIMEO(inode);
 		NFS_ATTRTIMEO_UPDATE(inode) = jiffies;
-		invalidate_inode_pages(inode);
+		invalidate_inode_pages(inode->i_mapping);
 		memset(NFS_COOKIEVERF(inode), 0, sizeof(NFS_COOKIEVERF(inode)));
 	} else if (time_after(jiffies, NFS_ATTRTIMEO_UPDATE(inode)+NFS_ATTRTIMEO(inode))) {
 		if ((NFS_ATTRTIMEO(inode) <<= 1) > NFS_MAXATTRTIMEO(inode))
diff -Nru a/fs/nfs/nfs2xdr.c b/fs/nfs/nfs2xdr.c
--- a/fs/nfs/nfs2xdr.c	Mon Oct  7 11:25:32 2002
+++ b/fs/nfs/nfs2xdr.c	Mon Oct  7 11:25:32 2002
@@ -434,7 +434,7 @@
 			goto short_pkt;
 		entry = p;
 	}
-	if (!nr)
+	if (!nr && (entry[0] != 0 || entry[1] == 0))
 		goto short_pkt;
  out:
 	kunmap(*page);
diff -Nru a/fs/nfs/nfs3xdr.c b/fs/nfs/nfs3xdr.c
--- a/fs/nfs/nfs3xdr.c	Mon Oct  7 11:25:40 2002
+++ b/fs/nfs/nfs3xdr.c	Mon Oct  7 11:25:40 2002
@@ -579,7 +579,7 @@
 			goto short_pkt;
 		entry = p;
 	}
-	if (!nr)
+	if (!nr && (entry[0] != 0 || entry[1] == 0))
 		goto short_pkt;
  out:
 	kunmap(*page);
diff -Nru a/fs/nfs/pagelist.c b/fs/nfs/pagelist.c
--- a/fs/nfs/pagelist.c	Mon Oct  7 11:25:31 2002
+++ b/fs/nfs/pagelist.c	Mon Oct  7 11:25:31 2002
@@ -103,6 +103,7 @@
 	 * long write-back delay. This will be adjusted in
 	 * update_nfs_request below if the region is not locked. */
 	req->wb_page    = page;
+	req->wb_index	= page->index;
 	page_cache_get(page);
 	req->wb_offset  = offset;
 	req->wb_bytes   = count;
@@ -175,6 +176,26 @@
 }
 
 /**
+ * nfs_release_list - cleanly dispose of an unattached list of page requests
+ * @list: list of doomed page requests
+ */
+void
+nfs_release_list(struct list_head *list)
+{
+	while (!list_empty(list)) {
+		struct nfs_page *req = nfs_list_entry(list);
+
+		nfs_list_remove_request(req);
+
+		page_cache_release(req->wb_page);
+
+		/* Release struct file or cached credential */
+		nfs_clear_request(req);
+		nfs_page_free(req);
+	}
+}
+
+/**
  * nfs_list_add_request - Insert a request into a sorted list
  * @req: request
  * @head: head of list into which to insert the request.
@@ -188,7 +209,6 @@
 nfs_list_add_request(struct nfs_page *req, struct list_head *head)
 {
 	struct list_head *pos;
-	unsigned long pg_idx = page_index(req->wb_page);
 
 #ifdef NFS_PARANOIA
 	if (!list_empty(&req->wb_list)) {
@@ -198,7 +218,7 @@
 #endif
 	list_for_each_prev(pos, head) {
 		struct nfs_page	*p = nfs_list_entry(pos);
-		if (page_index(p->wb_page) < pg_idx)
+		if (p->wb_index < req->wb_index)
 			break;
 	}
 	list_add(&req->wb_list, pos);
@@ -224,6 +244,37 @@
 }
 
 /**
+ * nfs_wait_for_reads - wait for outstanding requests to complete
+ * @head: list of page requests to wait for
+ */
+int
+nfs_wait_for_reads(struct list_head *head)
+{
+	struct list_head *p = head->next;
+	unsigned int res = 0;
+
+	while (p != head) {
+		struct nfs_page *req = nfs_list_entry(p);
+		int error;
+
+		if (!NFS_WBACK_BUSY(req))
+			continue;
+
+		req->wb_count++;
+		error = nfs_wait_on_request(req);
+		if (error < 0)
+			return error;
+		nfs_list_remove_request(req);
+		nfs_clear_request(req);
+		nfs_page_free(req);
+
+		p = head->next;
+		res++;
+	}
+	return res;
+}
+
+/**
  * nfs_coalesce_requests - Split coalesced requests out from a list.
  * @head: source list
  * @dst: destination list
@@ -247,7 +298,7 @@
 		if (prev) {
 			if (req->wb_cred != prev->wb_cred)
 				break;
-			if (page_index(req->wb_page) != page_index(prev->wb_page)+1)
+			if (req->wb_index != (prev->wb_index + 1))
 				break;
 
 			if (req->wb_offset != 0)
@@ -280,7 +331,7 @@
 	struct nfs_server *server = NFS_SERVER(req->wb_inode);
 	struct list_head *pos, *head = req->wb_list_head;
 	struct rpc_cred *cred = req->wb_cred;
-	unsigned long idx = page_index(req->wb_page) + 1;
+	unsigned long idx = req->wb_index + 1;
 	int npages = 0;
 
 	for (pos = req->wb_list.next; nfs_lock_request(req); pos = pos->next) {
@@ -296,7 +347,7 @@
 		if (req->wb_offset + req->wb_bytes != PAGE_CACHE_SIZE)
 			break;
 		req = nfs_list_entry(pos);
-		if (page_index(req->wb_page) != idx++)
+		if (req->wb_index != idx++)
 			break;
 		if (req->wb_offset != 0)
 			break;
@@ -393,17 +444,15 @@
 		idx_end = idx_start + npages - 1;
 
 	list_for_each_safe(pos, tmp, head) {
-		unsigned long pg_idx;
 
 		req = nfs_list_entry(pos);
 
 		if (file && req->wb_file != file)
 			continue;
 
-		pg_idx = page_index(req->wb_page);
-		if (pg_idx < idx_start)
+		if (req->wb_index < idx_start)
 			continue;
-		if (pg_idx > idx_end)
+		if (req->wb_index > idx_end)
 			break;
 
 		if (!nfs_lock_request(req))
diff -Nru a/fs/nfs/read.c b/fs/nfs/read.c
--- a/fs/nfs/read.c	Mon Oct  7 11:25:25 2002
+++ b/fs/nfs/read.c	Mon Oct  7 11:25:25 2002
@@ -179,7 +179,7 @@
 	nfs_mark_request_read(new);
 
 	if (nfsi->nread >= NFS_SERVER(inode)->rpages ||
-	    page_index(page) == (inode->i_size + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT)
+	    page->index == (inode->i_size + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT)
 		nfs_pagein_inode(inode, 0, 0);
 	return 0;
 }
@@ -207,7 +207,7 @@
 	data->inode	  = req->wb_inode;
 	data->cred	  = req->wb_cred;
 	data->args.fh     = NFS_FH(req->wb_inode);
-	data->args.offset = page_offset(req->wb_page) + req->wb_offset;
+	data->args.offset = req_offset(req) + req->wb_offset;
 	data->args.pgbase = req->wb_offset;
 	data->args.count  = count;
 	data->res.fattr   = &data->fattr;
@@ -441,7 +441,7 @@
                         req->wb_inode->i_sb->s_id,
                         (long long)NFS_FILEID(req->wb_inode),
                         req->wb_bytes,
-                        (long long)(page_offset(page) + req->wb_offset));
+                        (long long)(req_offset(req) + req->wb_offset));
 		nfs_clear_request(req);
 		nfs_release_request(req);
 		nfs_unlock_request(req);
diff -Nru a/fs/nfs/write.c b/fs/nfs/write.c
--- a/fs/nfs/write.c	Mon Oct  7 11:25:36 2002
+++ b/fs/nfs/write.c	Mon Oct  7 11:25:36 2002
@@ -291,7 +291,7 @@
 	if (NFS_SERVER(inode)->flags & NFS_MOUNT_NONLM)
 		return 0;
 
-	rqstart = page_offset(req->wb_page) + req->wb_offset;
+	rqstart = req_offset(req) + req->wb_offset;
 	rqend = rqstart + req->wb_bytes;
 	for (fl = inode->i_flock; fl; fl = fl->fl_next) {
 		if (fl->fl_owner == current->files && (fl->fl_flags & FL_POSIX)
@@ -357,7 +357,7 @@
  * Find a request
  */
 static inline struct nfs_page *
-_nfs_find_request(struct inode *inode, struct page *page)
+_nfs_find_request(struct inode *inode, unsigned long index)
 {
 	struct nfs_inode *nfsi = NFS_I(inode);
 	struct list_head	*head, *next;
@@ -367,7 +367,7 @@
 	while (next != head) {
 		struct nfs_page *req = nfs_inode_wb_entry(next);
 		next = next->next;
-		if (page_index(req->wb_page) != page_index(page))
+		if (req->wb_index != index)
 			continue;
 		req->wb_count++;
 		return req;
@@ -376,12 +376,12 @@
 }
 
 static struct nfs_page *
-nfs_find_request(struct inode *inode, struct page *page)
+nfs_find_request(struct inode *inode, unsigned long index)
 {
 	struct nfs_page		*req;
 
 	spin_lock(&nfs_wreq_lock);
-	req = _nfs_find_request(inode, page);
+	req = _nfs_find_request(inode, index);
 	spin_unlock(&nfs_wreq_lock);
 	return req;
 }
@@ -457,7 +457,6 @@
 	head = &nfsi->writeback;
 	p = head->next;
 	while (p != head) {
-		unsigned long pg_idx;
 		struct nfs_page *req = nfs_inode_wb_entry(p);
 
 		p = p->next;
@@ -465,8 +464,7 @@
 		if (file && req->wb_file != file)
 			continue;
 
-		pg_idx = page_index(req->wb_page);
-		if (pg_idx < idx_start || pg_idx > idx_end)
+		if (req->wb_index < idx_start || req->wb_index > idx_end)
 			continue;
 
 		if (!NFS_WBACK_BUSY(req))
@@ -654,7 +652,7 @@
 		 * A request for the page we wish to update
 		 */
 		spin_lock(&nfs_wreq_lock);
-		req = _nfs_find_request(inode, page);
+		req = _nfs_find_request(inode, page->index);
 		if (req) {
 			if (!nfs_lock_request_dontget(req)) {
 				int error;
@@ -776,7 +774,7 @@
 	 * Also do the same if we find a request from an existing
 	 * dropped page.
 	 */
-	req = nfs_find_request(inode,page);
+	req = nfs_find_request(inode, page->index);
 	if (req) {
 		if (req->wb_file != file || req->wb_cred != cred || req->wb_page != page)
 			status = nfs_wb_page(inode, page);
@@ -884,7 +882,7 @@
 	data->inode = req->wb_inode;
 	data->cred = req->wb_cred;
 	data->args.fh     = NFS_FH(req->wb_inode);
-	data->args.offset = page_offset(req->wb_page) + req->wb_offset;
+	data->args.offset = req_offset(req) + req->wb_offset;
 	data->args.pgbase = req->wb_offset;
 	data->args.count  = count;
 	data->res.fattr   = &data->fattr;
@@ -1072,7 +1070,7 @@
 			req->wb_inode->i_sb->s_id,
 			(long long)NFS_FILEID(req->wb_inode),
 			req->wb_bytes,
-			(long long)(page_offset(page) + req->wb_offset));
+			(long long)(req_offset(req) + req->wb_offset));
 
 		if (task->tk_status < 0) {
 			ClearPageUptodate(page);
@@ -1126,8 +1124,8 @@
 	 * Determine the offset range of requests in the COMMIT call.
 	 * We rely on the fact that data->pages is an ordered list...
 	 */
-	start = page_offset(first->wb_page) + first->wb_offset;
-	end = page_offset(last->wb_page) + (last->wb_offset + last->wb_bytes);
+	start = req_offset(first) + first->wb_offset;
+	end = req_offset(last) + (last->wb_offset + last->wb_bytes);
 	len = end - start;
 	/* If 'len' is not a 32-bit quantity, pass '0' in the COMMIT call */
 	if (end >= inode->i_size || len < 0 || len > (~((u32)0) >> 1))
@@ -1224,7 +1222,7 @@
 			req->wb_inode->i_sb->s_id,
 			(long long)NFS_FILEID(req->wb_inode),
 			req->wb_bytes,
-			(long long)(page_offset(req->wb_page) + req->wb_offset));
+			(long long)(req_offset(req) + req->wb_offset));
 		if (task->tk_status < 0) {
 			if (req->wb_file)
 				req->wb_file->f_error = task->tk_status;
diff -Nru a/fs/partitions/ibm.c b/fs/partitions/ibm.c
--- a/fs/partitions/ibm.c	Mon Oct  7 11:25:39 2002
+++ b/fs/partitions/ibm.c	Mon Oct  7 11:25:39 2002
@@ -12,23 +12,16 @@
  */
 
 #include <linux/config.h>
-#include <linux/fs.h>
-#include <linux/genhd.h>
-#include <linux/kernel.h>
-#include <linux/major.h>
-#include <linux/string.h>
-#include <linux/blk.h>
-#include <linux/slab.h>
+#include <linux/buffer_head.h>
 #include <linux/hdreg.h>
-#include <linux/ioctl.h>
-#include <linux/version.h>
+#include <linux/slab.h>
+#include <asm/dasd.h>
 #include <asm/ebcdic.h>
 #include <asm/uaccess.h>
-#include <asm/dasd.h>
+#include <asm/vtoc.h>
 
-#include "ibm.h"
 #include "check.h"
-#include <asm/vtoc.h>
+#include "ibm.h"
 
 /*
  * compute the block number from a 
diff -Nru a/fs/pipe.c b/fs/pipe.c
--- a/fs/pipe.c	Mon Oct  7 11:25:25 2002
+++ b/fs/pipe.c	Mon Oct  7 11:25:25 2002
@@ -25,6 +25,9 @@
  *
  * FIFOs and Pipes now generate SIGIO for both readers and writers.
  * -- Jeremy Elson <jelson@circlemud.org> 2001-08-16
+ *
+ * pipe_read & write cleanup
+ * -- Manfred Spraul <manfred@colorfullife.com> 2002-05-09
  */
 
 /* Drop the inode semaphore and wait for a pipe event, atomically */
@@ -44,97 +47,81 @@
 pipe_read(struct file *filp, char *buf, size_t count, loff_t *ppos)
 {
 	struct inode *inode = filp->f_dentry->d_inode;
-	ssize_t size, read, ret;
+	int do_wakeup;
+	ssize_t ret;
 
-	/* Seeks are not allowed on pipes.  */
-	ret = -ESPIPE;
-	read = 0;
-	if (ppos != &filp->f_pos)
-		goto out_nolock;
+	/* pread is not allowed on pipes. */
+	if (unlikely(ppos != &filp->f_pos))
+		return -ESPIPE;
+	
+	/* Null read succeeds. */
+	if (unlikely(count == 0))
+		return 0;
 
-	/* Always return 0 on null read.  */
+	do_wakeup = 0;
 	ret = 0;
-	if (count == 0)
-		goto out_nolock;
+	down(PIPE_SEM(*inode));
+	for (;;) {
+		int size = PIPE_LEN(*inode);
+		if (size) {
+			char *pipebuf = PIPE_BASE(*inode) + PIPE_START(*inode);
+			ssize_t chars = PIPE_MAX_RCHUNK(*inode);
 
-	/* Get the pipe semaphore */
-	ret = -ERESTARTSYS;
-	if (down_interruptible(PIPE_SEM(*inode)))
-		goto out_nolock;
-
-	if (PIPE_EMPTY(*inode)) {
-do_more_read:
-		ret = 0;
-		if (!PIPE_WRITERS(*inode))
-			goto out;
+			if (chars > count)
+				chars = count;
+			if (chars > size)
+				chars = size;
 
-		ret = -EAGAIN;
-		if (filp->f_flags & O_NONBLOCK)
-			goto out;
-
-		for (;;) {
-			PIPE_WAITING_READERS(*inode)++;
-			pipe_wait(inode);
-			PIPE_WAITING_READERS(*inode)--;
-			ret = -ERESTARTSYS;
-			if (signal_pending(current))
-				goto out;
-			ret = 0;
-			if (!PIPE_EMPTY(*inode))
+			if (copy_to_user(buf, pipebuf, chars)) {
+				if (!ret) ret = -EFAULT;
 				break;
-			if (!PIPE_WRITERS(*inode))
-				goto out;
-		}
-	}
+			}
+			ret += chars;
 
-	/* Read what data is available.  */
-	ret = -EFAULT;
-	while (count > 0 && (size = PIPE_LEN(*inode))) {
-		char *pipebuf = PIPE_BASE(*inode) + PIPE_START(*inode);
-		ssize_t chars = PIPE_MAX_RCHUNK(*inode);
-
-		if (chars > count)
-			chars = count;
-		if (chars > size)
-			chars = size;
-
-		if (copy_to_user(buf, pipebuf, chars))
-			goto out;
-
-		read += chars;
-		PIPE_START(*inode) += chars;
-		PIPE_START(*inode) &= (PIPE_SIZE - 1);
-		PIPE_LEN(*inode) -= chars;
-		count -= chars;
-		buf += chars;
+			PIPE_START(*inode) += chars;
+			PIPE_START(*inode) &= (PIPE_SIZE - 1);
+			PIPE_LEN(*inode) -= chars;
+			count -= chars;
+			buf += chars;
+			do_wakeup = 1;
+		}
+		if (!count)
+			break;	/* common path: read succeeded */
+		if (PIPE_LEN(*inode)) /* test for cyclic buffers */
+			continue;
+		if (!PIPE_WRITERS(*inode))
+			break;
+		if (!PIPE_WAITING_WRITERS(*inode)) {
+			/* syscall merging: Usually we must not sleep
+			 * if O_NONBLOCK is set, or if we got some data.
+			 * But if a writer sleeps in kernel space, then
+			 * we can wait for that data without violating POSIX.
+			 */
+			if (ret)
+				break;
+			if (filp->f_flags & O_NONBLOCK) {
+				ret = -EAGAIN;
+				break;
+			}
+		}
+		if (signal_pending(current)) {
+			if (!ret) ret = -ERESTARTSYS;
+			break;
+		}
+		if (do_wakeup) {
+			wake_up_interruptible(PIPE_WAIT(*inode));
+ 			kill_fasync(PIPE_FASYNC_WRITERS(*inode), SIGIO, POLL_OUT);
+		}
+		pipe_wait(inode);
 	}
-
-	/* Cache behaviour optimization */
-	if (!PIPE_LEN(*inode))
-		PIPE_START(*inode) = 0;
-
-	if (count && PIPE_WAITING_WRITERS(*inode) && !(filp->f_flags & O_NONBLOCK)) {
-		/*
-		 * We know that we are going to sleep: signal
-		 * writers synchronously that there is more
-		 * room.
-		 */
+	up(PIPE_SEM(*inode));
+	/* Signal writers asynchronously that there is more room.  */
+	if (do_wakeup) {
 		wake_up_interruptible_sync(PIPE_WAIT(*inode));
- 		kill_fasync(PIPE_FASYNC_WRITERS(*inode), SIGIO, POLL_OUT);
-		if (!PIPE_EMPTY(*inode))
-			BUG();
-		goto do_more_read;
+		kill_fasync(PIPE_FASYNC_WRITERS(*inode), SIGIO, POLL_OUT);
 	}
-	/* Signal writers asynchronously that there is more room.  */
-	wake_up_interruptible(PIPE_WAIT(*inode));
- 	kill_fasync(PIPE_FASYNC_WRITERS(*inode), SIGIO, POLL_OUT);
-
-	ret = read;
-out:
-	up(PIPE_SEM(*inode));
-out_nolock:
-	if (read)
-		ret = read;
+	if (ret > 0)
+		UPDATE_ATIME(inode);
 	return ret;
 }
 
@@ -142,116 +129,90 @@
 pipe_write(struct file *filp, const char *buf, size_t count, loff_t *ppos)
 {
 	struct inode *inode = filp->f_dentry->d_inode;
-	ssize_t free, written, ret;
-
-	/* Seeks are not allowed on pipes.  */
-	ret = -ESPIPE;
-	written = 0;
-	if (ppos != &filp->f_pos)
-		goto out_nolock;
+	ssize_t ret;
+	size_t min;
+	int do_wakeup;
+
+	/* pwrite is not allowed on pipes. */
+	if (unlikely(ppos != &filp->f_pos))
+		return -ESPIPE;
+	
+	/* Null write succeeds. */
+	if (unlikely(count == 0))
+		return 0;
 
-	/* Null write succeeds.  */
+	do_wakeup = 0;
 	ret = 0;
-	if (count == 0)
-		goto out_nolock;
-
-	ret = -ERESTARTSYS;
-	if (down_interruptible(PIPE_SEM(*inode)))
-		goto out_nolock;
-
-	/* No readers yields SIGPIPE.  */
-	if (!PIPE_READERS(*inode))
-		goto sigpipe;
-
-	/* If count <= PIPE_BUF, we have to make it atomic.  */
-	free = (count <= PIPE_BUF ? count : 1);
-
-	/* Wait, or check for, available space.  */
-	if (filp->f_flags & O_NONBLOCK) {
-		ret = -EAGAIN;
-		if (PIPE_FREE(*inode) < free)
-			goto out;
-	} else {
-		while (PIPE_FREE(*inode) < free) {
-			PIPE_WAITING_WRITERS(*inode)++;
-			pipe_wait(inode);
-			PIPE_WAITING_WRITERS(*inode)--;
-			ret = -ERESTARTSYS;
-			if (signal_pending(current))
-				goto out;
-
-			if (!PIPE_READERS(*inode))
-				goto sigpipe;
+	min = count;
+	if (min > PIPE_BUF)
+		min = 1;
+	down(PIPE_SEM(*inode));
+	for (;;) {
+		int free;
+		if (!PIPE_READERS(*inode)) {
+			send_sig(SIGPIPE, current, 0);
+			if (!ret) ret = -EPIPE;
+			break;
 		}
-	}
-
-	/* Copy into available space.  */
-	ret = -EFAULT;
-	while (count > 0) {
-		int space;
-		char *pipebuf = PIPE_BASE(*inode) + PIPE_END(*inode);
-		ssize_t chars = PIPE_MAX_WCHUNK(*inode);
-
-		if ((space = PIPE_FREE(*inode)) != 0) {
+		free = PIPE_FREE(*inode);
+		if (free >= min) {
+			/* transfer data */
+			ssize_t chars = PIPE_MAX_WCHUNK(*inode);
+			char *pipebuf = PIPE_BASE(*inode) + PIPE_END(*inode);
+			/* Always wakeup, even if the copy fails. Otherwise
+			 * we lock up (O_NONBLOCK-)readers that sleep due to
+			 * syscall merging.
+			 */
+			do_wakeup = 1;
 			if (chars > count)
 				chars = count;
-			if (chars > space)
-				chars = space;
+			if (chars > free)
+				chars = free;
 
-			if (copy_from_user(pipebuf, buf, chars))
-				goto out;
+			if (copy_from_user(pipebuf, buf, chars)) {
+				if (!ret) ret = -EFAULT;
+				break;
+			}
 
-			written += chars;
+			ret += chars;
 			PIPE_LEN(*inode) += chars;
 			count -= chars;
 			buf += chars;
-			space = PIPE_FREE(*inode);
+		}
+		if (!count)
+			break;
+		if (PIPE_FREE(*inode) && ret) {
+			/* handle cyclic data buffers */
+			min = 1;
 			continue;
 		}
-
-		ret = written;
-		if (filp->f_flags & O_NONBLOCK)
+		if (filp->f_flags & O_NONBLOCK) {
+			if (!ret) ret = -EAGAIN;
 			break;
-
-		do {
-			/*
-			 * Synchronous wake-up: it knows that this process
-			 * is going to give up this CPU, so it doesn't have
-			 * to do idle reschedules.
-			 */
+		}
+		if (signal_pending(current)) {
+			if (!ret) ret = -ERESTARTSYS;
+			break;
+		}
+		if (do_wakeup) {
 			wake_up_interruptible_sync(PIPE_WAIT(*inode));
- 			kill_fasync(PIPE_FASYNC_READERS(*inode), SIGIO, POLL_IN);
-			PIPE_WAITING_WRITERS(*inode)++;
-			pipe_wait(inode);
-			PIPE_WAITING_WRITERS(*inode)--;
-			if (signal_pending(current))
-				goto out;
-			if (!PIPE_READERS(*inode))
-				goto sigpipe;
-		} while (!PIPE_FREE(*inode));
-		ret = -EFAULT;
+			kill_fasync(PIPE_FASYNC_READERS(*inode), SIGIO, POLL_IN);
+			do_wakeup = 0;
+		}
+		PIPE_WAITING_WRITERS(*inode)++;
+		pipe_wait(inode);
+		PIPE_WAITING_WRITERS(*inode)--;
 	}
-
-	/* Signal readers asynchronously that there is more data.  */
-	wake_up_interruptible(PIPE_WAIT(*inode));
-	kill_fasync(PIPE_FASYNC_READERS(*inode), SIGIO, POLL_IN);
-
-	inode->i_ctime = inode->i_mtime = CURRENT_TIME;
-	mark_inode_dirty(inode);
-
-out:
 	up(PIPE_SEM(*inode));
-out_nolock:
-	if (written)
-		ret = written;
+	if (do_wakeup) {
+		wake_up_interruptible(PIPE_WAIT(*inode));
+		kill_fasync(PIPE_FASYNC_READERS(*inode), SIGIO, POLL_IN);
+	}
+	if (ret > 0) {
+		inode->i_ctime = inode->i_mtime = CURRENT_TIME;
+		mark_inode_dirty(inode);
+	}
 	return ret;
-
-sigpipe:
-	if (written)
-		goto out;
-	up(PIPE_SEM(*inode));
-	send_sig(SIGPIPE, current, 0);
-	return -EPIPE;
 }
 
 static ssize_t
@@ -525,7 +486,7 @@
 	PIPE_BASE(*inode) = (char*) page;
 	PIPE_START(*inode) = PIPE_LEN(*inode) = 0;
 	PIPE_READERS(*inode) = PIPE_WRITERS(*inode) = 0;
-	PIPE_WAITING_READERS(*inode) = PIPE_WAITING_WRITERS(*inode) = 0;
+	PIPE_WAITING_WRITERS(*inode) = 0;
 	PIPE_RCOUNTER(*inode) = PIPE_WCOUNTER(*inode) = 1;
 	*PIPE_FASYNC_READERS(*inode) = *PIPE_FASYNC_WRITERS(*inode) = NULL;
 
diff -Nru a/fs/proc/proc_misc.c b/fs/proc/proc_misc.c
--- a/fs/proc/proc_misc.c	Mon Oct  7 11:25:26 2002
+++ b/fs/proc/proc_misc.c	Mon Oct  7 11:25:26 2002
@@ -252,6 +252,18 @@
 	.release	= seq_release,
 };
 
+extern struct seq_operations vmstat_op;
+static int vmstat_open(struct inode *inode, struct file *file)
+{
+	return seq_open(file, &vmstat_op);
+}
+static struct file_operations proc_vmstat_file_operations = {
+	open:		vmstat_open,
+	read:		seq_read,
+	llseek:		seq_lseek,
+	release:	seq_release,
+};
+
 #ifdef CONFIG_PROC_HARDWARE
 static int hardware_read_proc(char *page, char **start, off_t off,
 				 int count, int *eof, void *data)
@@ -327,7 +339,7 @@
 	int i, len;
 	extern unsigned long total_forks;
 	unsigned long jif = jiffies;
-	unsigned int sum = 0, user = 0, nice = 0, system = 0;
+	unsigned int sum = 0, user = 0, nice = 0, system = 0, idle = 0, iowait = 0;
 	int major, disk;
 
 	for (i = 0 ; i < NR_CPUS; i++) {
@@ -337,38 +349,32 @@
 		user += kstat.per_cpu_user[i];
 		nice += kstat.per_cpu_nice[i];
 		system += kstat.per_cpu_system[i];
+		idle += kstat.per_cpu_idle[i];
+		iowait += kstat.per_cpu_iowait[i];
 #if !defined(CONFIG_ARCH_S390)
 		for (j = 0 ; j < NR_IRQS ; j++)
 			sum += kstat.irqs[i][j];
 #endif
 	}
 
-	len = sprintf(page, "cpu  %u %u %u %lu\n",
+	len = sprintf(page, "cpu  %u %u %u %u %u\n",
 		jiffies_to_clock_t(user),
 		jiffies_to_clock_t(nice),
 		jiffies_to_clock_t(system),
-		jiffies_to_clock_t(jif * num_online_cpus() - (user + nice + system)));
+		jiffies_to_clock_t(idle),
+		jiffies_to_clock_t(iowait));
 	for (i = 0 ; i < NR_CPUS; i++){
 		if (!cpu_online(i)) continue;
-		len += sprintf(page + len, "cpu%d %u %u %u %lu\n",
+		len += sprintf(page + len, "cpu%d %u %u %u %u %u\n",
 			i,
 			jiffies_to_clock_t(kstat.per_cpu_user[i]),
 			jiffies_to_clock_t(kstat.per_cpu_nice[i]),
 			jiffies_to_clock_t(kstat.per_cpu_system[i]),
-			jiffies_to_clock_t(jif - (  kstat.per_cpu_user[i] \
-				   + kstat.per_cpu_nice[i] \
-				   + kstat.per_cpu_system[i])));
+			jiffies_to_clock_t(kstat.per_cpu_idle[i]),
+			jiffies_to_clock_t(kstat.per_cpu_iowait[i]));
 	}
-	len += sprintf(page + len,
-		"page %u %u\n"
-		"swap %u %u\n"
-		"intr %u",
-			kstat.pgpgin >> 1,
-			kstat.pgpgout >> 1,
-			kstat.pswpin,
-			kstat.pswpout,
-			sum
-	);
+	len += sprintf(page + len, "intr %u", sum);
+
 #if !defined(CONFIG_ARCH_S390)
 	for (i = 0 ; i < NR_IRQS ; i++)
 		len += sprintf(page + len, " %u", kstat_irqs(i));
@@ -395,29 +401,9 @@
 	}
 
 	len += sprintf(page + len,
-		"\npageallocs %u\n"
-		"pagefrees %u\n"
-		"pageactiv %u\n"
-		"pagedeact %u\n"
-		"pagefault %u\n"
-		"majorfault %u\n"
-		"pagescan %u\n"
-		"pagesteal %u\n"
-		"pageoutrun %u\n"
-		"allocstall %u\n"
-		"ctxt %lu\n"
+		"\nctxt %lu\n"
 		"btime %lu\n"
 		"processes %lu\n",
-		kstat.pgalloc,
-		kstat.pgfree,
-		kstat.pgactivate,
-		kstat.pgdeactivate,
-		kstat.pgfault,
-		kstat.pgmajfault,
-		kstat.pgscan,
-		kstat.pgsteal,
-		kstat.pageoutrun,
-		kstat.allocstall,
 		nr_context_switches(),
 		xtime.tv_sec - jif / HZ,
 		total_forks);
@@ -432,6 +418,7 @@
 	return proc_calc_metrics(page, start, off, count, eof, len);
 }
 
+#if !defined(CONFIG_ARCH_S390)
 extern int show_interrupts(struct seq_file *p, void *v);
 static int interrupts_open(struct inode *inode, struct file *file)
 {
@@ -461,6 +448,7 @@
 	.llseek		= seq_lseek,
 	.release	= single_release,
 };
+#endif
 
 static int filesystems_read_proc(char *page, char **start, off_t off,
 				 int count, int *eof, void *data)
@@ -643,9 +631,12 @@
 		entry->proc_fops = &proc_kmsg_operations;
 	create_seq_entry("cpuinfo", 0, &proc_cpuinfo_operations);
 	create_seq_entry("partitions", 0, &proc_partitions_operations);
+#if !defined(CONFIG_ARCH_S390)
 	create_seq_entry("interrupts", 0, &proc_interrupts_operations);
+#endif
 	create_seq_entry("slabinfo",S_IWUSR|S_IRUGO,&proc_slabinfo_operations);
 	create_seq_entry("buddyinfo",S_IRUGO, &fragmentation_file_operations);
+	create_seq_entry("vmstat",S_IRUGO, &proc_vmstat_file_operations);
 #ifdef CONFIG_MODULES
 	create_seq_entry("modules", 0, &proc_modules_operations);
 	create_seq_entry("ksyms", 0, &proc_ksyms_operations);
diff -Nru a/fs/reiserfs/journal.c b/fs/reiserfs/journal.c
--- a/fs/reiserfs/journal.c	Mon Oct  7 11:25:35 2002
+++ b/fs/reiserfs/journal.c	Mon Oct  7 11:25:35 2002
@@ -59,12 +59,15 @@
 #include <linux/smp_lock.h>
 #include <linux/suspend.h>
 #include <linux/buffer_head.h>
+#include <linux/workqueue.h>
 
 /* the number of mounted filesystems.  This is used to decide when to
-** start and kill the commit thread
+** start and kill the commit workqueue
 */
 static int reiserfs_mounted_fs_count = 0 ;
 
+static struct workqueue_struct *commit_wq;
+
 #define JOURNAL_TRANS_HALF 1018   /* must be correct to keep the desc and commit
 				     structs at 4k */
 #define BUFNR 64 /*read ahead */
@@ -1334,7 +1337,11 @@
 
   reiserfs_mounted_fs_count-- ;
   /* wait for all commits to finish */
-  flush_scheduled_tasks();
+  flush_workqueue(commit_wq);
+  if (!reiserfs_mounted_fs_count) {
+    destroy_workqueue(commit_wq);
+    commit_wq = NULL;
+  }
 
   release_journal_dev( p_s_sb, SB_JOURNAL( p_s_sb ) );
   free_journal_ram(p_s_sb) ;
@@ -1798,12 +1805,11 @@
                        ** is zero, we free the whole struct on finish
 		       */
   struct reiserfs_journal_commit_task *self ;
-  struct wait_queue *task_done ;
-  struct tq_struct task ;
+  struct work_struct work;
 } ;
 
-static void reiserfs_journal_commit_task_func(struct reiserfs_journal_commit_task *ct) {
-
+static void reiserfs_journal_commit_task_func(void *__ct) {
+  struct reiserfs_journal_commit_task *ct = __ct;
   struct reiserfs_journal_list *jl ;
 
   /* FIXMEL: is this needed? */
@@ -1829,12 +1835,8 @@
   }
   ct->p_s_sb = p_s_sb ;
   ct->jindex = jindex ;
-  ct->task_done = NULL ;
-  INIT_LIST_HEAD(&ct->task.list) ;
-  ct->task.sync = 0 ;
-  ct->task.routine = (void *)(void *)reiserfs_journal_commit_task_func ; 
+  INIT_WORK(&ct->work, reiserfs_journal_commit_task_func, ct);
   ct->self = ct ;
-  ct->task.data = (void *)ct ;
 }
 
 static void commit_flush_async(struct super_block *p_s_sb, int jindex) {
@@ -1845,7 +1847,7 @@
   ct = reiserfs_kmalloc(sizeof(struct reiserfs_journal_commit_task), GFP_NOFS, p_s_sb) ;
   if (ct) {
     setup_commit_task_arg(ct, p_s_sb, jindex) ;
-    schedule_task(&ct->task) ;
+    queue_work(commit_wq, &ct->work) ;
   } else {
 #ifdef CONFIG_REISERFS_CHECK
     reiserfs_warning("journal-1540: kmalloc failed, doing sync commit\n") ;
@@ -2126,6 +2128,9 @@
     return 0;
 
   reiserfs_mounted_fs_count++ ;
+  if (reiserfs_mounted_fs_count <= 1)
+    commit_wq = create_workqueue("reiserfs");
+
   return 0 ;
 
 }
diff -Nru a/fs/smbfs/inode.c b/fs/smbfs/inode.c
--- a/fs/smbfs/inode.c	Mon Oct  7 11:25:26 2002
+++ b/fs/smbfs/inode.c	Mon Oct  7 11:25:26 2002
@@ -210,7 +210,7 @@
 			(long) last_sz, (long) inode->i_size);
 
 		if (!S_ISDIR(inode->i_mode))
-			invalidate_inode_pages(inode);
+			invalidate_inode_pages(inode->i_mapping);
 	}
 }
 
@@ -274,7 +274,7 @@
 			 * But we do want to invalidate the caches ...
 			 */
 			if (!S_ISDIR(inode->i_mode))
-				invalidate_inode_pages(inode);
+				invalidate_inode_pages(inode->i_mapping);
 			else
 				smb_invalid_dir_cache(inode);
 			error = -EIO;
diff -Nru a/fs/smbfs/sock.c b/fs/smbfs/sock.c
--- a/fs/smbfs/sock.c	Mon Oct  7 11:25:31 2002
+++ b/fs/smbfs/sock.c	Mon Oct  7 11:25:31 2002
@@ -19,7 +19,7 @@
 #include <linux/mm.h>
 #include <linux/netdevice.h>
 #include <linux/smp_lock.h>
-#include <linux/tqueue.h>
+#include <linux/workqueue.h>
 #include <net/scm.h>
 #include <net/ip.h>
 
diff -Nru a/fs/smbfs/symlink.c b/fs/smbfs/symlink.c
--- a/fs/smbfs/symlink.c	Mon Oct  7 11:25:36 2002
+++ b/fs/smbfs/symlink.c	Mon Oct  7 11:25:36 2002
@@ -63,6 +63,6 @@
 
 struct inode_operations smb_link_inode_operations =
 {
-	.readlink	smb_read_link,
-	.follow_link	smb_follow_link,
+	.readlink	= smb_read_link,
+	.follow_link	= smb_follow_link,
 };
diff -Nru a/fs/vfat/namei.c b/fs/vfat/namei.c
--- a/fs/vfat/namei.c	Mon Oct  7 11:25:36 2002
+++ b/fs/vfat/namei.c	Mon Oct  7 11:25:36 2002
@@ -117,8 +117,6 @@
 	savep = NULL;
 	ret = 1;
 	while ((this_char = strsep(&options,",")) != NULL) {
-		if (!*this_char)
-			continue;
 		if ((value = strchr(this_char,'=')) != NULL) {
 			save = *value;
 			savep = value;
@@ -154,8 +152,8 @@
 			else
 				ret = 0;
 		}
-		if (this_char != options)
-			*(this_char-1) = ',';
+		if (options)
+			*(options-1) = ',';
 		if (value) {
 			*savep = save;
 		}
diff -Nru a/fs/xfs/Makefile b/fs/xfs/Makefile
--- a/fs/xfs/Makefile	Mon Oct  7 11:25:36 2002
+++ b/fs/xfs/Makefile	Mon Oct  7 11:25:36 2002
@@ -32,15 +32,10 @@
 # Makefile for XFS on Linux.
 #
 
-
-# we also have source in the subdirectories..
-vpath %.c =  . linux pagebuf support
-
-
-# This needs -I. because everything does #include <xfs.h> instead of "xfs.h".
+# This needs -I because everything does #include <xfs.h> instead of "xfs.h".
 # The code is wrong, local files should be included using "xfs.h", not <xfs.h>
 # but I am not going to change every file at the moment.
-EXTRA_CFLAGS +=	 -I. -funsigned-char
+EXTRA_CFLAGS +=	 -Ifs/xfs -funsigned-char
 
 ifeq ($(CONFIG_XFS_DEBUG),y)
 	EXTRA_CFLAGS += -g -DSTATIC="" -DDEBUG -DXFSDEBUG
@@ -49,7 +44,8 @@
 	EXTRA_CFLAGS += -DPAGEBUF_TRACE
 endif
 
-export-objs			:= page_buf.o ktrace.o xfs_globals.o
+export-objs			:= pagebuf/page_buf.o support/ktrace.o \
+				   linux/xfs_globals.o
 
 obj-$(CONFIG_XFS_FS)		+= xfs.o
 
@@ -65,8 +61,8 @@
 xfs-obj-$(CONFIG_FS_POSIX_ACL)	+= xfs_acl.o
 xfs-obj-$(CONFIG_FS_POSIX_CAP)	+= xfs_cap.o
 xfs-obj-$(CONFIG_FS_POSIX_MAC)	+= xfs_mac.o
-xfs-obj-$(CONFIG_PROC_FS)	+= xfs_stats.o
-xfs-obj-$(CONFIG_SYSCTL)	+= xfs_sysctl.o
+xfs-obj-$(CONFIG_PROC_FS)	+= linux/xfs_stats.o
+xfs-obj-$(CONFIG_SYSCTL)	+= linux/xfs_sysctl.o
 
 
 xfs-objs			+= $(xfs-obj-y) \
@@ -118,11 +114,13 @@
 				   xfs_rw.o
 
 # Objects in pagebuf/
-xfs-objs			+= page_buf.o \
-				   page_buf_locking.o
+xfs-objs			+= $(addprefix pagebuf/, \
+				   page_buf.o \
+				   page_buf_locking.o)
 
 # Objects in linux/
-xfs-objs			+= xfs_aops.o \
+xfs-objs			+= $(addprefix linux/, \
+				   xfs_aops.o \
 				   xfs_behavior.o \
 				   xfs_file.o \
 				   xfs_fs_subr.o \
@@ -131,16 +129,17 @@
 				   xfs_iops.o \
 				   xfs_lrw.o \
 				   xfs_super.o \
-				   xfs_vnode.o
+				   xfs_vnode.o)
 
 # Objects in support/
-xfs-objs			+= debug.o \
+xfs-objs			+= $(addprefix support/, \
+				   debug.o \
 				   kmem.o \
 				   ktrace.o \
 				   move.o \
 				   mrlock.o \
 				   qsort.o \
-				   uuid.o
+				   uuid.o)
 
 # If both xfs and kdb modules are built in then xfsidbg is built in.  If xfs is
 # a module and kdb modules are being compiled then xfsidbg must be a module, to
@@ -155,6 +154,6 @@
   endif
 endif
 
-CFLAGS_xfsidbg.o += -I $(TOPDIR)/arch/$(ARCH)/kdb
+CFLAGS_xfsidbg.o += -Iarch/$(ARCH)/kdb
 
 include $(TOPDIR)/Rules.make
diff -Nru a/fs/xfs/linux/xfs_aops.c b/fs/xfs/linux/xfs_aops.c
--- a/fs/xfs/linux/xfs_aops.c	Mon Oct  7 11:25:38 2002
+++ b/fs/xfs/linux/xfs_aops.c	Mon Oct  7 11:25:38 2002
@@ -681,11 +681,13 @@
 STATIC int
 linvfs_direct_IO(
 	int			rw,
-	struct inode		*inode,
+	struct file		*file,
 	const struct iovec	*iov,
 	loff_t			offset,
 	unsigned long		nr_segs)
 {
+	struct inode *inode = file->f_dentry->d_inode->i_mapping->host;
+
         return generic_direct_IO(rw, inode, iov, offset, nr_segs,
 					linvfs_get_blocks_direct);
 }
diff -Nru a/fs/xfs/pagebuf/page_buf.c b/fs/xfs/pagebuf/page_buf.c
--- a/fs/xfs/pagebuf/page_buf.c	Mon Oct  7 11:25:25 2002
+++ b/fs/xfs/pagebuf/page_buf.c	Mon Oct  7 11:25:25 2002
@@ -160,8 +160,6 @@
 
 STATIC kmem_cache_t *pagebuf_cache;
 STATIC pagebuf_daemon_t *pb_daemon;
-STATIC struct list_head pagebuf_iodone_tq[NR_CPUS];
-STATIC wait_queue_head_t pagebuf_iodone_wait[NR_CPUS];
 STATIC void pagebuf_daemon_wakeup(int);
 
 /*
@@ -181,6 +179,11 @@
 struct pbstats pbstats;
 
 /*
+ * Queue for delayed I/O completion.
+ */
+struct workqueue_struct *pagebuf_workqueue;
+
+/*
  * Pagebuf allocation / freeing.
  */
 
@@ -1157,15 +1160,6 @@
 	current->state = TASK_RUNNING;
 }
 
-void
-pagebuf_queue_task(
-	struct tq_struct	*task)
-{
-	queue_task(task, &pagebuf_iodone_tq[smp_processor_id()]);
-	wake_up(&pagebuf_iodone_wait[smp_processor_id()]);
-}
-
-
 /*
  *	Buffer Utility Routines
  */
@@ -1178,7 +1172,7 @@
  *	present, will be called as a side-effect.
  */
 void
-pagebuf_iodone_sched(
+pagebuf_iodone_work(
 	void			*v)
 {
 	page_buf_t		*pb = (page_buf_t *)v;
@@ -1207,12 +1201,8 @@
 	PB_TRACE(pb, PB_TRACE_REC(done), pb->pb_iodone);
 
 	if ((pb->pb_iodone) || (pb->pb_flags & PBF_ASYNC)) {
-		INIT_TQUEUE(&pb->pb_iodone_sched,
-			pagebuf_iodone_sched, (void *)pb);
-
-		queue_task(&pb->pb_iodone_sched,
-				&pagebuf_iodone_tq[smp_processor_id()]);
-		wake_up(&pagebuf_iodone_wait[smp_processor_id()]);
+		INIT_WORK(&pb->pb_iodone_work, pagebuf_iodone_work, pb);
+		queue_work(pagebuf_workqueue, &pb->pb_iodone_work);
 	} else {
 		up(&pb->pb_iodonesema);
 	}
@@ -1666,62 +1656,6 @@
 	spin_unlock(&pb_daemon->pb_delwrite_lock);
 }
 
-
-/*
- * The pagebuf iodone daemon
- */
-
-STATIC int pb_daemons[NR_CPUS];
-
-STATIC int
-pagebuf_iodone_daemon(
-	void			*__bind_cpu)
-{
-	int			cpu = (long) __bind_cpu;
-	DECLARE_WAITQUEUE	(wait, current);
-
-	/*  Set up the thread  */
-	daemonize();
-
-	/* Avoid signals */
-	spin_lock_irq(&current->sig->siglock);
-	sigfillset(&current->blocked);
-	recalc_sigpending();
-	spin_unlock_irq(&current->sig->siglock);
-
-	/* Migrate to the right CPU */
-	set_cpus_allowed(current, 1UL << cpu);
-	if (smp_processor_id() != cpu)
-		BUG();
-
-	sprintf(current->comm, "pagebuf_io_CPU%d", cpu);
-	INIT_LIST_HEAD(&pagebuf_iodone_tq[cpu]);
-	init_waitqueue_head(&pagebuf_iodone_wait[cpu]);
-	__set_current_state(TASK_INTERRUPTIBLE);
-	mb();
-
-	pb_daemons[cpu] = 1;
-
-	for (;;) {
-		add_wait_queue(&pagebuf_iodone_wait[cpu],
-				&wait);
-
-		if (TQ_ACTIVE(pagebuf_iodone_tq[cpu]))
-			__set_task_state(current, TASK_RUNNING);
-		schedule();
-		remove_wait_queue(&pagebuf_iodone_wait[cpu],
-				&wait);
-		run_task_queue(&pagebuf_iodone_tq[cpu]);
-		if (pb_daemons[cpu] == 0)
-			break;
-		__set_current_state(TASK_INTERRUPTIBLE);
-	}
-
-	pb_daemons[cpu] = -1;
-	wake_up_interruptible(&pagebuf_iodone_wait[cpu]);
-	return 0;
-}
-
 /* Defines for pagebuf daemon */
 DECLARE_WAIT_QUEUE_HEAD(pbd_waitq);
 STATIC int force_flush;
@@ -1746,7 +1680,7 @@
 	page_buf_t		*pb;
 	struct list_head	*curr, *next, tmp;
 	struct timer_list	pb_daemon_timer =
-		{ {NULL, NULL}, 0, 0, (timeout_fn)pagebuf_daemon_wakeup };
+		{ .function = (timeout_fn)pagebuf_daemon_wakeup };
 
 	/*  Set up the thread  */
 	daemonize();
@@ -1907,8 +1841,6 @@
 pagebuf_daemon_start(void)
 {
 	if (!pb_daemon) {
-		int		cpu;
-
 		pb_daemon = (pagebuf_daemon_t *)
 				kmalloc(sizeof(pagebuf_daemon_t), GFP_KERNEL);
 		if (!pb_daemon) {
@@ -1924,19 +1856,10 @@
 
 		kernel_thread(pagebuf_daemon, (void *)pb_daemon,
 				CLONE_FS|CLONE_FILES|CLONE_VM);
-		for (cpu = 0; cpu < NR_CPUS; cpu++) {
-			if (!cpu_online(cpu))
-				continue;
-			if (kernel_thread(pagebuf_iodone_daemon,
-					(void *)(long) cpu,
-					CLONE_FS|CLONE_FILES|CLONE_VM) < 0) {
-				printk("pagebuf_daemon_start failed\n");
-			} else {
-				while (!pb_daemons[cpu]) {
-					yield();
-				}
-			}
-		}
+
+		pagebuf_workqueue = create_workqueue("pagebuf");
+		if (!pagebuf_workqueue)
+			return -1;
 	}
 	return 0;
 }
@@ -1950,7 +1873,7 @@
 pagebuf_daemon_stop(void)
 {
 	if (pb_daemon) {
-		int		cpu;
+		destroy_workqueue(pagebuf_workqueue);
 
 		pb_daemon->active = 0;
 		pb_daemon->io_active = 0;
@@ -1958,16 +1881,6 @@
 		wake_up_interruptible(&pbd_waitq);
 		while (pb_daemon->active == 0) {
 			interruptible_sleep_on(&pbd_waitq);
-		}
-		for (cpu = 0; cpu < NR_CPUS; cpu++) {
-			if (!cpu_online(cpu))
-				continue;
-			pb_daemons[cpu] = 0;
-			wake_up(&pagebuf_iodone_wait[cpu]);
-			while (pb_daemons[cpu] != -1) {
-				interruptible_sleep_on(
-					&pagebuf_iodone_wait[cpu]);
-			}
 		}
 
 		kfree(pb_daemon);
diff -Nru a/fs/xfs/pagebuf/page_buf.h b/fs/xfs/pagebuf/page_buf.h
--- a/fs/xfs/pagebuf/page_buf.h	Mon Oct  7 11:25:25 2002
+++ b/fs/xfs/pagebuf/page_buf.h	Mon Oct  7 11:25:25 2002
@@ -47,7 +47,7 @@
 #include <linux/fs.h>
 #include <linux/buffer_head.h>
 #include <linux/uio.h>
-#include <linux/tqueue.h>
+#include <linux/workqueue.h>
 
 enum xfs_buffer_state { BH_Delay = BH_PrivateStart };
 BUFFER_FNS(Delay, delay);
@@ -214,7 +214,7 @@
 	size_t			pb_buffer_length; /* size of buffer in bytes */
 	size_t			pb_count_desired; /* desired transfer size */
 	void			*pb_addr;	/* virtual address of buffer */
-	struct tq_struct	pb_iodone_sched;
+	struct work_struct	pb_iodone_work;
 	page_buf_iodone_t	pb_iodone;	/* I/O completion function */
 	page_buf_relse_t	pb_relse;	/* releasing function */
 	page_buf_bdstrat_t	pb_strat;	/* pre-write function */
@@ -324,9 +324,6 @@
 
 #define pagebuf_geterror(pb)	((pb)->pb_error)
 
-extern void pagebuf_queue_task(
-		struct tq_struct *);
-
 extern void pagebuf_iodone(		/* mark buffer I/O complete	*/
 		page_buf_t *);		/* buffer to mark		*/
 
@@ -397,5 +394,7 @@
 		return pb->pb_strat(pb);
 	return pagebuf_iorequest(pb);
 }
+
+extern struct workqueue_struct *pagebuf_workqueue;
 
 #endif /* __PAGE_BUF_H__ */
diff -Nru a/fs/xfs/xfs_log.c b/fs/xfs/xfs_log.c
--- a/fs/xfs/xfs_log.c	Mon Oct  7 11:25:40 2002
+++ b/fs/xfs/xfs_log.c	Mon Oct  7 11:25:40 2002
@@ -2714,7 +2714,7 @@
 	LOG_UNLOCK(log, s);
 }	/* xlog_state_put_ticket */
 
-void xlog_sync_sched(
+void xlog_sync_work(
 	void	*v)
 {
 	xlog_in_core_t	*iclog = (xlog_in_core_t *)v;
@@ -2773,13 +2773,12 @@
 	 * flags after this point.
 	 */
 	if (sync) {
-		INIT_TQUEUE(&iclog->ic_write_sched,
-			xlog_sync_sched, (void *) iclog);
+		INIT_WORK(&iclog->ic_write_work, xlog_sync_work, iclog);
 		switch (xlog_mode) {
 		case 0:
 			return xlog_sync(log, iclog, 0);
 		case 1:
-			pagebuf_queue_task(&iclog->ic_write_sched);
+		        queue_work(pagebuf_workqueue, &iclog->ic_write_work);
 		}
 	}
 	return (0);
diff -Nru a/fs/xfs/xfs_log_priv.h b/fs/xfs/xfs_log_priv.h
--- a/fs/xfs/xfs_log_priv.h	Mon Oct  7 11:25:26 2002
+++ b/fs/xfs/xfs_log_priv.h	Mon Oct  7 11:25:26 2002
@@ -438,7 +438,7 @@
 	int			ic_bwritecnt;
 	ushort_t		ic_state;
 	char			*ic_datap;	/* pointer to iclog data */
-	struct tq_struct	ic_write_sched;
+	struct work_struct	ic_write_work;
 } xlog_iclog_fields_t;
 
 typedef struct xlog_in_core2 {
@@ -458,7 +458,7 @@
  * Defines to save our code from this glop.
  */
 #define ic_forcesema	hic_fields.ic_forcesema
-#define ic_write_sched	hic_fields.ic_write_sched
+#define ic_write_work	hic_fields.ic_write_work
 #define ic_next		hic_fields.ic_next
 #define ic_prev		hic_fields.ic_prev
 #define ic_bp		hic_fields.ic_bp
diff -Nru a/fs/xfs/xfsidbg.c b/fs/xfs/xfsidbg.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/fs/xfs/xfsidbg.c	Mon Oct  7 11:25:40 2002
@@ -0,0 +1,5230 @@
+/*
+ * Copyright (c) 2000-2002 Silicon Graphics, Inc.  All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like.	 Any license provided herein, whether implied or
+ * otherwise, applies only to this software file.  Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ *
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA  94043, or:
+ *
+ * http://www.sgi.com
+ *
+ * For further information regarding this notice, see:
+ *
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+
+#include <xfs.h>
+#include <xfs_quota_priv.h>
+#include <xfs_log_recover.h>
+#include "pagebuf/page_buf_internal.h"
+
+#include <linux/ctype.h>
+#include <linux/kdb.h>
+#include <linux/kdbprivate.h>
+#include <linux/mm.h>
+#include <linux/init.h>
+
+MODULE_AUTHOR("SGI <sgi.com>");
+MODULE_DESCRIPTION("Additional kdb commands for debugging XFS");
+MODULE_LICENSE("GPL");
+EXPORT_NO_SYMBOLS;
+
+/*
+ * Command table functions.
+ */
+static void	xfsidbg_xagf(xfs_agf_t *);
+static void	xfsidbg_xagi(xfs_agi_t *);
+static void	xfsidbg_xaildump(xfs_mount_t *);
+static void	xfsidbg_xalloc(xfs_alloc_arg_t *);
+static void	xfsidbg_xattrcontext(xfs_attr_list_context_t *);
+static void	xfsidbg_xattrleaf(xfs_attr_leafblock_t *);
+static void	xfsidbg_xattrsf(xfs_attr_shortform_t *);
+static void	xfsidbg_xbirec(xfs_bmbt_irec_t *r);
+static void	xfsidbg_xbmalla(xfs_bmalloca_t *);
+static void	xfsidbg_xbrec(xfs_bmbt_rec_64_t *);
+static void	xfsidbg_xbroot(xfs_inode_t *);
+static void	xfsidbg_xbroota(xfs_inode_t *);
+static void	xfsidbg_xbtcur(xfs_btree_cur_t *);
+static void	xfsidbg_xbuf(xfs_buf_t *);
+static void	xfsidbg_xbuf_real(xfs_buf_t *, int);
+static void	xfsidbg_xchash(xfs_mount_t *mp);
+static void	xfsidbg_xchashlist(xfs_chashlist_t *chl);
+static void	xfsidbg_xdaargs(xfs_da_args_t *);
+static void	xfsidbg_xdabuf(xfs_dabuf_t *);
+static void	xfsidbg_xdanode(xfs_da_intnode_t *);
+static void	xfsidbg_xdastate(xfs_da_state_t *);
+static void	xfsidbg_xdirleaf(xfs_dir_leafblock_t *);
+static void	xfsidbg_xdirsf(xfs_dir_shortform_t *);
+static void	xfsidbg_xdir2free(xfs_dir2_free_t *);
+static void	xfsidbg_xdir2sf(xfs_dir2_sf_t *);
+static void	xfsidbg_xexlist(xfs_inode_t *);
+static void	xfsidbg_xflist(xfs_bmap_free_t *);
+static void	xfsidbg_xhelp(void);
+static void	xfsidbg_xiclog(xlog_in_core_t *);
+static void	xfsidbg_xiclogall(xlog_in_core_t *);
+static void	xfsidbg_xiclogcb(xlog_in_core_t *);
+static void	xfsidbg_xihash(xfs_mount_t *mp);
+static void	xfsidbg_xinodes(xfs_mount_t *);
+static void	xfsidbg_delayed_blocks(xfs_mount_t *);
+static void	xfsidbg_xinodes_quiesce(xfs_mount_t *);
+static void	xfsidbg_xlog(xlog_t *);
+static void	xfsidbg_xlog_ritem(xlog_recover_item_t *);
+static void	xfsidbg_xlog_rtrans(xlog_recover_t *);
+static void	xfsidbg_xlog_rtrans_entire(xlog_recover_t *);
+static void	xfsidbg_xlog_tic(xlog_ticket_t *);
+static void	xfsidbg_xlogitem(xfs_log_item_t *);
+static void	xfsidbg_xmount(xfs_mount_t *);
+static void	xfsidbg_xnode(xfs_inode_t *ip);
+static void	xfsidbg_xcore(xfs_iocore_t *io);
+static void	xfsidbg_xperag(xfs_mount_t *);
+static void	xfsidbg_xqm_diskdq(xfs_disk_dquot_t *);
+static void	xfsidbg_xqm_dqattached_inos(xfs_mount_t *);
+static void	xfsidbg_xqm_dquot(xfs_dquot_t *);
+static void	xfsidbg_xqm_mplist(xfs_mount_t *);
+static void	xfsidbg_xqm_qinfo(xfs_mount_t *mp);
+static void	xfsidbg_xqm_tpdqinfo(xfs_trans_t *tp);
+static void	xfsidbg_xsb(xfs_sb_t *, int convert);
+static void	xfsidbg_xtp(xfs_trans_t *);
+static void	xfsidbg_xtrans_res(xfs_mount_t *);
+#ifdef	CONFIG_XFS_QUOTA
+static void	xfsidbg_xqm(void);
+static void	xfsidbg_xqm_htab(void);
+static void	xfsidbg_xqm_freelist_print(xfs_frlist_t *qlist, char *title);
+static void	xfsidbg_xqm_freelist(void);
+#endif
+
+/* kdb wrappers */
+
+static int	kdbm_xfs_xagf(
+	int	argc,
+	const char **argv,
+	const char **envp,
+	struct pt_regs *regs)
+{
+	unsigned long addr;
+	int nextarg = 1;
+	long offset = 0;
+	int diag;
+
+	if (argc != 1)
+		return KDB_ARGCOUNT;
+	diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL, regs);
+	if (diag)
+		return diag;
+
+	xfsidbg_xagf((xfs_agf_t *)addr);
+	return 0;
+}
+
+static int	kdbm_xfs_xagi(
+	int	argc,
+	const char **argv,
+	const char **envp,
+	struct pt_regs *regs)
+{
+	unsigned long addr;
+	int nextarg = 1;
+	long offset = 0;
+	int diag;
+
+	if (argc != 1)
+		return KDB_ARGCOUNT;
+	diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL, regs);
+	if (diag)
+		return diag;
+
+	xfsidbg_xagi((xfs_agi_t *)addr);
+	return 0;
+}
+
+static int	kdbm_xfs_xaildump(
+	int	argc,
+	const char **argv,
+	const char **envp,
+	struct pt_regs *regs)
+{
+	unsigned long addr;
+	int nextarg = 1;
+	long offset = 0;
+	int diag;
+
+	if (argc != 1)
+		return KDB_ARGCOUNT;
+	diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL, regs);
+	if (diag)
+		return diag;
+
+	xfsidbg_xaildump((xfs_mount_t *) addr);
+	return 0;
+}
+
+static int	kdbm_xfs_xalloc(
+	int	argc,
+	const char **argv,
+	const char **envp,
+	struct pt_regs *regs)
+{
+	unsigned long addr;
+	int nextarg = 1;
+	long offset = 0;
+	int diag;
+
+	if (argc != 1)
+		return KDB_ARGCOUNT;
+	diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL, regs);
+	if (diag)
+		return diag;
+
+	xfsidbg_xalloc((xfs_alloc_arg_t *) addr);
+	return 0;
+}
+
+static int	kdbm_xfs_xattrcontext(
+	int	argc,
+	const char **argv,
+	const char **envp,
+	struct pt_regs *regs)
+{
+	unsigned long addr;
+	int nextarg = 1;
+	long offset = 0;
+	int diag;
+
+	if (argc != 1)
+		return KDB_ARGCOUNT;
+	diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL, regs);
+	if (diag)
+		return diag;
+
+	xfsidbg_xattrcontext((xfs_attr_list_context_t *) addr);
+	return 0;
+}
+
+static int	kdbm_xfs_xattrleaf(
+	int	argc,
+	const char **argv,
+	const char **envp,
+	struct pt_regs *regs)
+{
+	unsigned long addr;
+	int nextarg = 1;
+	long offset = 0;
+	int diag;
+
+	if (argc != 1)
+		return KDB_ARGCOUNT;
+	diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL, regs);
+	if (diag)
+		return diag;
+
+	xfsidbg_xattrleaf((xfs_attr_leafblock_t *) addr);
+	return 0;
+}
+
+static int	kdbm_xfs_xattrsf(
+	int	argc,
+	const char **argv,
+	const char **envp,
+	struct pt_regs *regs)
+{
+	unsigned long addr;
+	int nextarg = 1;
+	long offset = 0;
+	int diag;
+
+	if (argc != 1)
+		return KDB_ARGCOUNT;
+	diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL, regs);
+	if (diag)
+		return diag;
+
+	xfsidbg_xattrsf((xfs_attr_shortform_t *) addr);
+	return 0;
+}
+
+static int	kdbm_xfs_xbirec(
+	int	argc,
+	const char **argv,
+	const char **envp,
+	struct pt_regs *regs)
+{
+	unsigned long addr;
+	int nextarg = 1;
+	long offset = 0;
+	int diag;
+
+	if (argc != 1)
+		return KDB_ARGCOUNT;
+	diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL, regs);
+	if (diag)
+		return diag;
+
+	xfsidbg_xbirec((xfs_bmbt_irec_t *) addr);
+	return 0;
+}
+
+static int	kdbm_xfs_xbmalla(
+	int	argc,
+	const char **argv,
+	const char **envp,
+	struct pt_regs *regs)
+{
+	unsigned long addr;
+	int nextarg = 1;
+	long offset = 0;
+	int diag;
+
+	if (argc != 1)
+		return KDB_ARGCOUNT;
+	diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL, regs);
+	if (diag)
+		return diag;
+
+	xfsidbg_xbmalla((xfs_bmalloca_t *)addr);
+	return 0;
+}
+
+static int	kdbm_xfs_xbrec(
+	int	argc,
+	const char **argv,
+	const char **envp,
+	struct pt_regs *regs)
+{
+	unsigned long addr;
+	int nextarg = 1;
+	long offset = 0;
+	int diag;
+
+	if (argc != 1)
+		return KDB_ARGCOUNT;
+	diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL, regs);
+	if (diag)
+		return diag;
+
+	xfsidbg_xbrec((xfs_bmbt_rec_64_t *) addr);
+	return 0;
+}
+
+static int	kdbm_xfs_xbroot(
+	int	argc,
+	const char **argv,
+	const char **envp,
+	struct pt_regs *regs)
+{
+	unsigned long addr;
+	int nextarg = 1;
+	long offset = 0;
+	int diag;
+
+	if (argc != 1)
+		return KDB_ARGCOUNT;
+	diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL, regs);
+	if (diag)
+		return diag;
+
+	xfsidbg_xbroot((xfs_inode_t *) addr);
+	return 0;
+}
+
+static int	kdbm_xfs_xbroota(
+	int	argc,
+	const char **argv,
+	const char **envp,
+	struct pt_regs *regs)
+{
+	unsigned long addr;
+	int nextarg = 1;
+	long offset = 0;
+	int diag;
+
+	if (argc != 1)
+		return KDB_ARGCOUNT;
+	diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL, regs);
+	if (diag)
+		return diag;
+
+	xfsidbg_xbroota((xfs_inode_t *) addr);
+	return 0;
+}
+
+static int	kdbm_xfs_xbtcur(
+	int	argc,
+	const char **argv,
+	const char **envp,
+	struct pt_regs *regs)
+{
+	unsigned long addr;
+	int nextarg = 1;
+	long offset = 0;
+	int diag;
+
+	if (argc != 1)
+		return KDB_ARGCOUNT;
+	diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL, regs);
+	if (diag)
+		return diag;
+
+	xfsidbg_xbtcur((xfs_btree_cur_t *) addr);
+	return 0;
+}
+
+static int	kdbm_xfs_xbuf(
+	int	argc,
+	const char **argv,
+	const char **envp,
+	struct pt_regs *regs)
+{
+	unsigned long addr;
+	int nextarg = 1;
+	long offset = 0;
+	int diag;
+
+	if (argc != 1)
+		return KDB_ARGCOUNT;
+	diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL, regs);
+	if (diag)
+		return diag;
+
+	xfsidbg_xbuf((xfs_buf_t *) addr);
+	return 0;
+}
+
+
+static int	kdbm_xfs_xchash(
+	int	argc,
+	const char **argv,
+	const char **envp,
+	struct pt_regs *regs)
+{
+	unsigned long addr;
+	int nextarg = 1;
+	long offset = 0;
+	int diag;
+
+	if (argc != 1)
+		return KDB_ARGCOUNT;
+	diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL, regs);
+	if (diag)
+		return diag;
+
+	xfsidbg_xchash((xfs_mount_t *) addr);
+	return 0;
+}
+
+static int	kdbm_xfs_xchashlist(
+	int	argc,
+	const char **argv,
+	const char **envp,
+	struct pt_regs *regs)
+{
+	unsigned long addr;
+	int nextarg = 1;
+	long offset = 0;
+	int diag;
+
+	if (argc != 1)
+		return KDB_ARGCOUNT;
+	diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL, regs);
+	if (diag)
+		return diag;
+
+	xfsidbg_xchashlist((xfs_chashlist_t *) addr);
+	return 0;
+}
+
+
+static int	kdbm_xfs_xdaargs(
+	int	argc,
+	const char **argv,
+	const char **envp,
+	struct pt_regs *regs)
+{
+	unsigned long addr;
+	int nextarg = 1;
+	long offset = 0;
+	int diag;
+
+	if (argc != 1)
+		return KDB_ARGCOUNT;
+	diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL, regs);
+	if (diag)
+		return diag;
+
+	xfsidbg_xdaargs((xfs_da_args_t *) addr);
+	return 0;
+}
+
+static int	kdbm_xfs_xdabuf(
+	int	argc,
+	const char **argv,
+	const char **envp,
+	struct pt_regs *regs)
+{
+	unsigned long addr;
+	int nextarg = 1;
+	long offset = 0;
+	int diag;
+
+	if (argc != 1)
+		return KDB_ARGCOUNT;
+	diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL, regs);
+	if (diag)
+		return diag;
+
+	xfsidbg_xdabuf((xfs_dabuf_t *) addr);
+	return 0;
+}
+
+static int	kdbm_xfs_xdanode(
+	int	argc,
+	const char **argv,
+	const char **envp,
+	struct pt_regs *regs)
+{
+	unsigned long addr;
+	int nextarg = 1;
+	long offset = 0;
+	int diag;
+
+	if (argc != 1)
+		return KDB_ARGCOUNT;
+	diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL, regs);
+	if (diag)
+		return diag;
+
+	xfsidbg_xdanode((xfs_da_intnode_t *) addr);
+	return 0;
+}
+
+static int	kdbm_xfs_xdastate(
+	int	argc,
+	const char **argv,
+	const char **envp,
+	struct pt_regs *regs)
+{
+	unsigned long addr;
+	int nextarg = 1;
+	long offset = 0;
+	int diag;
+
+	if (argc != 1)
+		return KDB_ARGCOUNT;
+	diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL, regs);
+	if (diag)
+		return diag;
+
+	xfsidbg_xdastate((xfs_da_state_t *) addr);
+	return 0;
+}
+
+static int	kdbm_xfs_xdirleaf(
+	int	argc,
+	const char **argv,
+	const char **envp,
+	struct pt_regs *regs)
+{
+	unsigned long addr;
+	int nextarg = 1;
+	long offset = 0;
+	int diag;
+
+	if (argc != 1)
+		return KDB_ARGCOUNT;
+	diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL, regs);
+	if (diag)
+		return diag;
+
+	xfsidbg_xdirleaf((xfs_dir_leafblock_t *) addr);
+	return 0;
+}
+
+static int	kdbm_xfs_xdirsf(
+	int	argc,
+	const char **argv,
+	const char **envp,
+	struct pt_regs *regs)
+{
+	unsigned long addr;
+	int nextarg = 1;
+	long offset = 0;
+	int diag;
+
+	if (argc != 1)
+		return KDB_ARGCOUNT;
+	diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL, regs);
+	if (diag)
+		return diag;
+
+	xfsidbg_xdirsf((xfs_dir_shortform_t *) addr);
+	return 0;
+}
+
+static int	kdbm_xfs_xdir2free(
+	int	argc,
+	const char **argv,
+	const char **envp,
+	struct pt_regs *regs)
+{
+	unsigned long addr;
+	int nextarg = 1;
+	long offset = 0;
+	int diag;
+
+	if (argc != 1)
+		return KDB_ARGCOUNT;
+	diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL, regs);
+	if (diag)
+		return diag;
+
+	xfsidbg_xdir2free((xfs_dir2_free_t *) addr);
+	return 0;
+}
+
+static int	kdbm_xfs_xdir2sf(
+	int	argc,
+	const char **argv,
+	const char **envp,
+	struct pt_regs *regs)
+{
+	unsigned long addr;
+	int nextarg = 1;
+	long offset = 0;
+	int diag;
+
+	if (argc != 1)
+		return KDB_ARGCOUNT;
+	diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL, regs);
+	if (diag)
+		return diag;
+
+	xfsidbg_xdir2sf((xfs_dir2_sf_t *) addr);
+	return 0;
+}
+
+static int	kdbm_xfs_xexlist(
+	int	argc,
+	const char **argv,
+	const char **envp,
+	struct pt_regs *regs)
+{
+	unsigned long addr;
+	int nextarg = 1;
+	long offset = 0;
+	int diag;
+
+	if (argc != 1)
+		return KDB_ARGCOUNT;
+	diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL, regs);
+	if (diag)
+		return diag;
+
+	xfsidbg_xexlist((xfs_inode_t *) addr);
+	return 0;
+}
+
+static int	kdbm_xfs_xflist(
+	int	argc,
+	const char **argv,
+	const char **envp,
+	struct pt_regs *regs)
+{
+	unsigned long addr;
+	int nextarg = 1;
+	long offset = 0;
+	int diag;
+
+	if (argc != 1)
+		return KDB_ARGCOUNT;
+	diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL, regs);
+	if (diag)
+		return diag;
+
+	xfsidbg_xflist((xfs_bmap_free_t *) addr);
+	return 0;
+}
+
+static int	kdbm_xfs_xhelp(
+	int	argc,
+	const char **argv,
+	const char **envp,
+	struct pt_regs *regs)
+{
+	if (argc != 0)
+		return KDB_ARGCOUNT;
+
+	xfsidbg_xhelp();
+	return 0;
+}
+
+static int	kdbm_xfs_xiclog(
+	int	argc,
+	const char **argv,
+	const char **envp,
+	struct pt_regs *regs)
+{
+	unsigned long addr;
+	int nextarg = 1;
+	long offset = 0;
+	int diag;
+
+	if (argc != 1)
+		return KDB_ARGCOUNT;
+	diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL, regs);
+	if (diag)
+		return diag;
+
+	xfsidbg_xiclog((xlog_in_core_t *) addr);
+	return 0;
+}
+
+static int	kdbm_xfs_xiclogall(
+	int	argc,
+	const char **argv,
+	const char **envp,
+	struct pt_regs *regs)
+{
+	unsigned long addr;
+	int nextarg = 1;
+	long offset = 0;
+	int diag;
+
+	if (argc != 1)
+		return KDB_ARGCOUNT;
+	diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL, regs);
+	if (diag)
+		return diag;
+
+	xfsidbg_xiclogall((xlog_in_core_t *) addr);
+	return 0;
+}
+
+static int	kdbm_xfs_xiclogcb(
+	int	argc,
+	const char **argv,
+	const char **envp,
+	struct pt_regs *regs)
+{
+	unsigned long addr;
+	int nextarg = 1;
+	long offset = 0;
+	int diag;
+
+	if (argc != 1)
+		return KDB_ARGCOUNT;
+	diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL, regs);
+	if (diag)
+		return diag;
+
+	xfsidbg_xiclogcb((xlog_in_core_t *) addr);
+	return 0;
+}
+
+static int	kdbm_xfs_xihash(
+	int	argc,
+	const char **argv,
+	const char **envp,
+	struct pt_regs *regs)
+{
+	unsigned long addr;
+	int nextarg = 1;
+	long offset = 0;
+	int diag;
+
+	if (argc != 1)
+		return KDB_ARGCOUNT;
+	diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL, regs);
+	if (diag)
+		return diag;
+
+	xfsidbg_xihash((xfs_mount_t *) addr);
+	return 0;
+}
+
+static int	kdbm_xfs_xinodes(
+	int	argc,
+	const char **argv,
+	const char **envp,
+	struct pt_regs *regs)
+{
+	unsigned long addr;
+	int nextarg = 1;
+	long offset = 0;
+	int diag;
+
+	if (argc != 1)
+		return KDB_ARGCOUNT;
+	diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL, regs);
+	if (diag)
+		return diag;
+
+	xfsidbg_xinodes((xfs_mount_t *) addr);
+	return 0;
+}
+
+static int	kdbm_xfs_delayed_blocks(
+	int	argc,
+	const char **argv,
+	const char **envp,
+	struct pt_regs *regs)
+{
+	unsigned long addr;
+	int nextarg = 1;
+	long offset = 0;
+	int diag;
+
+	if (argc != 1)
+		return KDB_ARGCOUNT;
+	diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL, regs);
+	if (diag)
+		return diag;
+
+	xfsidbg_delayed_blocks((xfs_mount_t *) addr);
+	return 0;
+}
+
+
+static int	kdbm_xfs_xinodes_quiesce(
+	int	argc,
+	const char **argv,
+	const char **envp,
+	struct pt_regs *regs)
+{
+	unsigned long addr;
+	int nextarg = 1;
+	long offset = 0;
+	int diag;
+
+	if (argc != 1)
+		return KDB_ARGCOUNT;
+	diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL, regs);
+	if (diag)
+		return diag;
+
+	xfsidbg_xinodes_quiesce((xfs_mount_t *) addr);
+	return 0;
+}
+
+static int	kdbm_xfs_xlog(
+	int	argc,
+	const char **argv,
+	const char **envp,
+	struct pt_regs *regs)
+{
+	unsigned long addr;
+	int nextarg = 1;
+	long offset = 0;
+	int diag;
+
+	if (argc != 1)
+		return KDB_ARGCOUNT;
+	diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL, regs);
+	if (diag)
+		return diag;
+
+	xfsidbg_xlog((xlog_t *) addr);
+	return 0;
+}
+
+static int	kdbm_xfs_xlog_ritem(
+	int	argc,
+	const char **argv,
+	const char **envp,
+	struct pt_regs *regs)
+{
+	unsigned long addr;
+	int nextarg = 1;
+	long offset = 0;
+	int diag;
+
+	if (argc != 1)
+		return KDB_ARGCOUNT;
+	diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL, regs);
+	if (diag)
+		return diag;
+
+	xfsidbg_xlog_ritem((xlog_recover_item_t *) addr);
+	return 0;
+}
+
+static int	kdbm_xfs_xlog_rtrans(
+	int	argc,
+	const char **argv,
+	const char **envp,
+	struct pt_regs *regs)
+{
+	unsigned long addr;
+	int nextarg = 1;
+	long offset = 0;
+	int diag;
+
+	if (argc != 1)
+		return KDB_ARGCOUNT;
+	diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL, regs);
+	if (diag)
+		return diag;
+
+	xfsidbg_xlog_rtrans((xlog_recover_t *) addr);
+	return 0;
+}
+
+static int	kdbm_xfs_xlog_rtrans_entire(
+	int	argc,
+	const char **argv,
+	const char **envp,
+	struct pt_regs *regs)
+{
+	unsigned long addr;
+	int nextarg = 1;
+	long offset = 0;
+	int diag;
+
+	if (argc != 1)
+		return KDB_ARGCOUNT;
+	diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL, regs);
+	if (diag)
+		return diag;
+
+	xfsidbg_xlog_rtrans_entire((xlog_recover_t *) addr);
+	return 0;
+}
+
+static int	kdbm_xfs_xlog_tic(
+	int	argc,
+	const char **argv,
+	const char **envp,
+	struct pt_regs *regs)
+{
+	unsigned long addr;
+	int nextarg = 1;
+	long offset = 0;
+	int diag;
+
+	if (argc != 1)
+		return KDB_ARGCOUNT;
+	diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL, regs);
+	if (diag)
+		return diag;
+
+	xfsidbg_xlog_tic((xlog_ticket_t *) addr);
+	return 0;
+}
+
+static int	kdbm_xfs_xlogitem(
+	int	argc,
+	const char **argv,
+	const char **envp,
+	struct pt_regs *regs)
+{
+	unsigned long addr;
+	int nextarg = 1;
+	long offset = 0;
+	int diag;
+
+	if (argc != 1)
+		return KDB_ARGCOUNT;
+	diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL, regs);
+	if (diag)
+		return diag;
+
+	xfsidbg_xlogitem((xfs_log_item_t *) addr);
+	return 0;
+}
+
+static int	kdbm_xfs_xmount(
+	int	argc,
+	const char **argv,
+	const char **envp,
+	struct pt_regs *regs)
+{
+	unsigned long addr;
+	int nextarg = 1;
+	long offset = 0;
+	int diag;
+
+	if (argc != 1)
+		return KDB_ARGCOUNT;
+	diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL, regs);
+	if (diag)
+		return diag;
+
+	xfsidbg_xmount((xfs_mount_t *) addr);
+	return 0;
+}
+
+static int	kdbm_xfs_xnode(
+	int	argc,
+	const char **argv,
+	const char **envp,
+	struct pt_regs *regs)
+{
+	unsigned long addr;
+	int nextarg = 1;
+	long offset = 0;
+	int diag;
+
+	if (argc != 1)
+		return KDB_ARGCOUNT;
+	diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL, regs);
+	if (diag)
+		return diag;
+
+	xfsidbg_xnode((xfs_inode_t *) addr);
+	return 0;
+}
+
+static int	kdbm_xfs_xcore(
+	int	argc,
+	const char **argv,
+	const char **envp,
+	struct pt_regs *regs)
+{
+	unsigned long addr;
+	int nextarg = 1;
+	long offset = 0;
+	int diag;
+
+	if (argc != 1)
+		return KDB_ARGCOUNT;
+	diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL, regs);
+	if (diag)
+		return diag;
+
+	xfsidbg_xcore((xfs_iocore_t *) addr);
+	return 0;
+}
+
+static int	kdbm_xfs_xperag(
+	int	argc,
+	const char **argv,
+	const char **envp,
+	struct pt_regs *regs)
+{
+	unsigned long addr;
+	int nextarg = 1;
+	long offset = 0;
+	int diag;
+
+	if (argc != 1)
+		return KDB_ARGCOUNT;
+	diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL, regs);
+	if (diag)
+		return diag;
+
+	xfsidbg_xperag((xfs_mount_t *) addr);
+	return 0;
+}
+
+static int	kdbm_xfs_xqm_diskdq(
+	int	argc,
+	const char **argv,
+	const char **envp,
+	struct pt_regs *regs)
+{
+	unsigned long addr;
+	int nextarg = 1;
+	long offset = 0;
+	int diag;
+
+	if (argc != 1)
+		return KDB_ARGCOUNT;
+	diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL, regs);
+	if (diag)
+		return diag;
+
+	xfsidbg_xqm_diskdq((xfs_disk_dquot_t *) addr);
+	return 0;
+}
+
+static int	kdbm_xfs_xqm_dqattached_inos(
+	int	argc,
+	const char **argv,
+	const char **envp,
+	struct pt_regs *regs)
+{
+	unsigned long addr;
+	int nextarg = 1;
+	long offset = 0;
+	int diag;
+
+	if (argc != 1)
+		return KDB_ARGCOUNT;
+	diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL, regs);
+	if (diag)
+		return diag;
+
+	xfsidbg_xqm_dqattached_inos((xfs_mount_t *) addr);
+	return 0;
+}
+
+static int	kdbm_xfs_xqm_dquot(
+	int	argc,
+	const char **argv,
+	const char **envp,
+	struct pt_regs *regs)
+{
+	unsigned long addr;
+	int nextarg = 1;
+	long offset = 0;
+	int diag;
+
+	if (argc != 1)
+		return KDB_ARGCOUNT;
+	diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL, regs);
+	if (diag)
+		return diag;
+
+	xfsidbg_xqm_dquot((xfs_dquot_t *) addr);
+	return 0;
+}
+
+#ifdef	CONFIG_XFS_QUOTA
+static int	kdbm_xfs_xqm(
+	int	argc,
+	const char **argv,
+	const char **envp,
+	struct pt_regs *regs)
+{
+	if (argc != 0)
+		return KDB_ARGCOUNT;
+
+	xfsidbg_xqm();
+	return 0;
+}
+
+static int	kdbm_xfs_xqm_freelist(
+	int	argc,
+	const char **argv,
+	const char **envp,
+	struct pt_regs *regs)
+{
+	if (argc != 0)
+		return KDB_ARGCOUNT;
+
+	xfsidbg_xqm_freelist();
+	return 0;
+}
+
+static int	kdbm_xfs_xqm_htab(
+	int	argc,
+	const char **argv,
+	const char **envp,
+	struct pt_regs *regs)
+{
+	if (argc != 0)
+		return KDB_ARGCOUNT;
+
+	xfsidbg_xqm_htab();
+	return 0;
+}
+#endif
+
+static int	kdbm_xfs_xqm_mplist(
+	int	argc,
+	const char **argv,
+	const char **envp,
+	struct pt_regs *regs)
+{
+	unsigned long addr;
+	int nextarg = 1;
+	long offset = 0;
+	int diag;
+
+	if (argc != 1)
+		return KDB_ARGCOUNT;
+	diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL, regs);
+	if (diag)
+		return diag;
+
+	xfsidbg_xqm_mplist((xfs_mount_t *) addr);
+	return 0;
+}
+
+static int	kdbm_xfs_xqm_qinfo(
+	int	argc,
+	const char **argv,
+	const char **envp,
+	struct pt_regs *regs)
+{
+	unsigned long addr;
+	int nextarg = 1;
+	long offset = 0;
+	int diag;
+
+	if (argc != 1)
+		return KDB_ARGCOUNT;
+	diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL, regs);
+	if (diag)
+		return diag;
+
+	xfsidbg_xqm_qinfo((xfs_mount_t *) addr);
+	return 0;
+}
+
+static int	kdbm_xfs_xqm_tpdqinfo(
+	int	argc,
+	const char **argv,
+	const char **envp,
+	struct pt_regs *regs)
+{
+	unsigned long addr;
+	int nextarg = 1;
+	long offset = 0;
+	int diag;
+
+	if (argc != 1)
+		return KDB_ARGCOUNT;
+	diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL, regs);
+	if (diag)
+		return diag;
+
+	xfsidbg_xqm_tpdqinfo((xfs_trans_t *) addr);
+	return 0;
+}
+
+static int	kdbm_xfs_xsb(
+	int	argc,
+	const char **argv,
+	const char **envp,
+	struct pt_regs *regs)
+{
+	unsigned long addr;
+	unsigned long convert=0;
+	int nextarg = 1;
+	long offset = 0;
+	int diag;
+
+	if (argc != 1 && argc!=2)
+		return KDB_ARGCOUNT;
+	diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL, regs);
+	if (diag)
+		return diag;
+	if (argc==2) {
+	    /* extra argument - conversion flag */
+	    diag = kdbgetaddrarg(argc, argv, &nextarg, &convert, &offset, NULL, regs);
+	    if (diag)
+		    return diag;
+	}
+
+	xfsidbg_xsb((xfs_sb_t *) addr, (int)convert);
+	return 0;
+}
+
+static int	kdbm_xfs_xtp(
+	int	argc,
+	const char **argv,
+	const char **envp,
+	struct pt_regs *regs)
+{
+	unsigned long addr;
+	int nextarg = 1;
+	long offset = 0;
+	int diag;
+
+	if (argc != 1)
+		return KDB_ARGCOUNT;
+	diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL, regs);
+	if (diag)
+		return diag;
+
+	xfsidbg_xtp((xfs_trans_t *) addr);
+	return 0;
+}
+
+static int	kdbm_xfs_xtrans_res(
+	int	argc,
+	const char **argv,
+	const char **envp,
+	struct pt_regs *regs)
+{
+	unsigned long addr;
+	int nextarg = 1;
+	long offset = 0;
+	int diag;
+
+	if (argc != 1)
+		return KDB_ARGCOUNT;
+	diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL, regs);
+	if (diag)
+		return diag;
+
+	xfsidbg_xtrans_res((xfs_mount_t *) addr);
+	return 0;
+}
+
+/*
+ * Vnode descriptor dump.
+ * This table is a string version of all the flags defined in vnode.h.
+ */
+char *tab_vflags[] = {
+	/* local only flags */
+	"VINACT",		/*	 0x01 */
+	"VRECLM",		/*	 0x02 */
+	"VWAIT",		/*	 0x04 */
+	"VMODIFIED",		/*	 0x08 */
+	"INVALID0x10",		/*	 0x10 */
+	"INVALID0x20",		/*	 0x20 */
+	"INVALID0x40",		/*	 0x40 */
+	"INVALID0x80",		/*	 0x80 */
+	"INVALID0x100",		/*	0x100 */
+	"INVALID0x200",		/*	0x200 */
+	"INVALID0x400",		/*	0x400 */
+	"INVALID0x800",		/*	0x800 */
+	"INVALID0x1000",	/*     0x1000 */
+	"INVALID0x2000",	/*     0x2000 */
+	"INVALID0x4000",	/*     0x4000 */
+	"INVALID0x8000",	/*     0x8000 */
+	"INVALID0x10000",	/*    0x10000 */
+	"INVALID0x20000",	/*    0x20000 */
+	"INVALID0x40000",	/*    0x40000 */
+	"INVALID0x80000",	/*    0x80000 */
+	"VROOT",		/*   0x100000 */
+	"VNOSWAP",		/*   0x200000 */
+	"VISSWAP",		/*   0x400000 */
+	"VREPLICABLE",		/*   0x800000 */
+	"VNOTREPLICABLE",	/*  0x1000000 */
+	"VDOCMP",		/*  0x2000000 */
+	"VSHARE",		/*  0x4000000 */
+	"VFRLOCKS",		/*  0x8000000 */
+	"VENF_LOCKING",		/* 0x10000000 */
+	"VOPLOCK",		/* 0x20000000 */
+	"VPURGE",		/* 0x40000000 */
+	"INVALID0x80000000",	/* 0x80000000 */
+	0
+};
+
+
+static char *vnode_type[] = {
+	"VNON", "VREG", "VDIR", "VBLK", "VLNK", "VFIFO", "VBAD", "VSOCK"
+};
+
+static void
+printflags(register uint64_t flags,
+	register char **strings,
+	register char *name)
+{
+	register uint64_t mask = 1;
+
+	if (name)
+		kdb_printf("%s 0x%llx <", name, (unsigned long long)flags);
+
+	while (flags != 0 && *strings) {
+		if (mask & flags) {
+			kdb_printf("%s ", *strings);
+			flags &= ~mask;
+		}
+		mask <<= 1;
+		strings++;
+	}
+
+	if (name)
+		kdb_printf("> ");
+
+	return;
+}
+
+
+static void	printvnode(vnode_t *vp)
+{
+	bhv_desc_t	*bh;
+	kdb_symtab_t	 symtab;
+
+
+	kdb_printf("vnode: 0x%p type ", vp);
+	if ((size_t)vp->v_type >= sizeof(vnode_type)/sizeof(vnode_type[0]))
+		kdb_printf("out of range 0x%x\n", vp->v_type);
+	else
+		kdb_printf("%s\n", vnode_type[vp->v_type]);
+
+	if ((bh = vp->v_bh.bh_first)) {
+		kdb_printf("   v_inode 0x%p v_bh->bh_first 0x%p pobj 0x%p\n",
+					LINVFS_GET_IP(vp), bh, bh->bd_pdata);
+
+		if (kdbnearsym((unsigned long)bh->bd_ops, &symtab))
+			kdb_printf("   ops %s ", symtab.sym_name);
+		else
+			kdb_printf("   ops %s/0x%p ",
+						"???", (void *)bh->bd_ops);
+	} else {
+		kdb_printf("   v_inode 0x%p v_bh->bh_first = NULLBHV ",
+					LINVFS_GET_IP(vp));
+	}
+
+	printflags((__psunsigned_t)vp->v_flag, tab_vflags, "flag =");
+	kdb_printf("\n");
+
+#ifdef	CONFIG_XFS_VNODE_TRACING
+	kdb_printf("   v_trace 0x%p\n", vp->v_trace);
+#endif	/* CONFIG_XFS_VNODE_TRACING */
+}
+
+
+static int	kdbm_vnode(
+	int	argc,
+	const char **argv,
+	const char **envp,
+	struct pt_regs *regs)
+{
+	unsigned long addr;
+	int nextarg = 1;
+	long offset = 0;
+	int diag;
+	vnode_t		*vp;
+/*	bhv_desc_t	*bh; */
+/*	kdb_symtab_t	 symtab;*/
+
+	if (argc != 1)
+		return KDB_ARGCOUNT;
+
+	diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL, regs);
+
+	if (diag)
+		return diag;
+
+	vp = (vnode_t *)addr;
+
+	printvnode(vp);
+
+	return 0;
+}
+
+#ifdef	CONFIG_XFS_VNODE_TRACING
+/*
+ * Print a vnode trace entry.
+ */
+static int
+vn_trace_pr_entry(ktrace_entry_t *ktep)
+{
+	char		funcname[128];
+	kdb_symtab_t	symtab;
+
+
+	if ((__psint_t)ktep->val[0] == 0)
+		return 0;
+
+	if (kdbnearsym((unsigned int)ktep->val[8], &symtab)) {
+		unsigned long offval;
+
+		offval = (unsigned int)ktep->val[8] - symtab.sym_start;
+
+		if (offval)
+			sprintf(funcname, "%s+0x%lx", symtab.sym_name, offval);
+		else
+			sprintf(funcname, "%s", symtab.sym_name);
+	} else
+		funcname[0] = '\0';
+
+
+	switch ((__psint_t)ktep->val[0]) {
+	case VNODE_KTRACE_ENTRY:
+		kdb_printf("entry to %s i_count = %d",
+						(char *)ktep->val[1],
+						(__psint_t)ktep->val[3]);
+		break;
+
+	case VNODE_KTRACE_EXIT:
+		kdb_printf("exit from %s i_count = %d",
+						(char *)ktep->val[1],
+						(__psint_t)ktep->val[3]);
+		break;
+
+	case VNODE_KTRACE_HOLD:
+		if ((__psint_t)ktep->val[3] != 1)
+			kdb_printf("hold @%s:%d(%s) i_count %d => %d ",
+						(char *)ktep->val[1],
+						(__psint_t)ktep->val[2],
+						funcname,
+						(__psint_t)ktep->val[3] - 1,
+						(__psint_t)ktep->val[3]);
+		else
+			kdb_printf("get @%s:%d(%s) i_count = %d",
+						(char *)ktep->val[1],
+						(__psint_t)ktep->val[2],
+						funcname,
+						(__psint_t)ktep->val[3]);
+		break;
+
+	case VNODE_KTRACE_REF:
+		kdb_printf("ref @%s:%d(%s) i_count = %d",
+						(char *)ktep->val[1],
+						(__psint_t)ktep->val[2],
+						funcname,
+						(__psint_t)ktep->val[3]);
+		break;
+
+	case VNODE_KTRACE_RELE:
+		if ((__psint_t)ktep->val[3] != 1)
+			kdb_printf("rele @%s:%d(%s) i_count %d => %d ",
+						(char *)ktep->val[1],
+						(__psint_t)ktep->val[2],
+						funcname,
+						(__psint_t)ktep->val[3],
+						(__psint_t)ktep->val[3] - 1);
+		else
+			kdb_printf("free @%s:%d(%s) i_count = %d",
+						(char *)ktep->val[1],
+						(__psint_t)ktep->val[2],
+						funcname,
+						(__psint_t)ktep->val[3]);
+		break;
+
+	default:
+		kdb_printf("unknown vntrace record\n");
+		return 1;
+	}
+
+	kdb_printf("\n");
+
+	kdb_printf("  cpu = %d pid = %d ",
+			(__psint_t)ktep->val[6], (pid_t)ktep->val[7]);
+
+	printflags((__psunsigned_t)ktep->val[5], tab_vflags, "flag =");
+
+	if (kdbnearsym((unsigned int)ktep->val[4], &symtab)) {
+		unsigned long offval;
+
+		offval = (unsigned int)ktep->val[4] - symtab.sym_start;
+
+		if (offval)
+			kdb_printf("  ra = %s+0x%lx", symtab.sym_name, offval);
+		else
+			kdb_printf("  ra = %s", symtab.sym_name);
+	} else
+		kdb_printf("  ra = ?? 0x%p", (void *)ktep->val[4]);
+
+	return 1;
+}
+
+
+/*
+ * Print out the trace buffer attached to the given vnode.
+ */
+static int	kdbm_vntrace(
+	int	argc,
+	const char **argv,
+	const char **envp,
+	struct pt_regs *regs)
+{
+	int		diag;
+	int		nextarg = 1;
+	long		offset = 0;
+	unsigned long	addr;
+	vnode_t		*vp;
+	ktrace_entry_t	*ktep;
+	ktrace_snap_t	kts;
+
+
+	if (argc != 1)
+		return KDB_ARGCOUNT;
+
+	diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL, regs);
+
+	if (diag)
+		return diag;
+
+	vp = (vnode_t *)addr;
+
+	if (vp->v_trace == NULL) {
+		kdb_printf("The vnode trace buffer is not initialized\n");
+
+		return 0;
+	}
+
+	kdb_printf("vntrace vp 0x%p\n", vp);
+
+	ktep = ktrace_first(vp->v_trace, &kts);
+
+	while (ktep != NULL) {
+		if (vn_trace_pr_entry(ktep))
+			kdb_printf("\n");
+
+		ktep = ktrace_next(vp->v_trace, &kts);
+	}
+
+	return 0;
+}
+/*
+ * Print out the trace buffer attached to the given vnode.
+ */
+static int	kdbm_vntraceaddr(
+	int	argc,
+	const char **argv,
+	const char **envp,
+	struct pt_regs *regs)
+{
+	int		diag;
+	int		nextarg = 1;
+	long		offset = 0;
+	unsigned long	addr;
+	struct ktrace	*kt;
+	ktrace_entry_t	*ktep;
+	ktrace_snap_t	kts;
+
+
+	if (argc != 1)
+		return KDB_ARGCOUNT;
+
+	diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL, regs);
+
+	if (diag)
+		return diag;
+
+	kt = (struct ktrace *)addr;
+
+	kdb_printf("vntraceaddr kt 0x%p\n", kt);
+
+	ktep = ktrace_first(kt, &kts);
+
+	while (ktep != NULL) {
+		if (vn_trace_pr_entry(ktep))
+			kdb_printf("\n");
+
+		ktep = ktrace_next(kt, &kts);
+	}
+
+	return 0;
+}
+#endif	/* CONFIG_XFS_VNODE_TRACING */
+
+
+static void	printinode(struct inode *ip)
+{
+	unsigned long	addr;
+
+
+	if (ip == NULL)
+		return;
+
+	kdb_printf(" i_ino = %lu i_count = %u i_dev = 0x%x i_size %Ld\n",
+					ip->i_ino, atomic_read(&ip->i_count),
+					ip->i_dev, ip->i_size);
+
+	kdb_printf(
+		" i_mode = 0x%x	 i_nlink = %d  i_rdev = 0x%x i_state = 0x%lx\n",
+					ip->i_mode, ip->i_nlink,
+					kdev_t_to_nr(ip->i_rdev), ip->i_state);
+
+	kdb_printf(" i_hash.nxt = 0x%p i_hash.prv = 0x%p\n",
+					ip->i_hash.next, ip->i_hash.prev);
+	kdb_printf(" i_list.nxt = 0x%p i_list.prv = 0x%p\n",
+					ip->i_list.next, ip->i_list.prev);
+	kdb_printf(" i_dentry.nxt = 0x%p i_dentry.prv = 0x%p\n",
+					ip->i_dentry.next,
+					ip->i_dentry.prev);
+
+	addr = (unsigned long)ip;
+
+	kdb_printf(" i_sb = 0x%p i_op = 0x%p i_data = 0x%lx nrpages = %lu\n",
+					ip->i_sb, ip->i_op,
+					addr + offsetof(struct inode, i_data),
+					ip->i_data.nrpages);
+
+	kdb_printf("  vnode ptr 0x%p\n", LINVFS_GET_VP(ip));
+}
+
+
+static int	kdbm_vn(
+	int	argc,
+	const char **argv,
+	const char **envp,
+	struct pt_regs *regs)
+{
+	int		diag;
+	int		nextarg = 1;
+/*	char		*symname; */
+	long		offset = 0;
+	unsigned long	addr;
+	struct inode	*ip;
+/*	bhv_desc_t	*bh; */
+#ifdef	CONFIG_XFS_VNODE_TRACING
+	ktrace_entry_t	*ktep;
+	ktrace_snap_t	kts;
+#endif
+	vnode_t		*vp;
+
+	if (argc != 1)
+		return KDB_ARGCOUNT;
+
+	diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL, regs);
+
+	if (diag)
+		return diag;
+
+	vp = (vnode_t *)addr;
+
+	ip = LINVFS_GET_IP(vp);
+
+	kdb_printf("--> Inode @ 0x%p\n", ip);
+	printinode(ip);
+
+	kdb_printf("--> Vnode @ 0x%p\n", vp);
+	printvnode(vp);
+
+#ifdef	CONFIG_XFS_VNODE_TRACING
+
+	kdb_printf("--> Vntrace @ 0x%p/0x%p\n", vp, vp->v_trace);
+
+	if (vp->v_trace == NULL)
+		return 0;
+
+	ktep = ktrace_first(vp->v_trace, &kts);
+
+	while (ktep != NULL) {
+		if (vn_trace_pr_entry(ktep))
+			kdb_printf("\n");
+
+		ktep = ktrace_next(vp->v_trace, &kts);
+	}
+#endif	/* CONFIG_XFS_VNODE_TRACING */
+
+	return 0;
+}
+
+
+/* pagebuf stuff */
+
+static char	*pb_flag_vals[] = {
+	"READ", "WRITE", "MAPPED", "PARTIAL",
+	"ASYNC", "NONE", "DELWRI", "FREED", "SYNC",
+	"MAPPABLE", "STALE", "FS_MANAGED", "RELEASE",
+	"LOCK", "TRYLOCK", "ALLOCATE", "FILE_ALLOCATE", "DONT_BLOCK",
+	"DIRECT", "LOCKABLE", "NEXT_KEY", "ENTER_PAGES",
+	"ALL_PAGES_MAPPED", "SOME_INVALID_PAGES", "ADDR_ALLOCATED",
+	"MEM_ALLOCATED", "GRIO", "FORCEIO", "SHUTDOWN",
+	NULL };
+
+static char	*pbm_flag_vals[] = {
+	"EOF", "HOLE", "DELAY", "FLUSH_OVERLAPS",
+	"READAHEAD", "UNWRITTEN", "DONTALLOC", "NEW",
+	NULL };
+
+
+static char	*map_flags(unsigned long flags, char *mapping[])
+{
+	static	char	buffer[256];
+	int	index;
+	int	offset = 12;
+
+	buffer[0] = '\0';
+
+	for (index = 0; flags && mapping[index]; flags >>= 1, index++) {
+		if (flags & 1) {
+			if ((offset + strlen(mapping[index]) + 1) >= 80) {
+				strcat(buffer, "\n	      ");
+				offset = 12;
+			} else if (offset > 12) {
+				strcat(buffer, " ");
+				offset++;
+			}
+			strcat(buffer, mapping[index]);
+			offset += strlen(mapping[index]);
+		}
+	}
+
+	return (buffer);
+}
+
+static char	*pb_flags(page_buf_flags_t pb_flag)
+{
+	return(map_flags((unsigned long) pb_flag, pb_flag_vals));
+}
+
+static int
+kdbm_pb_flags(int argc, const char **argv, const char **envp, struct pt_regs *regs)
+{
+	unsigned long flags;
+	int diag;
+
+	if (argc != 1)
+		return KDB_ARGCOUNT;
+
+	diag = kdbgetularg(argv[1], &flags);
+	if (diag)
+		return diag;
+
+	kdb_printf("pb flags 0x%lx = %s\n", flags, pb_flags(flags));
+
+	return 0;
+}
+
+static int
+kdbm_pb(int argc, const char **argv, const char **envp, struct pt_regs *regs)
+{
+	page_buf_private_t bp;
+	unsigned long addr;
+	long	offset=0;
+	int nextarg;
+	int diag;
+
+	if (argc != 1)
+		return KDB_ARGCOUNT;
+
+	nextarg = 1;
+	if ((diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL, regs)) ||
+	    (diag = kdb_getarea(bp, addr)))
+		return diag;
+
+	kdb_printf("page_buf_t at 0x%lx\n", addr);
+	kdb_printf("  pb_flags %s\n", pb_flags(bp.pb_common.pb_flags));
+	kdb_printf("  pb_target 0x%p pb_hold %d pb_next 0x%p pb_prev 0x%p\n",
+		   bp.pb_common.pb_target, bp.pb_common.pb_hold.counter,
+		   bp.pb_common.pb_list.next, bp.pb_common.pb_list.prev);
+	kdb_printf("  pb_hash_index %d pb_hash_next 0x%p pb_hash_prev 0x%p\n",
+		   bp.pb_common.pb_hash_index,
+		   bp.pb_common.pb_hash_list.next,
+		   bp.pb_common.pb_hash_list.prev);
+	kdb_printf("  pb_file_offset 0x%llx pb_buffer_length 0x%llx pb_addr 0x%p\n",
+		   (unsigned long long) bp.pb_common.pb_file_offset,
+		   (unsigned long long) bp.pb_common.pb_buffer_length,
+		   bp.pb_common.pb_addr);
+	kdb_printf("  pb_bn 0x%Lx pb_count_desired 0x%lx\n",
+		   bp.pb_common.pb_bn,
+		   (unsigned long) bp.pb_common.pb_count_desired);
+	kdb_printf("  pb_io_remaining %d pb_error %u\n",
+		   bp.pb_io_remaining.counter, bp.pb_common.pb_error);
+	kdb_printf("  pb_page_count %u pb_offset 0x%x pb_pages 0x%p\n",
+		bp.pb_common.pb_page_count, bp.pb_common.pb_offset,
+		bp.pb_common.pb_pages);
+#ifdef PAGEBUF_LOCK_TRACKING
+	kdb_printf("  pb_iodonesema (%d,%d) pb_sema (%d,%d) pincount (%d) last holder %d\n",
+		   bp.pb_common.pb_iodonesema.count.counter,
+		   bp.pb_common.pb_iodonesema.sleepers,
+		   bp.pb_sema.count.counter, bp.pb_sema.sleepers,
+		   bp.pb_pin_count.counter, bp.pb_last_holder);
+#else
+	kdb_printf("  pb_iodonesema (%d,%d) pb_sema (%d,%d) pincount (%d)\n",
+		   bp.pb_common.pb_iodonesema.count.counter,
+		   bp.pb_common.pb_iodonesema.sleepers,
+		   bp.pb_sema.count.counter, bp.pb_sema.sleepers,
+		   bp.pb_pin_count.counter);
+#endif
+	if (bp.pb_common.pb_fspriv || bp.pb_common.pb_fspriv2) {
+		kdb_printf(  "pb_fspriv 0x%p pb_fspriv2 0x%p\n",
+			   bp.pb_common.pb_fspriv, bp.pb_common.pb_fspriv2);
+	}
+
+	return 0;
+}
+
+/* XXXXXXXXXXXXXXXXXXXXXX */
+/* The start of this deliberately looks like a read_descriptor_t in layout */
+typedef struct {
+	read_descriptor_t io_rdesc;
+
+	/* 0x10 */
+	page_buf_rw_t io_dir;	/* read or write */
+	loff_t io_offset;	/* Starting offset of I/O */
+	int io_iovec_nr;	/* Number of entries in iovec */
+
+	/* 0x20 */
+	struct iovec **io_iovec;	/* iovec list indexed by iovec_index */
+	loff_t io_iovec_offset; /* offset into current iovec. */
+	int io_iovec_index;	/* current iovec being processed */
+	unsigned int io_sshift; /* sector bit shift */
+	loff_t io_i_size;	/* size of the file */
+} pb_io_desc_t;
+
+static int
+kdbm_pbiodesc(int argc, const char **argv, const char **envp,
+	struct pt_regs *regs)
+{
+	pb_io_desc_t	pbio;
+	unsigned long addr;
+	long	offset=0;
+	int nextarg;
+	int diag;
+
+	if (argc != 1)
+		return KDB_ARGCOUNT;
+
+	nextarg = 1;
+	if ((diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL, regs)) ||
+	    (diag = kdb_getarea(pbio, addr)))
+
+	kdb_printf("pb_io_desc_t at 0x%lx\n", addr);
+	kdb_printf("  io_rdesc [ written 0x%lx count 0x%lx buf 0x%p error %d ]\n",
+			(unsigned long) pbio.io_rdesc.written,
+			(unsigned long) pbio.io_rdesc.count,
+			pbio.io_rdesc.buf, pbio.io_rdesc.error);
+
+	kdb_printf("  io_dir %d io_offset 0x%Lx io_iovec_nr 0x%d\n",
+			pbio.io_dir, pbio.io_offset, pbio.io_iovec_nr);
+
+	kdb_printf("  io_iovec 0x%p io_iovec_offset 0x%Lx io_iovec_index 0x%d\n",
+		pbio.io_iovec, pbio.io_iovec_offset, pbio.io_iovec_index);
+
+	kdb_printf("  io_sshift 0x%d io_i_size 0x%Lx\n",
+		pbio.io_sshift, pbio.io_i_size);
+
+	return 0;
+}
+
+static int
+kdbm_pbmap(int argc, const char **argv, const char **envp,
+	struct pt_regs *regs)
+{
+	page_buf_bmap_t pbm;
+	unsigned long addr;
+	long	offset=0;
+	int nextarg;
+	int diag;
+
+	if (argc != 1)
+		return KDB_ARGCOUNT;
+
+	nextarg = 1;
+	if ((diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL, regs)) ||
+	    (diag = kdb_getarea(pbm, addr)))
+
+	kdb_printf("page_buf_bmap_t at 0x%lx\n", addr);
+	kdb_printf("  pbm_bn 0x%llx pbm_offset 0x%Lx pbm_delta 0x%lx pbm_bsize 0x%lx\n",
+		(long long) pbm.pbm_bn, pbm.pbm_offset,
+		(unsigned long) pbm.pbm_delta, (unsigned long) pbm.pbm_bsize);
+
+	kdb_printf("  pbm_flags %s\n", map_flags(pbm.pbm_flags, pbm_flag_vals));
+
+	return 0;
+}
+
+#ifdef PAGEBUF_TRACE
+# ifdef __PAGEBUF_TRACE__
+# undef __PAGEBUF_TRACE__
+# undef PB_DEFINE_TRACES
+# undef PB_TRACE_START
+# undef PB_TRACE_REC
+# undef PB_TRACE_END
+# endif
+#include "pagebuf/page_buf_trace.h"
+
+#define EV_SIZE (sizeof(event_names)/sizeof(char *))
+
+void
+pb_trace_core(
+	unsigned long match,
+	char	*event_match,
+	unsigned long long offset,
+	long long mask)
+{
+	extern struct pagebuf_trace_buf pb_trace;
+	int i, total, end;
+	pagebuf_trace_t *trace;
+	char	*event;
+	char	value[10];
+
+	end = pb_trace.start - 1;
+	if (end < 0)
+		end = PB_TRACE_BUFSIZE - 1;
+
+	if (match && (match < PB_TRACE_BUFSIZE)) {
+		for (i = pb_trace.start, total = 0; i != end; i = CIRC_INC(i)) {
+			trace = &pb_trace.buf[i];
+			if (trace->pb == 0)
+				continue;
+			total++;
+		}
+		total = total - match;
+		for (i = pb_trace.start; i != end && total; i = CIRC_INC(i)) {
+			trace = &pb_trace.buf[i];
+			if (trace->pb == 0)
+				continue;
+			total--;
+		}
+		match = 0;
+	} else
+		i = pb_trace.start;
+	for ( ; i != end; i = CIRC_INC(i)) {
+		trace = &pb_trace.buf[i];
+
+		if (offset) {
+			if ((trace->offset & ~mask) != offset)
+				continue;
+		}
+
+		if (trace->pb == 0)
+			continue;
+
+		if ((match != 0) && (trace->pb != match))
+			continue;
+
+		if ((trace->event < EV_SIZE-1) && event_names[trace->event]) {
+			event = event_names[trace->event];
+		} else if (trace->event == EV_SIZE) {
+			event = (char *)trace->misc;
+		} else {
+			event = value;
+			sprintf(value, "%8d", trace->event);
+		}
+
+		if (event_match && strcmp(event, event_match)) {
+			continue;
+		}
+
+
+		kdb_printf("pb 0x%lx [%s] (hold %u lock %d) misc 0x%p",
+			   trace->pb, event,
+			   trace->hold, trace->lock_value,
+			   trace->misc);
+		kdb_symbol_print((unsigned int)trace->ra, NULL,
+			KDB_SP_SPACEB|KDB_SP_PAREN|KDB_SP_NEWLINE);
+		kdb_printf("	offset 0x%Lx size 0x%x task 0x%p\n",
+			   trace->offset, trace->size, trace->task);
+		kdb_printf("	flags: %s\n",
+			   pb_flags(trace->flags));
+	}
+}
+
+
+static int
+kdbm_pbtrace_offset(int argc, const char **argv, const char **envp,
+	struct pt_regs *regs)
+{
+	long mask = 0;
+	unsigned long offset = 0;
+	int diag;
+
+	if (argc > 2)
+		return KDB_ARGCOUNT;
+
+	if (argc > 0) {
+		diag = kdbgetularg(argv[1], &offset);
+		if (diag)
+			return diag;
+	}
+
+	if (argc > 1) {
+		diag = kdbgetularg(argv[1], &mask);
+		if (diag)
+			return diag;
+	}
+
+	pb_trace_core(0, NULL, (unsigned long long)offset,
+			       (long long)mask);	/* sign extent mask */
+	return 0;
+}
+
+static int
+kdbm_pbtrace(int argc, const char **argv, const char **envp,
+	struct pt_regs *regs)
+{
+	unsigned long addr = 0;
+	int diag, nextarg;
+	long offset = 0;
+	char	*event_match = NULL;
+
+	if (argc > 1)
+		return KDB_ARGCOUNT;
+
+	if (argc == 1) {
+		if (isupper(argv[1][0]) || islower(argv[1][0])) {
+			event_match = (char *)argv[1];
+			printk("event match on \"%s\"\n", event_match);
+			argc = 0;
+		} else {
+			nextarg = 1;
+			diag = kdbgetaddrarg(argc, argv, &nextarg, &addr, &offset, NULL, regs);
+			if (diag) {
+				printk("failed to parse %s as a number\n",
+								argv[1]);
+				return diag;
+			}
+		}
+	}
+
+	pb_trace_core(addr, event_match, 0LL, 0LL);
+	return 0;
+}
+
+#else	/* PAGEBUF_TRACE */
+static int
+kdbm_pbtrace(int argc, const char **argv, const char **envp,
+	struct pt_regs *regs)
+{
+	kdb_printf("pagebuf tracing not compiled in\n");
+
+	return 0;
+}
+#endif	/* PAGEBUF_TRACE */
+
+static struct xif {
+	char	*name;
+	int	(*func)(int, const char **, const char **, struct pt_regs *);
+	char	*args;
+	char	*help;
+} xfsidbg_funcs[] = {
+  {  "vn",	kdbm_vn,	"<vnode>", "Dump inode/vnode/trace"},
+  {  "vnode",	kdbm_vnode,	"<vnode>", "Dump vnode"},
+#ifdef	CONFIG_XFS_VNODE_TRACING
+  {  "vntrace", kdbm_vntrace,	"<vntrace>", "Dump vnode Trace"},
+  {  "vntraceaddr",	kdbm_vntraceaddr, "<vntrace>", "Dump vnode Trace by Address"},
+#endif	/* CONFIG_XFS_VNODE_TRACING */
+  {  "xagf",	kdbm_xfs_xagf,	"<agf>",
+				"Dump XFS allocation group freespace" },
+  {  "xagi",	kdbm_xfs_xagi,	"<agi>",
+				"Dump XFS allocation group inode" },
+  {  "xail",	kdbm_xfs_xaildump,	"<xfs_mount_t>",
+				"Dump XFS AIL for a mountpoint" },
+  {  "xalloc",	kdbm_xfs_xalloc,	"<xfs_alloc_arg_t>",
+				"Dump XFS allocation args structure" },
+  {  "xattrcx", kdbm_xfs_xattrcontext,	"<xfs_attr_list_context_t>",
+				"Dump XFS attr_list context struct"},
+  {  "xattrlf", kdbm_xfs_xattrleaf,	"<xfs_attr_leafblock_t>",
+				"Dump XFS attribute leaf block"},
+  {  "xattrsf", kdbm_xfs_xattrsf,	"<xfs_attr_shortform_t>",
+				"Dump XFS attribute shortform"},
+  {  "xbirec",	kdbm_xfs_xbirec,	"<xfs_bmbt_irec_t",
+				"Dump XFS bmap incore record"},
+  {  "xbmalla", kdbm_xfs_xbmalla,	"<xfs_bmalloca_t>",
+				"Dump XFS bmalloc args structure"},
+  {  "xbrec",	kdbm_xfs_xbrec,		"<xfs_bmbt_rec_64_t",
+				"Dump XFS bmap record"},
+  {  "xbroot",	kdbm_xfs_xbroot,	"<xfs_inode_t>",
+				"Dump XFS bmap btree root (data)"},
+  {  "xbroota", kdbm_xfs_xbroota,	"<xfs_inode_t>",
+				"Dump XFS bmap btree root (attr)"},
+  {  "xbtcur",	kdbm_xfs_xbtcur,	"<xfs_btree_cur_t>",
+				"Dump XFS btree cursor"},
+  {  "xbuf",	kdbm_xfs_xbuf,		"<xfs_buf_t>",
+				"Dump XFS data from a buffer"},
+  {  "xchash",	kdbm_xfs_xchash,	"<xfs_mount_t>",
+				"Dump XFS cluster hash"},
+  {  "xchlist", kdbm_xfs_xchashlist,	"<xfs_chashlist_t>",
+				"Dump XFS cluster hash list"},
+  {  "xd2free", kdbm_xfs_xdir2free,	"<xfs_dir2_free_t>",
+				"Dump XFS directory v2 freemap"},
+  {  "xdaargs", kdbm_xfs_xdaargs,	"<xfs_da_args_t>",
+				"Dump XFS dir/attr args structure"},
+  {  "xdabuf",	kdbm_xfs_xdabuf,	"<xfs_dabuf_t>",
+				"Dump XFS dir/attr buf structure"},
+  {  "xdanode", kdbm_xfs_xdanode,	"<xfs_da_intnode_t>",
+				"Dump XFS dir/attr node block"},
+  {  "xdastat", kdbm_xfs_xdastate,	"<xfs_da_state_t>",
+				"Dump XFS dir/attr state_blk struct"},
+  {  "xdelay",	kdbm_xfs_delayed_blocks,	"<xfs_mount_t>",
+				"Dump delayed block totals"},
+  {  "xdirlf",	kdbm_xfs_xdirleaf,	"<xfs_dir_leafblock_t>",
+				"Dump XFS directory leaf block"},
+  {  "xdirsf",	kdbm_xfs_xdirsf,	"<xfs_dir_shortform_t>",
+				"Dump XFS directory shortform"},
+  {  "xdir2sf", kdbm_xfs_xdir2sf,	"<xfs_dir2_sf_t>",
+				"Dump XFS directory v2 shortform"},
+  {  "xdiskdq", kdbm_xfs_xqm_diskdq,	"<xfs_disk_dquot_t>",
+				"Dump XFS ondisk dquot (quota) struct"},
+  {  "xdqatt",	kdbm_xfs_xqm_dqattached_inos,	"<xfs_mount_t>",
+				 "All incore inodes with dquots"},
+  {  "xdqinfo", kdbm_xfs_xqm_tpdqinfo,	"<xfs_trans_t>",
+				"Dump dqinfo structure of a trans"},
+  {  "xdquot",	kdbm_xfs_xqm_dquot,	"<xfs_dquot_t>",
+				"Dump XFS dquot (quota) structure"},
+  {  "xexlist", kdbm_xfs_xexlist,	"<xfs_inode_t>",
+				"Dump XFS bmap extents in inode"},
+  {  "xflist",	kdbm_xfs_xflist,	"<xfs_bmap_free_t>",
+				"Dump XFS to-be-freed extent list"},
+  {  "xhelp",	kdbm_xfs_xhelp,		"",
+				"Print idbg-xfs help"},
+  {  "xicall",	kdbm_xfs_xiclogall,	"<xlog_in_core_t>",
+				"Dump All XFS in-core logs"},
+  {  "xiclog",	kdbm_xfs_xiclog,	"<xlog_in_core_t>",
+				"Dump XFS in-core log"},
+  {  "xihash",	kdbm_xfs_xihash,	"<xfs_mount_t>",
+				"Dump XFS inode hash statistics"},
+  {  "xinodes", kdbm_xfs_xinodes,	"<xfs_mount_t>",
+				"Dump XFS inodes per mount"},
+  {  "xquiesce",kdbm_xfs_xinodes_quiesce, "<xfs_mount_t>",
+				"Dump non-quiesced XFS inodes per mount"},
+  {  "xl_rcit", kdbm_xfs_xlog_ritem,	"<xlog_recover_item_t>",
+				"Dump XFS recovery item"},
+  {  "xl_rctr", kdbm_xfs_xlog_rtrans,	"<xlog_recover_t>",
+				"Dump XFS recovery transaction"},
+  {  "xl_rctr2",kdbm_xfs_xlog_rtrans_entire,	"<xlog_recover_t>",
+				"Dump entire recovery transaction"},
+  {  "xl_tic",	kdbm_xfs_xlog_tic,	"<xlog_ticket_t>",
+				"Dump XFS log ticket"},
+  {  "xlog",	kdbm_xfs_xlog,	"<xlog_t>",
+				"Dump XFS log"},
+  {  "xlogcb",	kdbm_xfs_xiclogcb,	"<xlog_in_core_t>",
+				"Dump XFS in-core log callbacks"},
+  {  "xlogitm", kdbm_xfs_xlogitem,	"<xfs_log_item_t>",
+				"Dump XFS log item structure"},
+  {  "xmount",	kdbm_xfs_xmount,	"<xfs_mount_t>",
+				"Dump XFS mount structure"},
+  {  "xnode",	kdbm_xfs_xnode,		"<xfs_inode_t>",
+				"Dump XFS inode"},
+  {  "xiocore", kdbm_xfs_xcore,		"<xfs_iocore_t>",
+				"Dump XFS iocore"},
+  {  "xperag",	kdbm_xfs_xperag,	"<xfs_mount_t>",
+				"Dump XFS per-allocation group data"},
+  {  "xqinfo",	kdbm_xfs_xqm_qinfo,	"<xfs_mount_t>",
+				"Dump mount->m_quotainfo structure"},
+#ifdef	CONFIG_XFS_QUOTA
+  {  "xqm",	kdbm_xfs_xqm,		"",
+				"Dump XFS quota manager structure"},
+  {  "xqmfree", kdbm_xfs_xqm_freelist,	"",
+				"Dump XFS global freelist of dquots"},
+  {  "xqmhtab", kdbm_xfs_xqm_htab,	"",
+				"Dump XFS hashtable of dquots"},
+#endif	/* CONFIG_XFS_QUOTA */
+  {  "xqmplist",kdbm_xfs_xqm_mplist,	"<xfs_mount_t>",
+				"Dump XFS all dquots of a f/s"},
+  {  "xsb",	kdbm_xfs_xsb,		"<xfs_sb_t> <cnv>",
+				"Dump XFS superblock"},
+  {  "xtp",	kdbm_xfs_xtp,		"<xfs_trans_t>",
+				"Dump XFS transaction structure"},
+  {  "xtrres",	kdbm_xfs_xtrans_res,	"<xfs_mount_t>",
+				"Dump XFS reservation values"},
+  {  0,		0,	0 }
+};
+
+static int
+__init xfsidbg_init(void)
+{
+	struct xif	*p;
+
+	for (p = xfsidbg_funcs; p->name; p++)
+		kdb_register(p->name, p->func, p->args, p->help, 0);
+
+	kdb_register("pb", kdbm_pb, "<vaddr>", "Display page_buf_t", 0);
+	kdb_register("pbflags", kdbm_pb_flags, "<flags>",
+					"Display page buf flags", 0);
+	kdb_register("pbiodesc", kdbm_pbiodesc, "<pb_io_desc_t *>",
+					"Display I/O Descriptor", 0);
+	kdb_register("pbmap", kdbm_pbmap, "<page_buf_bmap_t *>",
+					"Display Bmap", 0);
+	kdb_register("pbtrace", kdbm_pbtrace, "<vaddr>|<count>",
+					"page_buf_t trace", 0);
+#ifdef PAGEBUF_TRACE
+	kdb_register("pboffset", kdbm_pbtrace_offset, "<addr> [<mask>]",
+					"page_buf_t trace", 0);
+#endif
+	return 0;
+}
+
+static void
+__exit xfsidbg_exit(void)
+{
+	struct xif	*p;
+
+	for (p = xfsidbg_funcs; p->name; p++)
+		kdb_unregister(p->name);
+
+	kdb_unregister("pb");
+	kdb_unregister("pbflags");
+	kdb_unregister("pbmap");
+	kdb_unregister("pbiodesc");
+	kdb_unregister("pbtrace");
+#ifdef PAGEBUF_TRACE
+	kdb_unregister("pboffset");
+#endif
+
+}
+
+/*
+ * Argument to xfs_alloc routines, for allocation type.
+ */
+static char *xfs_alloctype[] = {
+	"any_ag", "first_ag", "start_ag", "this_ag",
+	"start_bno", "near_bno", "this_bno"
+};
+
+
+/*
+ * Prototypes for static functions.
+ */
+static void xfs_broot(xfs_inode_t *ip, xfs_ifork_t *f);
+static void xfs_btalloc(xfs_alloc_block_t *bt, int bsz);
+static void xfs_btbmap(xfs_bmbt_block_t *bt, int bsz);
+static void xfs_btino(xfs_inobt_block_t *bt, int bsz);
+static void xfs_buf_item_print(xfs_buf_log_item_t *blip, int summary);
+static void xfs_convert_extent(xfs_bmbt_rec_64_t *rp, xfs_dfiloff_t *op,
+			xfs_dfsbno_t *sp, xfs_dfilblks_t *cp, int *fp);
+static void xfs_dastate_path(xfs_da_state_path_t *p);
+static void xfs_dir2data(void *addr, int size);
+static void xfs_dir2leaf(xfs_dir2_leaf_t *leaf, int size);
+static void xfs_dquot_item_print(xfs_dq_logitem_t *lip, int summary);
+static void xfs_efd_item_print(xfs_efd_log_item_t *efdp, int summary);
+static void xfs_efi_item_print(xfs_efi_log_item_t *efip, int summary);
+static char *xfs_fmtformat(xfs_dinode_fmt_t f);
+static char *xfs_fmtfsblock(xfs_fsblock_t bno, xfs_mount_t *mp);
+static char *xfs_fmtino(xfs_ino_t ino, xfs_mount_t *mp);
+static char *xfs_fmtlsn(xfs_lsn_t *lsnp);
+static char *xfs_fmtmode(int m);
+static char *xfs_fmtsize(size_t i);
+static char *xfs_fmtuuid(uuid_t *);
+static void xfs_inode_item_print(xfs_inode_log_item_t *ilip, int summary);
+static void xfs_inodebuf(xfs_buf_t *bp);
+static void xfs_prdinode(xfs_dinode_t *di, int coreonly, int convert);
+static void xfs_prdinode_core(xfs_dinode_core_t *dip, int convert);
+static void xfs_qoff_item_print(xfs_qoff_logitem_t *lip, int summary);
+static void xfs_xexlist_fork(xfs_inode_t *ip, int whichfork);
+static void xfs_xnode_fork(char *name, xfs_ifork_t *f);
+
+/*
+ * Static functions.
+ */
+
+
+/*
+ * Print an xfs in-inode bmap btree root.
+ */
+static void
+xfs_broot(xfs_inode_t *ip, xfs_ifork_t *f)
+{
+	xfs_bmbt_block_t	*broot;
+	int			format;
+	int			i;
+	xfs_bmbt_key_t		*kp;
+	xfs_bmbt_ptr_t		*pp;
+
+	format = f == &ip->i_df ? ip->i_d.di_format : ip->i_d.di_aformat;
+	if ((f->if_flags & XFS_IFBROOT) == 0 ||
+	    format != XFS_DINODE_FMT_BTREE) {
+		kdb_printf("inode 0x%p not btree format\n", ip);
+		return;
+	}
+	broot = f->if_broot;
+	kdb_printf("block @0x%p magic %x level %d numrecs %d\n",
+		broot, INT_GET(broot->bb_magic, ARCH_CONVERT), INT_GET(broot->bb_level, ARCH_CONVERT), INT_GET(broot->bb_numrecs, ARCH_CONVERT));
+	kp = XFS_BMAP_BROOT_KEY_ADDR(broot, 1, f->if_broot_bytes);
+	pp = XFS_BMAP_BROOT_PTR_ADDR(broot, 1, f->if_broot_bytes);
+	for (i = 1; i <= INT_GET(broot->bb_numrecs, ARCH_CONVERT); i++)
+		kdb_printf("\t%d: startoff %Ld ptr %Lx %s\n",
+			i, INT_GET(kp[i - 1].br_startoff, ARCH_CONVERT), INT_GET(pp[i - 1], ARCH_CONVERT),
+			xfs_fmtfsblock(INT_GET(pp[i - 1], ARCH_CONVERT), ip->i_mount));
+}
+
+/*
+ * Print allocation btree block.
+ */
+static void
+xfs_btalloc(xfs_alloc_block_t *bt, int bsz)
+{
+	int i;
+
+	kdb_printf("magic 0x%x level %d numrecs %d leftsib 0x%x rightsib 0x%x\n",
+		INT_GET(bt->bb_magic, ARCH_CONVERT), INT_GET(bt->bb_level, ARCH_CONVERT), INT_GET(bt->bb_numrecs, ARCH_CONVERT),
+		INT_GET(bt->bb_leftsib, ARCH_CONVERT), INT_GET(bt->bb_rightsib, ARCH_CONVERT));
+	if (INT_ISZERO(bt->bb_level, ARCH_CONVERT)) {
+
+		for (i = 1; i <= INT_GET(bt->bb_numrecs, ARCH_CONVERT); i++) {
+			xfs_alloc_rec_t *r;
+
+			r = XFS_BTREE_REC_ADDR(bsz, xfs_alloc, bt, i, 0);
+			kdb_printf("rec %d startblock 0x%x blockcount %d\n",
+				i, INT_GET(r->ar_startblock, ARCH_CONVERT), INT_GET(r->ar_blockcount, ARCH_CONVERT));
+		}
+	} else {
+		int mxr;
+
+		mxr = XFS_BTREE_BLOCK_MAXRECS(bsz, xfs_alloc, 0);
+		for (i = 1; i <= INT_GET(bt->bb_numrecs, ARCH_CONVERT); i++) {
+			xfs_alloc_key_t *k;
+			xfs_alloc_ptr_t *p;
+
+			k = XFS_BTREE_KEY_ADDR(bsz, xfs_alloc, bt, i, mxr);
+			p = XFS_BTREE_PTR_ADDR(bsz, xfs_alloc, bt, i, mxr);
+			kdb_printf("key %d startblock 0x%x blockcount %d ptr 0x%x\n",
+				i, INT_GET(k->ar_startblock, ARCH_CONVERT), INT_GET(k->ar_blockcount, ARCH_CONVERT), *p);
+		}
+	}
+}
+
+/*
+ * Print a bmap btree block.
+ */
+static void
+xfs_btbmap(xfs_bmbt_block_t *bt, int bsz)
+{
+	int i;
+
+	kdb_printf("magic 0x%x level %d numrecs %d leftsib %Lx ",
+		INT_GET(bt->bb_magic, ARCH_CONVERT),
+		INT_GET(bt->bb_level, ARCH_CONVERT),
+		INT_GET(bt->bb_numrecs, ARCH_CONVERT),
+		INT_GET(bt->bb_leftsib, ARCH_CONVERT));
+	kdb_printf("rightsib %Lx\n", INT_GET(bt->bb_rightsib, ARCH_CONVERT));
+	if (INT_ISZERO(bt->bb_level, ARCH_CONVERT)) {
+		for (i = 1; i <= INT_GET(bt->bb_numrecs, ARCH_CONVERT); i++) {
+			xfs_bmbt_rec_64_t *r;
+			xfs_dfiloff_t o;
+			xfs_dfsbno_t s;
+			xfs_dfilblks_t c;
+			int fl;
+
+			r = (xfs_bmbt_rec_64_t *)XFS_BTREE_REC_ADDR(bsz,
+				xfs_bmbt, bt, i, 0);
+			xfs_convert_extent(r, &o, &s, &c, &fl);
+			kdb_printf("rec %d startoff %Ld ", i, o);
+			kdb_printf("startblock %Lx ", s);
+			kdb_printf("blockcount %Ld flag %d\n", c, fl);
+		}
+	} else {
+		int mxr;
+
+		mxr = XFS_BTREE_BLOCK_MAXRECS(bsz, xfs_bmbt, 0);
+		for (i = 1; i <= INT_GET(bt->bb_numrecs, ARCH_CONVERT); i++) {
+			xfs_bmbt_key_t *k;
+			xfs_bmbt_ptr_t *p;
+
+			k = XFS_BTREE_KEY_ADDR(bsz, xfs_bmbt, bt, i, mxr);
+			p = XFS_BTREE_PTR_ADDR(bsz, xfs_bmbt, bt, i, mxr);
+			kdb_printf("key %d startoff %Ld ",
+				i, INT_GET(k->br_startoff, ARCH_CONVERT));
+			kdb_printf("ptr %Lx\n", INT_GET(*p, ARCH_CONVERT));
+		}
+	}
+}
+
+/*
+ * Print an inode btree block.
+ */
+static void
+xfs_btino(xfs_inobt_block_t *bt, int bsz)
+{
+	int i;
+
+	kdb_printf("magic 0x%x level %d numrecs %d leftsib 0x%x rightsib 0x%x\n",
+		INT_GET(bt->bb_magic, ARCH_CONVERT), INT_GET(bt->bb_level, ARCH_CONVERT), INT_GET(bt->bb_numrecs, ARCH_CONVERT),
+		INT_GET(bt->bb_leftsib, ARCH_CONVERT), INT_GET(bt->bb_rightsib, ARCH_CONVERT));
+	if (INT_ISZERO(bt->bb_level, ARCH_CONVERT)) {
+
+		for (i = 1; i <= INT_GET(bt->bb_numrecs, ARCH_CONVERT); i++) {
+			xfs_inobt_rec_t *r;
+
+			r = XFS_BTREE_REC_ADDR(bsz, xfs_inobt, bt, i, 0);
+			kdb_printf("rec %d startino 0x%x freecount %d, free %Lx\n",
+				i, INT_GET(r->ir_startino, ARCH_CONVERT), INT_GET(r->ir_freecount, ARCH_CONVERT),
+				INT_GET(r->ir_free, ARCH_CONVERT));
+		}
+	} else {
+		int mxr;
+
+		mxr = XFS_BTREE_BLOCK_MAXRECS(bsz, xfs_inobt, 0);
+		for (i = 1; i <= INT_GET(bt->bb_numrecs, ARCH_CONVERT); i++) {
+			xfs_inobt_key_t *k;
+			xfs_inobt_ptr_t *p;
+
+			k = XFS_BTREE_KEY_ADDR(bsz, xfs_inobt, bt, i, mxr);
+			p = XFS_BTREE_PTR_ADDR(bsz, xfs_inobt, bt, i, mxr);
+			kdb_printf("key %d startino 0x%x ptr 0x%x\n",
+				i, INT_GET(k->ir_startino, ARCH_CONVERT), INT_GET(*p, ARCH_CONVERT));
+		}
+	}
+}
+
+/*
+ * Print a buf log item.
+ */
+static void
+xfs_buf_item_print(xfs_buf_log_item_t *blip, int summary)
+{
+	static char *bli_flags[] = {
+		"hold",		/* 0x1 */
+		"dirty",	/* 0x2 */
+		"stale",	/* 0x4 */
+		"logged",	/* 0x8 */
+		"ialloc",	/* 0x10 */
+		0
+		};
+	static char *blf_flags[] = {
+		"inode",	/* 0x1 */
+		"cancel",	/* 0x2 */
+		0
+		};
+
+	if (summary) {
+		kdb_printf("buf 0x%p blkno 0x%Lx ", blip->bli_buf,
+			     blip->bli_format.blf_blkno);
+		printflags(blip->bli_flags, bli_flags, "flags:");
+		kdb_printf("\n	 ");
+		xfsidbg_xbuf_real(blip->bli_buf, 1);
+		return;
+	}
+	kdb_printf("buf 0x%p recur %d refcount %d flags:",
+		blip->bli_buf, blip->bli_recur,
+		atomic_read(&blip->bli_refcount));
+	printflags(blip->bli_flags, bli_flags, NULL);
+	kdb_printf("\n");
+	kdb_printf("size %d blkno 0x%Lx len 0x%x map size %d map 0x%p\n",
+		blip->bli_format.blf_size, blip->bli_format.blf_blkno,
+		(uint) blip->bli_format.blf_len, blip->bli_format.blf_map_size,
+		&(blip->bli_format.blf_data_map[0]));
+	kdb_printf("blf flags: ");
+	printflags((uint)blip->bli_format.blf_flags, blf_flags, NULL);
+#ifdef XFS_TRANS_DEBUG
+	kdb_printf("orig 0x%x logged 0x%x",
+		blip->bli_orig, blip->bli_logged);
+#endif
+	kdb_printf("\n");
+}
+
+/*
+ * Convert an external extent descriptor to internal form.
+ */
+static void
+xfs_convert_extent(xfs_bmbt_rec_64_t *rp, xfs_dfiloff_t *op, xfs_dfsbno_t *sp,
+		   xfs_dfilblks_t *cp, int *fp)
+{
+	xfs_dfiloff_t o;
+	xfs_dfsbno_t s;
+	xfs_dfilblks_t c;
+	int flag;
+
+	flag = (int)((INT_GET(rp->l0, ARCH_CONVERT)) >> (64 - 1 ));
+	o = ((xfs_fileoff_t)INT_GET(rp->l0, ARCH_CONVERT) &
+			   (((__uint64_t)1 << ( 64 - 1	)) - 1) ) >> 9;
+	s = (((xfs_fsblock_t)INT_GET(rp->l0, ARCH_CONVERT) & (((__uint64_t)1 << ( 9 )) - 1) ) << 43) |
+			   (((xfs_fsblock_t)INT_GET(rp->l1, ARCH_CONVERT)) >> 21);
+	c = (xfs_filblks_t)(INT_GET(rp->l1, ARCH_CONVERT) & (((__uint64_t)1 << ( 21 )) - 1) );
+	*op = o;
+	*sp = s;
+	*cp = c;
+	*fp = flag;
+}
+
+
+/*
+ * Print an xfs_da_state_path structure.
+ */
+static void
+xfs_dastate_path(xfs_da_state_path_t *p)
+{
+	int i;
+
+	kdb_printf("active %d\n", p->active);
+	for (i = 0; i < XFS_DA_NODE_MAXDEPTH; i++) {
+		kdb_printf(" blk %d bp 0x%p blkno 0x%x",
+			i, p->blk[i].bp, p->blk[i].blkno);
+		kdb_printf(" index %d hashval 0x%x ",
+			p->blk[i].index, (uint_t)p->blk[i].hashval);
+		switch(p->blk[i].magic) {
+		case XFS_DA_NODE_MAGIC:		kdb_printf("NODE\n");	break;
+		case XFS_DIR_LEAF_MAGIC:	kdb_printf("DIR\n");	break;
+		case XFS_ATTR_LEAF_MAGIC:	kdb_printf("ATTR\n");	break;
+		case XFS_DIR2_LEAFN_MAGIC:	kdb_printf("DIR2\n");	break;
+		default:			kdb_printf("type ??\n");	break;
+		}
+	}
+}
+
+
+/*
+ * Print an efd log item.
+ */
+static void
+xfs_efd_item_print(xfs_efd_log_item_t *efdp, int summary)
+{
+	int		i;
+	xfs_extent_t	*ep;
+
+	if (summary) {
+		kdb_printf("Extent Free Done: ID 0x%Lx nextents %d (at 0x%p)\n",
+				efdp->efd_format.efd_efi_id,
+				efdp->efd_format.efd_nextents, efdp);
+		return;
+	}
+	kdb_printf("size %d nextents %d next extent %d efip 0x%p\n",
+		efdp->efd_format.efd_size, efdp->efd_format.efd_nextents,
+		efdp->efd_next_extent, efdp->efd_efip);
+	kdb_printf("efi_id 0x%Lx\n", efdp->efd_format.efd_efi_id);
+	kdb_printf("efd extents:\n");
+	ep = &(efdp->efd_format.efd_extents[0]);
+	for (i = 0; i < efdp->efd_next_extent; i++, ep++) {
+		kdb_printf("	block %Lx len %d\n",
+			ep->ext_start, ep->ext_len);
+	}
+}
+
+/*
+ * Print an efi log item.
+ */
+static void
+xfs_efi_item_print(xfs_efi_log_item_t *efip, int summary)
+{
+	int		i;
+	xfs_extent_t	*ep;
+	static char *efi_flags[] = {
+		"recovered",	/* 0x1 */
+		"committed",	/* 0x2 */
+		"cancelled",	/* 0x4 */
+		0,
+		};
+
+	if (summary) {
+		kdb_printf("Extent Free Intention: ID 0x%Lx nextents %d (at 0x%p)\n",
+				efip->efi_format.efi_id,
+				efip->efi_format.efi_nextents, efip);
+		return;
+	}
+	kdb_printf("size %d nextents %d next extent %d\n",
+		efip->efi_format.efi_size, efip->efi_format.efi_nextents,
+		efip->efi_next_extent);
+	kdb_printf("id %Lx", efip->efi_format.efi_id);
+	printflags(efip->efi_flags, efi_flags, "flags :");
+	kdb_printf("\n");
+	kdb_printf("efi extents:\n");
+	ep = &(efip->efi_format.efi_extents[0]);
+	for (i = 0; i < efip->efi_next_extent; i++, ep++) {
+		kdb_printf("	block %Lx len %d\n",
+			ep->ext_start, ep->ext_len);
+	}
+}
+
+/*
+ * Format inode "format" into a static buffer & return it.
+ */
+static char *
+xfs_fmtformat(xfs_dinode_fmt_t f)
+{
+	static char *t[] = {
+		"dev",
+		"local",
+		"extents",
+		"btree",
+		"uuid"
+	};
+
+	return t[f];
+}
+
+/*
+ * Format fsblock number into a static buffer & return it.
+ */
+static char *
+xfs_fmtfsblock(xfs_fsblock_t bno, xfs_mount_t *mp)
+{
+	static char rval[50];
+
+	if (bno == NULLFSBLOCK)
+		sprintf(rval, "NULLFSBLOCK");
+	else if (ISNULLSTARTBLOCK(bno))
+		sprintf(rval, "NULLSTARTBLOCK(%Ld)", STARTBLOCKVAL(bno));
+	else if (mp)
+		sprintf(rval, "%Ld[%x:%x]", (xfs_dfsbno_t)bno,
+			XFS_FSB_TO_AGNO(mp, bno), XFS_FSB_TO_AGBNO(mp, bno));
+	else
+		sprintf(rval, "%Ld", (xfs_dfsbno_t)bno);
+	return rval;
+}
+
+/*
+ * Format inode number into a static buffer & return it.
+ */
+static char *
+xfs_fmtino(xfs_ino_t ino, xfs_mount_t *mp)
+{
+	static char rval[50];
+
+	if (mp)
+		sprintf(rval, "%llu[%x:%x:%x]",
+			(unsigned long long) ino,
+			XFS_INO_TO_AGNO(mp, ino),
+			XFS_INO_TO_AGBNO(mp, ino),
+			XFS_INO_TO_OFFSET(mp, ino));
+	else
+		sprintf(rval, "%llu", (unsigned long long) ino);
+	return rval;
+}
+
+/*
+ * Format an lsn for printing into a static buffer & return it.
+ */
+static char *
+xfs_fmtlsn(xfs_lsn_t *lsnp)
+{
+	uint		*wordp;
+	uint		*word2p;
+	static char	buf[20];
+
+	wordp = (uint *)lsnp;
+	word2p = wordp++;
+	sprintf(buf, "[%u:%u]", *wordp, *word2p);
+
+	return buf;
+}
+
+/*
+ * Format file mode into a static buffer & return it.
+ */
+static char *
+xfs_fmtmode(int m)
+{
+	static char rval[16];
+
+	sprintf(rval, "%c%c%c%c%c%c%c%c%c%c%c%c%c",
+		"?fc?dxb?r?l?S?m?"[(m & IFMT) >> 12],
+		m & ISUID ? 'u' : '-',
+		m & ISGID ? 'g' : '-',
+		m & ISVTX ? 'v' : '-',
+		m & IREAD ? 'r' : '-',
+		m & IWRITE ? 'w' : '-',
+		m & IEXEC ? 'x' : '-',
+		m & (IREAD >> 3) ? 'r' : '-',
+		m & (IWRITE >> 3) ? 'w' : '-',
+		m & (IEXEC >> 3) ? 'x' : '-',
+		m & (IREAD >> 6) ? 'r' : '-',
+		m & (IWRITE >> 6) ? 'w' : '-',
+		m & (IEXEC >> 6) ? 'x' : '-');
+	return rval;
+}
+
+/*
+ * Format a size into a static buffer & return it.
+ */
+static char *
+xfs_fmtsize(size_t i)
+{
+	static char rval[20];
+
+	/* size_t is 32 bits in 32-bit kernel, 64 bits in 64-bit kernel */
+	sprintf(rval, "0x%lx", (unsigned long) i);
+	return rval;
+}
+
+/*
+ * Format a uuid into a static buffer & return it.
+ */
+static char *
+xfs_fmtuuid(uuid_t *uu)
+{
+	static char rval[40];
+	char	    *o		= rval;
+	char	    *i		= (unsigned char*)uu;
+	int	    b;
+
+	for (b=0;b<16;b++) {
+	    o+=sprintf(o, "%02x", *i++);
+	    if (b==3||b==5||b==7||b==9) *o++='-';
+	}
+	*o='\0';
+
+	return rval;
+}
+
+/*
+ * Print an inode log item.
+ */
+static void
+xfs_inode_item_print(xfs_inode_log_item_t *ilip, int summary)
+{
+	static char *ili_flags[] = {
+		"hold",		/* 0x1 */
+		"iolock excl",	/* 0x2 */
+		"iolock shrd",	/* 0x4 */
+		0
+		};
+	static char *ilf_fields[] = {
+		"core",		/* 0x001 */
+		"ddata",	/* 0x002 */
+		"dexts",	/* 0x004 */
+		"dbroot",	/* 0x008 */
+		"dev",		/* 0x010 */
+		"uuid",		/* 0x020 */
+		"adata",	/* 0x040 */
+		"aext",		/* 0x080 */
+		"abroot",	/* 0x100 */
+		0
+		};
+
+	if (summary) {
+		kdb_printf("inode 0x%p logged %d ",
+			ilip->ili_inode, ilip->ili_logged);
+		printflags(ilip->ili_flags, ili_flags, "flags:");
+		printflags(ilip->ili_format.ilf_fields, ilf_fields, "format:");
+		printflags(ilip->ili_last_fields, ilf_fields, "lastfield:");
+		kdb_printf("\n");
+		return;
+	}
+	kdb_printf("inode 0x%p ino 0x%llu logged %d flags: ",
+		ilip->ili_inode, (unsigned long long) ilip->ili_format.ilf_ino,
+		ilip->ili_logged);
+	printflags(ilip->ili_flags, ili_flags, NULL);
+	kdb_printf("\n");
+	kdb_printf("ilock recur %d iolock recur %d ext buf 0x%p\n",
+		ilip->ili_ilock_recur, ilip->ili_iolock_recur,
+		ilip->ili_extents_buf);
+#ifdef XFS_TRANS_DEBUG
+	kdb_printf("root bytes %d root orig 0x%x\n",
+		ilip->ili_root_size, ilip->ili_orig_root);
+#endif
+	kdb_printf("size %d fields: ", ilip->ili_format.ilf_size);
+	printflags(ilip->ili_format.ilf_fields, ilf_fields, "formatfield");
+	kdb_printf(" last fields: ");
+	printflags(ilip->ili_last_fields, ilf_fields, "lastfield");
+	kdb_printf("\n");
+	kdb_printf(" flush lsn %s last lsn %s\n",
+		xfs_fmtlsn(&(ilip->ili_flush_lsn)),
+		xfs_fmtlsn(&(ilip->ili_last_lsn)));
+	kdb_printf("dsize %d, asize %d, rdev 0x%x\n",
+		ilip->ili_format.ilf_dsize,
+		ilip->ili_format.ilf_asize,
+		ilip->ili_format.ilf_u.ilfu_rdev);
+	kdb_printf("blkno 0x%Lx len 0x%x boffset 0x%x\n",
+		ilip->ili_format.ilf_blkno,
+		ilip->ili_format.ilf_len,
+		ilip->ili_format.ilf_boffset);
+}
+
+/*
+ * Print a dquot log item.
+ */
+/* ARGSUSED */
+static void
+xfs_dquot_item_print(xfs_dq_logitem_t *lip, int summary)
+{
+	kdb_printf("dquot 0x%p\n",
+		lip->qli_dquot);
+
+}
+
+/*
+ * Print a quotaoff log item.
+ */
+/* ARGSUSED */
+static void
+xfs_qoff_item_print(xfs_qoff_logitem_t *lip, int summary)
+{
+	kdb_printf("start qoff item 0x%p flags 0x%x\n",
+		lip->qql_start_lip, lip->qql_format.qf_flags);
+
+}
+
+/*
+ * Print buffer full of inodes.
+ */
+static void
+xfs_inodebuf(xfs_buf_t *bp)
+{
+	xfs_dinode_t *di;
+	int n, i;
+
+	n = XFS_BUF_COUNT(bp) >> 8;
+	for (i = 0; i < n; i++) {
+		di = (xfs_dinode_t *)xfs_buf_offset(bp,
+					i * 256);
+		xfs_prdinode(di, 0, ARCH_CONVERT);
+	}
+}
+
+
+/*
+ * Print disk inode.
+ */
+static void
+xfs_prdinode(xfs_dinode_t *di, int coreonly, int convert)
+{
+	xfs_prdinode_core(&di->di_core, convert);
+	if (!coreonly)
+		kdb_printf("next_unlinked 0x%x u@0x%p\n",
+			INT_GET(di->di_next_unlinked, convert),
+			&di->di_u);
+}
+
+/*
+ * Print disk inode core.
+ */
+static void
+xfs_prdinode_core(xfs_dinode_core_t *dip, int convert)
+{
+	static char *diflags[] = {
+		"realtime",		/* XFS_DIFLAG_REALTIME */
+		"prealloc",		/* XFS_DIFLAG_PREALLOC */
+		NULL
+	};
+
+	kdb_printf("magic 0x%x mode 0%o (%s) version 0x%x format 0x%x (%s)\n",
+		INT_GET(dip->di_magic, convert),
+		INT_GET(dip->di_mode, convert),
+		xfs_fmtmode(INT_GET(dip->di_mode, convert)),
+		INT_GET(dip->di_version, convert),
+		INT_GET(dip->di_format, convert),
+		xfs_fmtformat(
+		    (xfs_dinode_fmt_t)INT_GET(dip->di_format, convert)));
+	kdb_printf("nlink 0x%x uid 0x%x gid 0x%x projid 0x%x\n",
+		INT_GET(dip->di_nlink, convert),
+		INT_GET(dip->di_uid, convert),
+		INT_GET(dip->di_gid, convert),
+		(uint)INT_GET(dip->di_projid, convert));
+	kdb_printf("atime 0x%x:%x mtime 0x%x:%x ctime 0x%x:%x\n",
+		INT_GET(dip->di_atime.t_sec, convert),
+		INT_GET(dip->di_atime.t_nsec, convert),
+		INT_GET(dip->di_mtime.t_sec, convert),
+		INT_GET(dip->di_mtime.t_nsec, convert),
+		INT_GET(dip->di_ctime.t_sec, convert),
+		INT_GET(dip->di_ctime.t_nsec, convert));
+	kdb_printf("size 0x%Lx ", INT_GET(dip->di_size, convert));
+	kdb_printf("nblocks %Ld extsize 0x%x nextents 0x%x anextents 0x%x\n",
+		INT_GET(dip->di_nblocks, convert),
+		INT_GET(dip->di_extsize, convert),
+		INT_GET(dip->di_nextents, convert),
+		INT_GET(dip->di_anextents, convert));
+	kdb_printf("forkoff %d aformat 0x%x (%s) dmevmask 0x%x dmstate 0x%x ",
+		INT_GET(dip->di_forkoff, convert),
+		INT_GET(dip->di_aformat, convert),
+		xfs_fmtformat(
+		    (xfs_dinode_fmt_t)INT_GET(dip->di_aformat, convert)),
+		INT_GET(dip->di_dmevmask, convert),
+		INT_GET(dip->di_dmstate, convert));
+	printflags(INT_GET(dip->di_flags, convert), diflags, "flags");
+	kdb_printf("gen 0x%x\n", INT_GET(dip->di_gen, convert));
+}
+
+/*
+ * Print xfs extent list for a fork.
+ */
+static void
+xfs_xexlist_fork(xfs_inode_t *ip, int whichfork)
+{
+	int nextents, i;
+	xfs_dfiloff_t o;
+	xfs_dfsbno_t s;
+	xfs_dfilblks_t c;
+	int flag;
+	xfs_ifork_t *ifp;
+
+	ifp = XFS_IFORK_PTR(ip, whichfork);
+	if (ifp->if_flags & XFS_IFEXTENTS) {
+		nextents = ifp->if_bytes / sizeof(xfs_bmbt_rec_64_t);
+		kdb_printf("inode 0x%p %cf extents 0x%p nextents 0x%x\n",
+			ip, "da"[whichfork], ifp->if_u1.if_extents, nextents);
+		for (i = 0; i < nextents; i++) {
+			xfs_convert_extent(
+				(xfs_bmbt_rec_64_t *)&ifp->if_u1.if_extents[i],
+				&o, &s, &c, &flag);
+			kdb_printf(
+		"%d: startoff %Ld startblock %s blockcount %Ld flag %d\n",
+				i, o, xfs_fmtfsblock(s, ip->i_mount), c, flag);
+		}
+	}
+}
+
+static void
+xfs_xnode_fork(char *name, xfs_ifork_t *f)
+{
+	static char *tab_flags[] = {
+		"inline",	/* XFS_IFINLINE */
+		"extents",	/* XFS_IFEXTENTS */
+		"broot",	/* XFS_IFBROOT */
+		NULL
+	};
+	int *p;
+
+	kdb_printf("%s fork", name);
+	if (f == NULL) {
+		kdb_printf(" empty\n");
+		return;
+	} else
+		kdb_printf("\n");
+	kdb_printf(" bytes %s ", xfs_fmtsize(f->if_bytes));
+	kdb_printf("real_bytes %s lastex 0x%x u1:%s 0x%p\n",
+		xfs_fmtsize(f->if_real_bytes), f->if_lastex,
+		f->if_flags & XFS_IFINLINE ? "data" : "extents",
+		f->if_flags & XFS_IFINLINE ?
+			f->if_u1.if_data :
+			(char *)f->if_u1.if_extents);
+	kdb_printf(" broot 0x%p broot_bytes %s ext_max %d ",
+		f->if_broot, xfs_fmtsize(f->if_broot_bytes), f->if_ext_max);
+	printflags(f->if_flags, tab_flags, "flags");
+	kdb_printf("\n");
+	kdb_printf(" u2");
+	for (p = (int *)&f->if_u2;
+	     p < (int *)((char *)&f->if_u2 + XFS_INLINE_DATA);
+	     p++)
+		kdb_printf(" 0x%x", *p);
+	kdb_printf("\n");
+}
+
+/*
+ * Command-level xfs-idbg functions.
+ */
+
+/*
+ * Print xfs allocation group freespace header.
+ */
+static void
+xfsidbg_xagf(xfs_agf_t *agf)
+{
+	kdb_printf("magicnum 0x%x versionnum 0x%x seqno 0x%x length 0x%x\n",
+		INT_GET(agf->agf_magicnum, ARCH_CONVERT),
+		INT_GET(agf->agf_versionnum, ARCH_CONVERT),
+		INT_GET(agf->agf_seqno, ARCH_CONVERT),
+		INT_GET(agf->agf_length, ARCH_CONVERT));
+	kdb_printf("roots b 0x%x c 0x%x levels b %d c %d\n",
+		INT_GET(agf->agf_roots[XFS_BTNUM_BNO], ARCH_CONVERT),
+		INT_GET(agf->agf_roots[XFS_BTNUM_CNT], ARCH_CONVERT),
+		INT_GET(agf->agf_levels[XFS_BTNUM_BNO], ARCH_CONVERT),
+		INT_GET(agf->agf_levels[XFS_BTNUM_CNT], ARCH_CONVERT));
+	kdb_printf("flfirst %d fllast %d flcount %d freeblks %d longest %d\n",
+		INT_GET(agf->agf_flfirst, ARCH_CONVERT),
+		INT_GET(agf->agf_fllast, ARCH_CONVERT),
+		INT_GET(agf->agf_flcount, ARCH_CONVERT),
+		INT_GET(agf->agf_freeblks, ARCH_CONVERT),
+		INT_GET(agf->agf_longest, ARCH_CONVERT));
+}
+
+/*
+ * Print xfs allocation group inode header.
+ */
+static void
+xfsidbg_xagi(xfs_agi_t *agi)
+{
+	int	i;
+	int	j;
+
+	kdb_printf("magicnum 0x%x versionnum 0x%x seqno 0x%x length 0x%x\n",
+		INT_GET(agi->agi_magicnum, ARCH_CONVERT),
+		INT_GET(agi->agi_versionnum, ARCH_CONVERT),
+		INT_GET(agi->agi_seqno, ARCH_CONVERT),
+		INT_GET(agi->agi_length, ARCH_CONVERT));
+	kdb_printf("count 0x%x root 0x%x level 0x%x\n",
+		INT_GET(agi->agi_count, ARCH_CONVERT),
+		INT_GET(agi->agi_root, ARCH_CONVERT),
+		INT_GET(agi->agi_level, ARCH_CONVERT));
+	kdb_printf("freecount 0x%x newino 0x%x dirino 0x%x\n",
+		INT_GET(agi->agi_freecount, ARCH_CONVERT),
+		INT_GET(agi->agi_newino, ARCH_CONVERT),
+		INT_GET(agi->agi_dirino, ARCH_CONVERT));
+
+	kdb_printf("unlinked buckets\n");
+	for (i = 0; i < XFS_AGI_UNLINKED_BUCKETS; i++) {
+		for (j = 0; j < 4; j++, i++) {
+			kdb_printf("0x%08x ",
+				INT_GET(agi->agi_unlinked[i], ARCH_CONVERT));
+		}
+		kdb_printf("\n");
+	}
+}
+
+
+/*
+ * Print an allocation argument structure for XFS.
+ */
+static void
+xfsidbg_xalloc(xfs_alloc_arg_t *args)
+{
+	kdb_printf("tp 0x%p mp 0x%p agbp 0x%p pag 0x%p fsbno %s\n",
+		args->tp, args->mp, args->agbp, args->pag,
+		xfs_fmtfsblock(args->fsbno, args->mp));
+	kdb_printf("agno 0x%x agbno 0x%x minlen 0x%x maxlen 0x%x mod 0x%x\n",
+		args->agno, args->agbno, args->minlen, args->maxlen, args->mod);
+	kdb_printf("prod 0x%x minleft 0x%x total 0x%x alignment 0x%x\n",
+		args->prod, args->minleft, args->total, args->alignment);
+	kdb_printf("minalignslop 0x%x len 0x%x type %s otype %s wasdel %d\n",
+		args->minalignslop, args->len, xfs_alloctype[args->type],
+		xfs_alloctype[args->otype], args->wasdel);
+	kdb_printf("wasfromfl %d isfl %d userdata %d\n",
+		args->wasfromfl, args->isfl, args->userdata);
+}
+
+
+
+/*
+ * Print an attr_list() context structure.
+ */
+static void
+xfsidbg_xattrcontext(xfs_attr_list_context_t *context)
+{
+	static char *attr_arg_flags[] = {
+		"DONTFOLLOW",	/* 0x0001 */
+		"?",		/* 0x0002 */
+		"?",		/* 0x0004 */
+		"?",		/* 0x0008 */
+		"CREATE",	/* 0x0010 */
+		"?",		/* 0x0020 */
+		"?",		/* 0x0040 */
+		"?",		/* 0x0080 */
+		"?",		/* 0x0100 */
+		"?",		/* 0x0200 */
+		"?",		/* 0x0400 */
+		"?",		/* 0x0800 */
+		"KERNOTIME",	/* 0x1000 */
+		NULL
+	};
+
+	kdb_printf("dp 0x%p, dupcnt %d, resynch %d",
+		    context->dp, context->dupcnt, context->resynch);
+	printflags((__psunsigned_t)context->flags, attr_arg_flags, ", flags");
+	kdb_printf("\ncursor h/b/o 0x%x/0x%x/%d -- p/p/i 0x%x/0x%x/0x%x\n",
+			  context->cursor->hashval, context->cursor->blkno,
+			  context->cursor->offset, context->cursor->pad1,
+			  context->cursor->pad2, context->cursor->initted);
+	kdb_printf("alist 0x%p, bufsize 0x%x, count %d, firstu 0x%x\n",
+		       context->alist, context->bufsize, context->count,
+		       context->firstu);
+}
+
+/*
+ * Print attribute leaf block.
+ */
+static void
+xfsidbg_xattrleaf(xfs_attr_leafblock_t *leaf)
+{
+	xfs_attr_leaf_hdr_t *h;
+	xfs_da_blkinfo_t *i;
+	xfs_attr_leaf_map_t *m;
+	xfs_attr_leaf_entry_t *e;
+	xfs_attr_leaf_name_local_t *l;
+	xfs_attr_leaf_name_remote_t *r;
+	int j, k;
+
+	h = &leaf->hdr;
+	i = &h->info;
+	kdb_printf("hdr info forw 0x%x back 0x%x magic 0x%x\n",
+		i->forw, i->back, i->magic);
+	kdb_printf("hdr count %d usedbytes %d firstused %d holes %d\n",
+		INT_GET(h->count, ARCH_CONVERT),
+		INT_GET(h->usedbytes, ARCH_CONVERT),
+		INT_GET(h->firstused, ARCH_CONVERT), h->holes);
+	for (j = 0, m = h->freemap; j < XFS_ATTR_LEAF_MAPSIZE; j++, m++) {
+		kdb_printf("hdr freemap %d base %d size %d\n",
+			j, INT_GET(m->base, ARCH_CONVERT),
+			INT_GET(m->size, ARCH_CONVERT));
+	}
+	for (j = 0, e = leaf->entries; j < INT_GET(h->count, ARCH_CONVERT); j++, e++) {
+		kdb_printf("[%2d] hash 0x%x nameidx %d flags 0x%x",
+			j, INT_GET(e->hashval, ARCH_CONVERT),
+			INT_GET(e->nameidx, ARCH_CONVERT), e->flags);
+		if (e->flags & XFS_ATTR_LOCAL)
+			kdb_printf("LOCAL ");
+		if (e->flags & XFS_ATTR_ROOT)
+			kdb_printf("ROOT ");
+		if (e->flags & XFS_ATTR_INCOMPLETE)
+			kdb_printf("INCOMPLETE ");
+		k = ~(XFS_ATTR_LOCAL | XFS_ATTR_ROOT | XFS_ATTR_INCOMPLETE);
+		if ((e->flags & k) != 0)
+			kdb_printf("0x%x", e->flags & k);
+		kdb_printf(">\n	    name \"");
+		if (e->flags & XFS_ATTR_LOCAL) {
+			l = XFS_ATTR_LEAF_NAME_LOCAL(leaf, j);
+			for (k = 0; k < l->namelen; k++)
+				kdb_printf("%c", l->nameval[k]);
+			kdb_printf("\"(%d) value \"", l->namelen);
+			for (k = 0; (k < INT_GET(l->valuelen, ARCH_CONVERT)) && (k < 32); k++)
+				kdb_printf("%c", l->nameval[l->namelen + k]);
+			if (k == 32)
+				kdb_printf("...");
+			kdb_printf("\"(%d)\n",
+				INT_GET(l->valuelen, ARCH_CONVERT));
+		} else {
+			r = XFS_ATTR_LEAF_NAME_REMOTE(leaf, j);
+			for (k = 0; k < r->namelen; k++)
+				kdb_printf("%c", r->name[k]);
+			kdb_printf("\"(%d) value blk 0x%x len %d\n",
+				    r->namelen,
+				    INT_GET(r->valueblk, ARCH_CONVERT),
+				    INT_GET(r->valuelen, ARCH_CONVERT));
+		}
+	}
+}
+
+/*
+ * Print a shortform attribute list.
+ */
+static void
+xfsidbg_xattrsf(xfs_attr_shortform_t *s)
+{
+	xfs_attr_sf_hdr_t *sfh;
+	xfs_attr_sf_entry_t *sfe;
+	int i, j;
+
+	sfh = &s->hdr;
+	kdb_printf("hdr count %d\n", INT_GET(sfh->count, ARCH_CONVERT));
+	for (i = 0, sfe = s->list; i < INT_GET(sfh->count, ARCH_CONVERT); i++) {
+		kdb_printf("entry %d namelen %d name \"", i, sfe->namelen);
+		for (j = 0; j < sfe->namelen; j++)
+			kdb_printf("%c", sfe->nameval[j]);
+		kdb_printf("\" valuelen %d value \"", INT_GET(sfe->valuelen, ARCH_CONVERT));
+		for (j = 0; (j < INT_GET(sfe->valuelen, ARCH_CONVERT)) && (j < 32); j++)
+			kdb_printf("%c", sfe->nameval[sfe->namelen + j]);
+		if (j == 32)
+			kdb_printf("...");
+		kdb_printf("\"\n");
+		sfe = XFS_ATTR_SF_NEXTENTRY(sfe);
+	}
+}
+
+
+/*
+ * Print xfs bmap internal record
+ */
+static void
+xfsidbg_xbirec(xfs_bmbt_irec_t *r)
+{
+	kdb_printf(
+	"startoff %Ld startblock %Lx blockcount %Ld state %Ld\n",
+		(__uint64_t)r->br_startoff,
+		(__uint64_t)r->br_startblock,
+		(__uint64_t)r->br_blockcount,
+		(__uint64_t)r->br_state);
+}
+
+
+/*
+ * Print a bmap alloc argument structure for XFS.
+ */
+static void
+xfsidbg_xbmalla(xfs_bmalloca_t *a)
+{
+	kdb_printf("tp 0x%p ip 0x%p eof %d prevp 0x%p\n",
+		a->tp, a->ip, a->eof, a->prevp);
+	kdb_printf("gotp 0x%p firstblock %s alen %d total %d\n",
+		a->gotp, xfs_fmtfsblock(a->firstblock, a->ip->i_mount),
+		a->alen, a->total);
+	kdb_printf("off %s wasdel %d userdata %d minlen %d\n",
+		xfs_fmtfsblock(a->off, a->ip->i_mount), a->wasdel,
+		a->userdata, a->minlen);
+	kdb_printf("minleft %d low %d rval %s aeof %d\n",
+		a->minleft, a->low, xfs_fmtfsblock(a->rval, a->ip->i_mount),
+		a->aeof);
+}
+
+
+/*
+ * Print xfs bmap record
+ */
+static void
+xfsidbg_xbrec(xfs_bmbt_rec_64_t *r)
+{
+	xfs_dfiloff_t o;
+	xfs_dfsbno_t s;
+	xfs_dfilblks_t c;
+	int flag;
+
+	xfs_convert_extent(r, &o, &s, &c, &flag);
+	kdb_printf("startoff %Ld startblock %Lx blockcount %Ld flag %d\n",
+		o, s, c, flag);
+}
+
+/*
+ * Print an xfs in-inode bmap btree root (data fork).
+ */
+static void
+xfsidbg_xbroot(xfs_inode_t *ip)
+{
+	xfs_broot(ip, &ip->i_df);
+}
+
+/*
+ * Print an xfs in-inode bmap btree root (attribute fork).
+ */
+static void
+xfsidbg_xbroota(xfs_inode_t *ip)
+{
+	if (ip->i_afp)
+		xfs_broot(ip, ip->i_afp);
+}
+
+/*
+ * Print xfs btree cursor.
+ */
+static void
+xfsidbg_xbtcur(xfs_btree_cur_t *c)
+{
+	int l;
+
+	kdb_printf("tp 0x%p mp 0x%p\n",
+		c->bc_tp,
+		c->bc_mp);
+	if (c->bc_btnum == XFS_BTNUM_BMAP) {
+		kdb_printf("rec.b ");
+		xfsidbg_xbirec(&c->bc_rec.b);
+	} else if (c->bc_btnum == XFS_BTNUM_INO) {
+		kdb_printf("rec.i startino 0x%x freecount 0x%x free %Lx\n",
+			c->bc_rec.i.ir_startino, c->bc_rec.i.ir_freecount,
+			c->bc_rec.i.ir_free);
+	} else {
+		kdb_printf("rec.a startblock 0x%x blockcount 0x%x\n",
+			c->bc_rec.a.ar_startblock,
+			c->bc_rec.a.ar_blockcount);
+	}
+	kdb_printf("bufs");
+	for (l = 0; l < c->bc_nlevels; l++)
+		kdb_printf(" 0x%p", c->bc_bufs[l]);
+	kdb_printf("\n");
+	kdb_printf("ptrs");
+	for (l = 0; l < c->bc_nlevels; l++)
+		kdb_printf(" 0x%x", c->bc_ptrs[l]);
+	kdb_printf("  ra");
+	for (l = 0; l < c->bc_nlevels; l++)
+		kdb_printf(" %d", c->bc_ra[l]);
+	kdb_printf("\n");
+	kdb_printf("nlevels %d btnum %s blocklog %d\n",
+		c->bc_nlevels,
+		c->bc_btnum == XFS_BTNUM_BNO ? "bno" :
+		(c->bc_btnum == XFS_BTNUM_CNT ? "cnt" :
+		 (c->bc_btnum == XFS_BTNUM_BMAP ? "bmap" : "ino")),
+		c->bc_blocklog);
+	if (c->bc_btnum == XFS_BTNUM_BMAP) {
+		kdb_printf("private forksize 0x%x whichfork %d ip 0x%p flags %d\n",
+			c->bc_private.b.forksize,
+			c->bc_private.b.whichfork,
+			c->bc_private.b.ip,
+			c->bc_private.b.flags);
+		kdb_printf("private firstblock %s flist 0x%p allocated 0x%x\n",
+			xfs_fmtfsblock(c->bc_private.b.firstblock, c->bc_mp),
+			c->bc_private.b.flist,
+			c->bc_private.b.allocated);
+	} else if (c->bc_btnum == XFS_BTNUM_INO) {
+		kdb_printf("private agbp 0x%p agno 0x%x\n",
+			c->bc_private.i.agbp,
+			c->bc_private.i.agno);
+	} else {
+		kdb_printf("private agbp 0x%p agno 0x%x\n",
+			c->bc_private.a.agbp,
+			c->bc_private.a.agno);
+	}
+}
+
+/*
+ * Figure out what kind of xfs block the buffer contains,
+ * and invoke a print routine.
+ */
+static void
+xfsidbg_xbuf(xfs_buf_t *bp)
+{
+	xfsidbg_xbuf_real(bp, 0);
+}
+
+/*
+ * Figure out what kind of xfs block the buffer contains,
+ * and invoke a print routine (if asked to).
+ */
+static void
+xfsidbg_xbuf_real(xfs_buf_t *bp, int summary)
+{
+	void *d;
+	xfs_agf_t *agf;
+	xfs_agi_t *agi;
+	xfs_sb_t *sb;
+	xfs_alloc_block_t *bta;
+	xfs_bmbt_block_t *btb;
+	xfs_inobt_block_t *bti;
+	xfs_attr_leafblock_t *aleaf;
+	xfs_dir_leafblock_t *dleaf;
+	xfs_da_intnode_t *node;
+	xfs_dinode_t *di;
+	xfs_disk_dquot_t *dqb;
+	xfs_dir2_block_t *d2block;
+	xfs_dir2_data_t *d2data;
+	xfs_dir2_leaf_t *d2leaf;
+	xfs_dir2_free_t *d2free;
+
+	d = XFS_BUF_PTR(bp);
+	if (INT_GET((agf = d)->agf_magicnum, ARCH_CONVERT) == XFS_AGF_MAGIC) {
+		if (summary) {
+			kdb_printf("freespace hdr for AG %d (at 0x%p)\n",
+				INT_GET(agf->agf_seqno, ARCH_CONVERT), agf);
+		} else {
+			kdb_printf("buf 0x%p agf 0x%p\n", bp, agf);
+			xfsidbg_xagf(agf);
+		}
+	} else if (INT_GET((agi = d)->agi_magicnum, ARCH_CONVERT) == XFS_AGI_MAGIC) {
+		if (summary) {
+			kdb_printf("Inode hdr for AG %d (at 0x%p)\n",
+			       INT_GET(agi->agi_seqno, ARCH_CONVERT), agi);
+		} else {
+			kdb_printf("buf 0x%p agi 0x%p\n", bp, agi);
+			xfsidbg_xagi(agi);
+		}
+	} else if (INT_GET((bta = d)->bb_magic, ARCH_CONVERT) == XFS_ABTB_MAGIC) {
+		if (summary) {
+			kdb_printf("Alloc BNO Btree blk, level %d (at 0x%p)\n",
+				       INT_GET(bta->bb_level, ARCH_CONVERT), bta);
+		} else {
+			kdb_printf("buf 0x%p abtbno 0x%p\n", bp, bta);
+			xfs_btalloc(bta, XFS_BUF_COUNT(bp));
+		}
+	} else if (INT_GET((bta = d)->bb_magic, ARCH_CONVERT) == XFS_ABTC_MAGIC) {
+		if (summary) {
+			kdb_printf("Alloc COUNT Btree blk, level %d (at 0x%p)\n",
+				       INT_GET(bta->bb_level, ARCH_CONVERT), bta);
+		} else {
+			kdb_printf("buf 0x%p abtcnt 0x%p\n", bp, bta);
+			xfs_btalloc(bta, XFS_BUF_COUNT(bp));
+		}
+	} else if (INT_GET((btb = d)->bb_magic, ARCH_CONVERT) == XFS_BMAP_MAGIC) {
+		if (summary) {
+			kdb_printf("Bmap Btree blk, level %d (at 0x%p)\n",
+				      INT_GET(btb->bb_level, ARCH_CONVERT), btb);
+		} else {
+			kdb_printf("buf 0x%p bmapbt 0x%p\n", bp, btb);
+			xfs_btbmap(btb, XFS_BUF_COUNT(bp));
+		}
+	} else if (INT_GET((bti = d)->bb_magic, ARCH_CONVERT) == XFS_IBT_MAGIC) {
+		if (summary) {
+			kdb_printf("Inode Btree blk, level %d (at 0x%p)\n",
+				       INT_GET(bti->bb_level, ARCH_CONVERT), bti);
+		} else {
+			kdb_printf("buf 0x%p inobt 0x%p\n", bp, bti);
+			xfs_btino(bti, XFS_BUF_COUNT(bp));
+		}
+	} else if (INT_GET((aleaf = d)->hdr.info.magic, ARCH_CONVERT) == XFS_ATTR_LEAF_MAGIC) {
+		if (summary) {
+			kdb_printf("Attr Leaf, 1st hash 0x%x (at 0x%p)\n",
+				      INT_GET(aleaf->entries[0].hashval, ARCH_CONVERT), aleaf);
+		} else {
+			kdb_printf("buf 0x%p attr leaf 0x%p\n", bp, aleaf);
+			xfsidbg_xattrleaf(aleaf);
+		}
+	} else if (INT_GET((dleaf = d)->hdr.info.magic, ARCH_CONVERT) == XFS_DIR_LEAF_MAGIC) {
+		if (summary) {
+			kdb_printf("Dir Leaf, 1st hash 0x%x (at 0x%p)\n",
+				     dleaf->entries[0].hashval, dleaf);
+		} else {
+			kdb_printf("buf 0x%p dir leaf 0x%p\n", bp, dleaf);
+			xfsidbg_xdirleaf(dleaf);
+		}
+	} else if (INT_GET((node = d)->hdr.info.magic, ARCH_CONVERT) == XFS_DA_NODE_MAGIC) {
+		if (summary) {
+			kdb_printf("Dir/Attr Node, level %d, 1st hash 0x%x (at 0x%p)\n",
+			      node->hdr.level, node->btree[0].hashval, node);
+		} else {
+			kdb_printf("buf 0x%p dir/attr node 0x%p\n", bp, node);
+			xfsidbg_xdanode(node);
+		}
+	} else if (INT_GET((di = d)->di_core.di_magic, ARCH_CONVERT) == XFS_DINODE_MAGIC) {
+		if (summary) {
+			kdb_printf("Disk Inode (at 0x%p)\n", di);
+		} else {
+			kdb_printf("buf 0x%p dinode 0x%p\n", bp, di);
+			xfs_inodebuf(bp);
+		}
+	} else if (INT_GET((sb = d)->sb_magicnum, ARCH_CONVERT) == XFS_SB_MAGIC) {
+		if (summary) {
+			kdb_printf("Superblock (at 0x%p)\n", sb);
+		} else {
+			kdb_printf("buf 0x%p sb 0x%p\n", bp, sb);
+			/* SB in a buffer - we need to convert */
+			xfsidbg_xsb(sb, 1);
+		}
+	} else if ((dqb = d)->d_magic == XFS_DQUOT_MAGIC) {
+#define XFSIDBG_DQTYPESTR(d)	 \
+	((INT_GET((d)->d_flags, ARCH_CONVERT) & XFS_DQ_USER) ? "USR" : \
+	((INT_GET((d)->d_flags, ARCH_CONVERT) & XFS_DQ_GROUP) ? "GRP" : "???"))
+		kdb_printf("Quota blk starting ID [%d], type %s at 0x%p\n",
+			INT_GET(dqb->d_id, ARCH_CONVERT), XFSIDBG_DQTYPESTR(dqb), dqb);
+
+	} else if (INT_GET((d2block = d)->hdr.magic, ARCH_CONVERT) == XFS_DIR2_BLOCK_MAGIC) {
+		if (summary) {
+			kdb_printf("Dir2 block (at 0x%p)\n", d2block);
+		} else {
+			kdb_printf("buf 0x%p dir2 block 0x%p\n", bp, d2block);
+			xfs_dir2data((void *)d2block, XFS_BUF_COUNT(bp));
+		}
+	} else if (INT_GET((d2data = d)->hdr.magic, ARCH_CONVERT) == XFS_DIR2_DATA_MAGIC) {
+		if (summary) {
+			kdb_printf("Dir2 data (at 0x%p)\n", d2data);
+		} else {
+			kdb_printf("buf 0x%p dir2 data 0x%p\n", bp, d2data);
+			xfs_dir2data((void *)d2data, XFS_BUF_COUNT(bp));
+		}
+	} else if (INT_GET((d2leaf = d)->hdr.info.magic, ARCH_CONVERT) == XFS_DIR2_LEAF1_MAGIC) {
+		if (summary) {
+			kdb_printf("Dir2 leaf(1) (at 0x%p)\n", d2leaf);
+		} else {
+			kdb_printf("buf 0x%p dir2 leaf 0x%p\n", bp, d2leaf);
+			xfs_dir2leaf(d2leaf, XFS_BUF_COUNT(bp));
+		}
+	} else if (INT_GET(d2leaf->hdr.info.magic, ARCH_CONVERT) == XFS_DIR2_LEAFN_MAGIC) {
+		if (summary) {
+			kdb_printf("Dir2 leaf(n) (at 0x%p)\n", d2leaf);
+		} else {
+			kdb_printf("buf 0x%p dir2 leaf 0x%p\n", bp, d2leaf);
+			xfs_dir2leaf(d2leaf, XFS_BUF_COUNT(bp));
+		}
+	} else if (INT_GET((d2free = d)->hdr.magic, ARCH_CONVERT) == XFS_DIR2_FREE_MAGIC) {
+		if (summary) {
+			kdb_printf("Dir2 free (at 0x%p)\n", d2free);
+		} else {
+			kdb_printf("buf 0x%p dir2 free 0x%p\n", bp, d2free);
+			xfsidbg_xdir2free(d2free);
+		}
+	} else {
+		kdb_printf("buf 0x%p unknown 0x%p\n", bp, d);
+	}
+}
+
+
+/*
+ * Print an xfs_da_args structure.
+ */
+static void
+xfsidbg_xdaargs(xfs_da_args_t *n)
+{
+	char *ch;
+	int i;
+
+	kdb_printf(" name \"");
+	for (i = 0; i < n->namelen; i++) {
+		kdb_printf("%c", n->name[i]);
+	}
+	kdb_printf("\"(%d) value ", n->namelen);
+	if (n->value) {
+		kdb_printf("\"");
+		ch = n->value;
+		for (i = 0; (i < n->valuelen) && (i < 32); ch++, i++) {
+			switch(*ch) {
+			case '\n':	kdb_printf("\n");		break;
+			case '\b':	kdb_printf("\b");		break;
+			case '\t':	kdb_printf("\t");		break;
+			default:	kdb_printf("%c", *ch);	break;
+			}
+		}
+		if (i == 32)
+			kdb_printf("...");
+		kdb_printf("\"(%d)\n", n->valuelen);
+	} else {
+		kdb_printf("(NULL)(%d)\n", n->valuelen);
+	}
+	kdb_printf(" hashval 0x%x whichfork %d flags <",
+		  (uint_t)n->hashval, n->whichfork);
+	if (n->flags & ATTR_ROOT)
+		kdb_printf("ROOT ");
+	if (n->flags & ATTR_CREATE)
+		kdb_printf("CREATE ");
+	if (n->flags & ATTR_REPLACE)
+		kdb_printf("REPLACE ");
+	if (n->flags & XFS_ATTR_INCOMPLETE)
+		kdb_printf("INCOMPLETE ");
+	i = ~(ATTR_ROOT | ATTR_CREATE | ATTR_REPLACE | XFS_ATTR_INCOMPLETE);
+	if ((n->flags & i) != 0)
+		kdb_printf("0x%x", n->flags & i);
+	kdb_printf(">\n");
+	kdb_printf(" rename %d justcheck %d addname %d oknoent %d\n",
+		  n->rename, n->justcheck, n->addname, n->oknoent);
+	kdb_printf(" leaf: blkno %d index %d rmtblkno %d rmtblkcnt %d\n",
+		  n->blkno, n->index, n->rmtblkno, n->rmtblkcnt);
+	kdb_printf(" leaf2: blkno %d index %d rmtblkno %d rmtblkcnt %d\n",
+		  n->blkno2, n->index2, n->rmtblkno2, n->rmtblkcnt2);
+	kdb_printf(" inumber %llu dp 0x%p firstblock 0x%p flist 0x%p\n",
+		  (unsigned long long) n->inumber,
+		  n->dp, n->firstblock, n->flist);
+	kdb_printf(" trans 0x%p total %d\n",
+		  n->trans, n->total);
+}
+
+/*
+ * Print a da buffer structure.
+ */
+static void
+xfsidbg_xdabuf(xfs_dabuf_t *dabuf)
+{
+	int	i;
+
+	kdb_printf("nbuf %d dirty %d bbcount %d data 0x%p bps",
+		dabuf->nbuf, dabuf->dirty, dabuf->bbcount, dabuf->data);
+	for (i = 0; i < dabuf->nbuf; i++)
+		kdb_printf(" %d:0x%p", i, dabuf->bps[i]);
+	kdb_printf("\n");
+#ifdef XFS_DABUF_DEBUG
+	kdb_printf(" ra 0x%x prev 0x%x next 0x%x dev 0x%x blkno 0x%x\n",
+		dabuf->ra, dabuf->prev, dabuf->next, dabuf->dev, dabuf->blkno);
+#endif
+}
+
+/*
+ * Print a directory/attribute internal node block.
+ */
+static void
+xfsidbg_xdanode(xfs_da_intnode_t *node)
+{
+	xfs_da_node_hdr_t *h;
+	xfs_da_blkinfo_t *i;
+	xfs_da_node_entry_t *e;
+	int j;
+
+	h = &node->hdr;
+	i = &h->info;
+	kdb_printf("hdr info forw 0x%x back 0x%x magic 0x%x\n",
+		INT_GET(i->forw, ARCH_CONVERT), INT_GET(i->back, ARCH_CONVERT), INT_GET(i->magic, ARCH_CONVERT));
+	kdb_printf("hdr count %d level %d\n",
+		INT_GET(h->count, ARCH_CONVERT), INT_GET(h->level, ARCH_CONVERT));
+	for (j = 0, e = node->btree; j < INT_GET(h->count, ARCH_CONVERT); j++, e++) {
+		kdb_printf("btree %d hashval 0x%x before 0x%x\n",
+			j, (uint_t)INT_GET(e->hashval, ARCH_CONVERT), INT_GET(e->before, ARCH_CONVERT));
+	}
+}
+
+/*
+ * Print an xfs_da_state_blk structure.
+ */
+static void
+xfsidbg_xdastate(xfs_da_state_t *s)
+{
+	xfs_da_state_blk_t *eblk;
+
+	kdb_printf("args 0x%p mp 0x%p blocksize %d inleaf %d\n",
+		s->args, s->mp, s->blocksize, s->inleaf);
+	if (s->args)
+		xfsidbg_xdaargs(s->args);
+
+	kdb_printf("path:  ");
+	xfs_dastate_path(&s->path);
+
+	kdb_printf("altpath:  ");
+	xfs_dastate_path(&s->altpath);
+
+	eblk = &s->extrablk;
+	kdb_printf("extra: valid %d, after %d\n", s->extravalid, s->extraafter);
+	kdb_printf(" bp 0x%p blkno 0x%x ", eblk->bp, eblk->blkno);
+	kdb_printf("index %d hashval 0x%x\n", eblk->index, (uint_t)eblk->hashval);
+}
+
+/*
+ * Print a directory leaf block.
+ */
+static void
+xfsidbg_xdirleaf(xfs_dir_leafblock_t *leaf)
+{
+	xfs_dir_leaf_hdr_t *h;
+	xfs_da_blkinfo_t *i;
+	xfs_dir_leaf_map_t *m;
+	xfs_dir_leaf_entry_t *e;
+	xfs_dir_leaf_name_t *n;
+	int j, k;
+	xfs_ino_t ino;
+
+	h = &leaf->hdr;
+	i = &h->info;
+	kdb_printf("hdr info forw 0x%x back 0x%x magic 0x%x\n",
+		INT_GET(i->forw, ARCH_CONVERT), INT_GET(i->back, ARCH_CONVERT), INT_GET(i->magic, ARCH_CONVERT));
+	kdb_printf("hdr count %d namebytes %d firstused %d holes %d\n",
+		INT_GET(h->count, ARCH_CONVERT), INT_GET(h->namebytes, ARCH_CONVERT), INT_GET(h->firstused, ARCH_CONVERT), h->holes);
+	for (j = 0, m = h->freemap; j < XFS_DIR_LEAF_MAPSIZE; j++, m++) {
+		kdb_printf("hdr freemap %d base %d size %d\n",
+			j, INT_GET(m->base, ARCH_CONVERT), INT_GET(m->size, ARCH_CONVERT));
+	}
+	for (j = 0, e = leaf->entries; j < INT_GET(h->count, ARCH_CONVERT); j++, e++) {
+		n = XFS_DIR_LEAF_NAMESTRUCT(leaf, INT_GET(e->nameidx, ARCH_CONVERT));
+		XFS_DIR_SF_GET_DIRINO_ARCH(&n->inumber, &ino, ARCH_CONVERT);
+		kdb_printf("leaf %d hashval 0x%x nameidx %d inumber %llu ",
+			j, (uint_t)INT_GET(e->hashval, ARCH_CONVERT),
+			INT_GET(e->nameidx, ARCH_CONVERT),
+			(unsigned long long)ino);
+		kdb_printf("namelen %d name \"", e->namelen);
+		for (k = 0; k < e->namelen; k++)
+			kdb_printf("%c", n->name[k]);
+		kdb_printf("\"\n");
+	}
+}
+
+/*
+ * Print a directory v2 data block, single or multiple.
+ */
+static void
+xfs_dir2data(void *addr, int size)
+{
+	xfs_dir2_data_t *db;
+	xfs_dir2_block_t *bb;
+	xfs_dir2_data_hdr_t *h;
+	xfs_dir2_data_free_t *m;
+	xfs_dir2_data_entry_t *e;
+	xfs_dir2_data_unused_t *u;
+	xfs_dir2_leaf_entry_t *l=NULL;
+	int j, k;
+	char *p;
+	char *t;
+	xfs_dir2_block_tail_t *tail=NULL;
+
+	db = (xfs_dir2_data_t *)addr;
+	bb = (xfs_dir2_block_t *)addr;
+	h = &db->hdr;
+	kdb_printf("hdr magic 0x%x (%s)\nhdr bestfree", INT_GET(h->magic, ARCH_CONVERT),
+		INT_GET(h->magic, ARCH_CONVERT) == XFS_DIR2_DATA_MAGIC ? "DATA" :
+			(INT_GET(h->magic, ARCH_CONVERT) == XFS_DIR2_BLOCK_MAGIC ? "BLOCK" : ""));
+	for (j = 0, m = h->bestfree; j < XFS_DIR2_DATA_FD_COUNT; j++, m++) {
+		kdb_printf(" %d: 0x%x@0x%x", j, INT_GET(m->length, ARCH_CONVERT), INT_GET(m->offset, ARCH_CONVERT));
+	}
+	kdb_printf("\n");
+	if (INT_GET(h->magic, ARCH_CONVERT) == XFS_DIR2_DATA_MAGIC)
+		t = (char *)db + size;
+	else {
+		/* XFS_DIR2_BLOCK_TAIL_P */
+		tail = (xfs_dir2_block_tail_t *)
+		       ((char *)bb + size - sizeof(xfs_dir2_block_tail_t));
+		l = XFS_DIR2_BLOCK_LEAF_P_ARCH(tail, ARCH_CONVERT);
+		t = (char *)l;
+	}
+	for (p = (char *)(h + 1); p < t; ) {
+		u = (xfs_dir2_data_unused_t *)p;
+		if (u->freetag == XFS_DIR2_DATA_FREE_TAG) {
+			kdb_printf("0x%lx unused freetag 0x%x length 0x%x tag 0x%x\n",
+				(unsigned long) (p - (char *)addr),
+				INT_GET(u->freetag, ARCH_CONVERT),
+				INT_GET(u->length, ARCH_CONVERT),
+				INT_GET(*XFS_DIR2_DATA_UNUSED_TAG_P_ARCH(u, ARCH_CONVERT), ARCH_CONVERT));
+			p += INT_GET(u->length, ARCH_CONVERT);
+			continue;
+		}
+		e = (xfs_dir2_data_entry_t *)p;
+		kdb_printf("0x%lx entry inumber %llu namelen %d name \"",
+			(unsigned long) (p - (char *)addr),
+			(unsigned long long) INT_GET(e->inumber, ARCH_CONVERT),
+			e->namelen);
+		for (k = 0; k < e->namelen; k++)
+			kdb_printf("%c", e->name[k]);
+		kdb_printf("\" tag 0x%x\n", INT_GET(*XFS_DIR2_DATA_ENTRY_TAG_P(e), ARCH_CONVERT));
+		p += XFS_DIR2_DATA_ENTSIZE(e->namelen);
+	}
+	if (INT_GET(h->magic, ARCH_CONVERT) == XFS_DIR2_DATA_MAGIC)
+		return;
+	for (j = 0; j < INT_GET(tail->count, ARCH_CONVERT); j++, l++) {
+		kdb_printf("0x%lx leaf %d hashval 0x%x address 0x%x (byte 0x%x)\n",
+			(unsigned long) ((char *)l - (char *)addr), j,
+			(uint_t)INT_GET(l->hashval, ARCH_CONVERT),
+			INT_GET(l->address, ARCH_CONVERT),
+			/* XFS_DIR2_DATAPTR_TO_BYTE */
+			INT_GET(l->address, ARCH_CONVERT) << XFS_DIR2_DATA_ALIGN_LOG);
+	}
+	kdb_printf("0x%lx tail count %d\n",
+		(unsigned long) ((char *)tail - (char *)addr),
+		INT_GET(tail->count, ARCH_CONVERT));
+}
+
+static void
+xfs_dir2leaf(xfs_dir2_leaf_t *leaf, int size)
+{
+	xfs_dir2_leaf_hdr_t *h;
+	xfs_da_blkinfo_t *i;
+	xfs_dir2_leaf_entry_t *e;
+	xfs_dir2_data_off_t *b;
+	xfs_dir2_leaf_tail_t *t;
+	int j;
+
+	h = &leaf->hdr;
+	i = &h->info;
+	e = leaf->ents;
+	kdb_printf("hdr info forw 0x%x back 0x%x magic 0x%x\n",
+		INT_GET(i->forw, ARCH_CONVERT), INT_GET(i->back, ARCH_CONVERT), INT_GET(i->magic, ARCH_CONVERT));
+	kdb_printf("hdr count %d stale %d\n", INT_GET(h->count, ARCH_CONVERT), INT_GET(h->stale, ARCH_CONVERT));
+	for (j = 0; j < INT_GET(h->count, ARCH_CONVERT); j++, e++) {
+		kdb_printf("0x%lx ent %d hashval 0x%x address 0x%x (byte 0x%x)\n",
+			(unsigned long) ((char *)e - (char *)leaf), j,
+			(uint_t)INT_GET(e->hashval, ARCH_CONVERT),
+			INT_GET(e->address, ARCH_CONVERT),
+			/* XFS_DIR2_DATAPTR_TO_BYTE */
+			INT_GET(e->address, ARCH_CONVERT) << XFS_DIR2_DATA_ALIGN_LOG);
+	}
+	if (INT_GET(i->magic, ARCH_CONVERT) == XFS_DIR2_LEAFN_MAGIC)
+		return;
+	/* XFS_DIR2_LEAF_TAIL_P */
+	t = (xfs_dir2_leaf_tail_t *)((char *)leaf + size - sizeof(*t));
+	b = XFS_DIR2_LEAF_BESTS_P_ARCH(t, ARCH_CONVERT);
+	for (j = 0; j < INT_GET(t->bestcount, ARCH_CONVERT); j++, b++) {
+		kdb_printf("0x%lx best %d 0x%x\n",
+			(unsigned long) ((char *)b - (char *)leaf), j,
+			INT_GET(*b, ARCH_CONVERT));
+	}
+	kdb_printf("tail bestcount %d\n", INT_GET(t->bestcount, ARCH_CONVERT));
+}
+
+/*
+ * Print a shortform directory.
+ */
+static void
+xfsidbg_xdirsf(xfs_dir_shortform_t *s)
+{
+	xfs_dir_sf_hdr_t *sfh;
+	xfs_dir_sf_entry_t *sfe;
+	xfs_ino_t ino;
+	int i, j;
+
+	sfh = &s->hdr;
+	XFS_DIR_SF_GET_DIRINO_ARCH(&sfh->parent, &ino, ARCH_CONVERT);
+	kdb_printf("hdr parent %llu", (unsigned long long)ino);
+	kdb_printf(" count %d\n", sfh->count);
+	for (i = 0, sfe = s->list; i < sfh->count; i++) {
+		XFS_DIR_SF_GET_DIRINO_ARCH(&sfe->inumber, &ino, ARCH_CONVERT);
+		kdb_printf("entry %d inumber %llu", i, (unsigned long long)ino);
+		kdb_printf(" namelen %d name \"", sfe->namelen);
+		for (j = 0; j < sfe->namelen; j++)
+			kdb_printf("%c", sfe->name[j]);
+		kdb_printf("\"\n");
+		sfe = XFS_DIR_SF_NEXTENTRY(sfe);
+	}
+}
+
+/*
+ * Print a shortform v2 directory.
+ */
+static void
+xfsidbg_xdir2sf(xfs_dir2_sf_t *s)
+{
+	xfs_dir2_sf_hdr_t *sfh;
+	xfs_dir2_sf_entry_t *sfe;
+	xfs_ino_t ino;
+	int i, j;
+
+	sfh = &s->hdr;
+	ino = XFS_DIR2_SF_GET_INUMBER_ARCH(s, &sfh->parent, ARCH_CONVERT);
+	kdb_printf("hdr count %d i8count %d parent %llu\n",
+		sfh->count, sfh->i8count, (unsigned long long) ino);
+	for (i = 0, sfe = XFS_DIR2_SF_FIRSTENTRY(s); i < sfh->count; i++) {
+		ino = XFS_DIR2_SF_GET_INUMBER_ARCH(s, XFS_DIR2_SF_INUMBERP(sfe), ARCH_CONVERT);
+		kdb_printf("entry %d inumber %llu offset 0x%x namelen %d name \"",
+			i, (unsigned long long) ino,
+			XFS_DIR2_SF_GET_OFFSET_ARCH(sfe, ARCH_CONVERT),
+			sfe->namelen);
+		for (j = 0; j < sfe->namelen; j++)
+			kdb_printf("%c", sfe->name[j]);
+		kdb_printf("\"\n");
+		sfe = XFS_DIR2_SF_NEXTENTRY(s, sfe);
+	}
+}
+
+/*
+ * Print a node-form v2 directory freemap block.
+ */
+static void
+xfsidbg_xdir2free(xfs_dir2_free_t *f)
+{
+	int	i;
+
+	kdb_printf("hdr magic 0x%x firstdb %d nvalid %d nused %d\n",
+		INT_GET(f->hdr.magic, ARCH_CONVERT), INT_GET(f->hdr.firstdb, ARCH_CONVERT), INT_GET(f->hdr.nvalid, ARCH_CONVERT), INT_GET(f->hdr.nused, ARCH_CONVERT));
+	for (i = 0; i < INT_GET(f->hdr.nvalid, ARCH_CONVERT); i++) {
+		kdb_printf("entry %d db %d count %d\n",
+			i, i + INT_GET(f->hdr.firstdb, ARCH_CONVERT), INT_GET(f->bests[i], ARCH_CONVERT));
+	}
+}
+
+
+/*
+ * Print xfs extent list.
+ */
+static void
+xfsidbg_xexlist(xfs_inode_t *ip)
+{
+	xfs_xexlist_fork(ip, XFS_DATA_FORK);
+	if (XFS_IFORK_Q(ip))
+		xfs_xexlist_fork(ip, XFS_ATTR_FORK);
+}
+
+/*
+ * Print an xfs free-extent list.
+ */
+static void
+xfsidbg_xflist(xfs_bmap_free_t *flist)
+{
+	xfs_bmap_free_item_t	*item;
+
+	kdb_printf("flist@0x%p: first 0x%p count %d low %d\n", flist,
+		flist->xbf_first, flist->xbf_count, flist->xbf_low);
+	for (item = flist->xbf_first; item; item = item->xbfi_next) {
+		kdb_printf("item@0x%p: startblock %Lx blockcount %d", item,
+			(xfs_dfsbno_t)item->xbfi_startblock,
+			item->xbfi_blockcount);
+	}
+}
+
+/*
+ * Print out the help messages for these functions.
+ */
+static void
+xfsidbg_xhelp(void)
+{
+	struct xif	*p;
+
+	for (p = xfsidbg_funcs; p->name; p++)
+		kdb_printf("%-16s %s %s\n", p->name, p->args, p->help);
+}
+
+/*
+ * Print out an XFS in-core log structure.
+ */
+static void
+xfsidbg_xiclog(xlog_in_core_t *iclog)
+{
+	int i;
+	static char *ic_flags[] = {
+		"ACTIVE",	/* 0x0001 */
+		"WANT_SYNC",	/* 0x0002 */
+		"SYNCING",	/* 0X0004 */
+		"DONE_SYNC",	/* 0X0008 */
+		"DO_CALLBACK",	/* 0X0010 */
+		"CALLBACK",	/* 0X0020 */
+		"DIRTY",	/* 0X0040 */
+		"IOERROR",	/* 0X0080 */
+		"NOTUSED",	/* 0X8000 */
+		0
+	};
+
+	kdb_printf("xlog_in_core/header at 0x%p/0x%p\n",
+		iclog, iclog->hic_data);
+	kdb_printf("magicno: %x	 cycle: %d  version: %d	 lsn: 0x%Lx\n",
+		INT_GET(iclog->ic_header.h_magicno, ARCH_CONVERT), INT_GET(iclog->ic_header.h_cycle, ARCH_CONVERT),
+		INT_GET(iclog->ic_header.h_version, ARCH_CONVERT), INT_GET(iclog->ic_header.h_lsn, ARCH_CONVERT));
+	kdb_printf("tail_lsn: 0x%Lx  len: %d  prev_block: %d  num_ops: %d\n",
+		INT_GET(iclog->ic_header.h_tail_lsn, ARCH_CONVERT), INT_GET(iclog->ic_header.h_len, ARCH_CONVERT),
+		INT_GET(iclog->ic_header.h_prev_block, ARCH_CONVERT), INT_GET(iclog->ic_header.h_num_logops, ARCH_CONVERT));
+	kdb_printf("cycle_data: ");
+	for (i=0; i<(iclog->ic_size>>BBSHIFT); i++) {
+		kdb_printf("%x	", INT_GET(iclog->ic_header.h_cycle_data[i], ARCH_CONVERT));
+	}
+	kdb_printf("\n");
+	kdb_printf("size: %d\n", INT_GET(iclog->ic_header.h_size, ARCH_CONVERT));
+	kdb_printf("\n");
+	kdb_printf("--------------------------------------------------\n");
+	kdb_printf("data: 0x%p	&forcesema: 0x%p  next: 0x%p bp: 0x%p\n",
+		iclog->ic_datap, &iclog->ic_forcesema, iclog->ic_next,
+		iclog->ic_bp);
+	kdb_printf("log: 0x%p  callb: 0x%p  callb_tail: 0x%p  roundoff: %d\n",
+		iclog->ic_log, iclog->ic_callback, iclog->ic_callback_tail,
+		iclog->ic_roundoff);
+	kdb_printf("size: %d (OFFSET: %d) refcnt: %d  bwritecnt: %d",
+		iclog->ic_size, iclog->ic_offset,
+		iclog->ic_refcnt, iclog->ic_bwritecnt);
+	if (iclog->ic_state & XLOG_STATE_ALL)
+		printflags(iclog->ic_state, ic_flags, "state:");
+	else
+		kdb_printf("state: ILLEGAL 0x%x", iclog->ic_state);
+	kdb_printf("\n");
+}	/* xfsidbg_xiclog */
+
+
+/*
+ * Print all incore logs.
+ */
+static void
+xfsidbg_xiclogall(xlog_in_core_t *iclog)
+{
+    xlog_in_core_t *first_iclog = iclog;
+
+    do {
+	xfsidbg_xiclog(iclog);
+	kdb_printf("=================================================\n");
+	iclog = iclog->ic_next;
+    } while (iclog != first_iclog);
+}	/* xfsidbg_xiclogall */
+
+/*
+ * Print out the callback structures attached to an iclog.
+ */
+static void
+xfsidbg_xiclogcb(xlog_in_core_t *iclog)
+{
+	xfs_log_callback_t	*cb;
+	kdb_symtab_t		 symtab;
+
+	for (cb = iclog->ic_callback; cb != NULL; cb = cb->cb_next) {
+
+		if (kdbnearsym((unsigned long)cb->cb_func, &symtab)) {
+			unsigned long offval;
+
+			offval = (unsigned long)cb->cb_func - symtab.sym_start;
+
+			if (offval)
+				kdb_printf("func = %s+0x%lx",
+							symtab.sym_name,
+							offval);
+			else
+				kdb_printf("func = %s", symtab.sym_name);
+		} else
+			kdb_printf("func = ?? 0x%p", (void *)cb->cb_func);
+
+		kdb_printf(" arg 0x%p next 0x%p\n", cb->cb_arg, cb->cb_next);
+	}
+}
+
+
+/*
+ * Print all of the inodes attached to the given mount structure.
+ */
+static void
+xfsidbg_xinodes(xfs_mount_t *mp)
+{
+	xfs_inode_t	*ip;
+
+	kdb_printf("xfs_mount at 0x%p\n", mp);
+	ip = mp->m_inodes;
+	if (ip != NULL) {
+		do {
+			if (ip->i_mount == NULL) {
+				ip = ip->i_mnext;
+				continue;
+			}
+			kdb_printf("\n");
+			xfsidbg_xnode(ip);
+			ip = ip->i_mnext;
+		} while (ip != mp->m_inodes);
+	}
+	kdb_printf("\nEnd of Inodes\n");
+}
+
+static void
+xfsidbg_delayed_blocks(xfs_mount_t *mp)
+{
+	xfs_inode_t	*ip;
+	unsigned int	total = 0;
+	unsigned int	icount = 0;
+
+	ip = mp->m_inodes;
+	if (ip != NULL) {
+		do {
+			if (ip->i_mount == NULL) {
+				ip = ip->i_mnext;
+				continue;
+			}
+			if (ip->i_delayed_blks) {
+				total += ip->i_delayed_blks;
+				icount++;
+			}
+			ip = ip->i_mnext;
+		} while (ip != mp->m_inodes);
+	}
+	kdb_printf("delayed blocks total: %d in %d inodes\n", total, icount);
+}
+
+static void
+xfsidbg_xinodes_quiesce(xfs_mount_t *mp)
+{
+	xfs_inode_t	*ip;
+
+	kdb_printf("xfs_mount at 0x%p\n", mp);
+	ip = mp->m_inodes;
+	if (ip != NULL) {
+		do {
+			if (ip->i_mount == NULL) {
+				ip = ip->i_mnext;
+				continue;
+			}
+			if (!(ip->i_flags & XFS_IQUIESCE)) {
+				kdb_printf("ip 0x%p not quiesced\n", ip);
+			} else if (!BHV_IS_WRITE_LOCKED(VN_BHV_HEAD(XFS_ITOV(ip)))) {
+				kdb_printf("ip 0x%p not write locked\n", ip);
+			}
+			ip = ip->i_mnext;
+		} while (ip != mp->m_inodes);
+	}
+	kdb_printf("\nEnd of Inodes\n");
+}
+
+static char *
+xfsidbg_get_cstate(int state)
+{
+	switch(state) {
+	case  XLOG_STATE_COVER_IDLE:
+		return("idle");
+	case  XLOG_STATE_COVER_NEED:
+		return("need");
+	case  XLOG_STATE_COVER_DONE:
+		return("done");
+	case  XLOG_STATE_COVER_NEED2:
+		return("need2");
+	case  XLOG_STATE_COVER_DONE2:
+		return("done2");
+	default:
+		return("unknown");
+	}
+}
+
+/*
+ * Print out an XFS log structure.
+ */
+static void
+xfsidbg_xlog(xlog_t *log)
+{
+	int rbytes;
+	int wbytes;
+	static char *t_flags[] = {
+		"CHKSUM_MISMATCH",	/* 0x01 */
+		"ACTIVE_RECOVERY",	/* 0x02 */
+		"RECOVERY_NEEDED",	/* 0x04 */
+		"IO_ERROR",		/* 0x08 */
+		0
+	};
+
+	kdb_printf("xlog at 0x%p\n", log);
+	kdb_printf("&flushsm: 0x%p  tic_cnt: %d	 tic_tcnt: %d  \n",
+		&log->l_flushsema, log->l_ticket_cnt, log->l_ticket_tcnt);
+	kdb_printf("freelist: 0x%p  tail: 0x%p	ICLOG: 0x%p  \n",
+		log->l_freelist, log->l_tail, log->l_iclog);
+	kdb_printf("&icloglock: 0x%p  tail_lsn: %s  last_sync_lsn: %s \n",
+		&log->l_icloglock, xfs_fmtlsn(&log->l_tail_lsn),
+		xfs_fmtlsn(&log->l_last_sync_lsn));
+	kdb_printf("mp: 0x%p  xbuf: 0x%p  roundoff: %d	l_covered_state: %s \n",
+		log->l_mp, log->l_xbuf, log->l_roundoff,
+			xfsidbg_get_cstate(log->l_covered_state));
+	kdb_printf("flags: ");
+	printflags(log->l_flags, t_flags,"log");
+	kdb_printf("  dev: 0x%x logBBstart: %lld logsize: %d logBBsize: %d\n",
+		log->l_dev, (long long) log->l_logBBstart,
+		log->l_logsize,log->l_logBBsize);
+	kdb_printf("curr_cycle: %d  prev_cycle: %d  curr_block: %d  prev_block: %d\n",
+	     log->l_curr_cycle, log->l_prev_cycle, log->l_curr_block,
+	     log->l_prev_block);
+	kdb_printf("iclog_bak: 0x%p  iclog_size: 0x%x (%d)  num iclogs: %d\n",
+		log->l_iclog_bak, log->l_iclog_size, log->l_iclog_size,
+		log->l_iclog_bufs);
+	kdb_printf("l_iclog_hsize %d l_iclog_heads %d\n",
+		log->l_iclog_hsize, log->l_iclog_heads);
+	kdb_printf("&grant_lock: 0x%p  resHeadQ: 0x%p  wrHeadQ: 0x%p\n",
+		&log->l_grant_lock, log->l_reserve_headq, log->l_write_headq);
+	kdb_printf("GResCycle: %d  GResBytes: %d  GWrCycle: %d	GWrBytes: %d\n",
+		log->l_grant_reserve_cycle, log->l_grant_reserve_bytes,
+		log->l_grant_write_cycle, log->l_grant_write_bytes);
+	rbytes = log->l_grant_reserve_bytes + log->l_roundoff;
+	wbytes = log->l_grant_write_bytes + log->l_roundoff;
+       kdb_printf("GResBlocks: %d  GResRemain: %d  GWrBlocks: %d  GWrRemain: %d\n",
+	       rbytes / BBSIZE, rbytes % BBSIZE,
+	       wbytes / BBSIZE, wbytes % BBSIZE);
+}	/* xfsidbg_xlog */
+
+
+/*
+ * Print out an XFS recovery transaction
+ */
+static void
+xfsidbg_xlog_ritem(xlog_recover_item_t *item)
+{
+	int i = XLOG_MAX_REGIONS_IN_ITEM;
+
+	kdb_printf("(xlog_recover_item 0x%p) ", item);
+	kdb_printf("next: 0x%p prev: 0x%p type: %d cnt: %d ttl: %d\n",
+		item->ri_next, item->ri_prev, ITEM_TYPE(item), item->ri_cnt,
+		item->ri_total);
+	for ( ; i > 0; i--) {
+		if (!item->ri_buf[XLOG_MAX_REGIONS_IN_ITEM-i].i_addr)
+			break;
+		kdb_printf("a: 0x%p l: %d ",
+			item->ri_buf[XLOG_MAX_REGIONS_IN_ITEM-i].i_addr,
+			item->ri_buf[XLOG_MAX_REGIONS_IN_ITEM-i].i_len);
+	}
+	kdb_printf("\n");
+}	/* xfsidbg_xlog_ritem */
+
+/*
+ * Print out an XFS recovery transaction
+ */
+static void
+xfsidbg_xlog_rtrans(xlog_recover_t *trans)
+{
+	xlog_recover_item_t *rip, *first_rip;
+
+	kdb_printf("(xlog_recover 0x%p) ", trans);
+	kdb_printf("tid: %x type: %d items: %d ttid: 0x%x  ",
+		trans->r_log_tid, trans->r_theader.th_type,
+		trans->r_theader.th_num_items, trans->r_theader.th_tid);
+	kdb_printf("itemq: 0x%p\n", trans->r_itemq);
+	if (trans->r_itemq) {
+		rip = first_rip = trans->r_itemq;
+		do {
+			kdb_printf("(recovery item: 0x%p) ", rip);
+			kdb_printf("type: %d cnt: %d total: %d\n",
+				ITEM_TYPE(rip), rip->ri_cnt, rip->ri_total);
+			rip = rip->ri_next;
+		} while (rip != first_rip);
+	}
+}	/* xfsidbg_xlog_rtrans */
+
+static void
+xfsidbg_xlog_buf_logitem(xlog_recover_item_t *item)
+{
+	xfs_buf_log_format_t	*buf_f;
+	int			i, j;
+	int			bit;
+	int			nbits;
+	unsigned int		*data_map;
+	unsigned int		map_size;
+	int			size;
+
+	buf_f = (xfs_buf_log_format_t *)item->ri_buf[0].i_addr;
+	if (buf_f->blf_flags & XFS_BLI_INODE_BUF) {
+		kdb_printf("\tINODE BUF <blkno=0x%Lx, len=0x%x>\n",
+			buf_f->blf_blkno, buf_f->blf_len);
+	} else if (buf_f->blf_flags & (XFS_BLI_UDQUOT_BUF | XFS_BLI_GDQUOT_BUF)) {
+		kdb_printf("\tDQUOT BUF <blkno=0x%Lx, len=0x%x>\n",
+			buf_f->blf_blkno, buf_f->blf_len);
+	} else {
+		data_map = buf_f->blf_data_map;
+		map_size = buf_f->blf_map_size;
+		kdb_printf("\tREG BUF <blkno=0x%Lx, len=0x%x map 0x%p size %d>\n",
+			buf_f->blf_blkno, buf_f->blf_len, data_map, map_size);
+		bit = 0;
+		i = 0;  /* 0 is the buf format structure */
+		while (1) {
+			bit = xfs_next_bit(data_map, map_size, bit);
+			if (bit == -1)
+				break;
+			nbits = xfs_contig_bits(data_map, map_size, bit);
+			size = ((uint)bit << XFS_BLI_SHIFT)+(nbits<<XFS_BLI_SHIFT);
+			kdb_printf("\t\tlogbuf.i_addr 0x%p, size 0x%x\n",
+				item->ri_buf[i].i_addr, size);
+			kdb_printf("\t\t\t\"");
+			for (j=0; j<8 && j<size; j++) {
+				kdb_printf("%02x", ((char *)item->ri_buf[i].i_addr)[j]);
+			}
+			kdb_printf("...\"\n");
+			i++;
+			bit += nbits;
+		}
+
+	}
+}
+
+/*
+ * Print out an ENTIRE XFS recovery transaction
+ */
+static void
+xfsidbg_xlog_rtrans_entire(xlog_recover_t *trans)
+{
+	xlog_recover_item_t *item, *first_rip;
+
+	kdb_printf("(Recovering Xact 0x%p) ", trans);
+	kdb_printf("tid: %x type: %d nitems: %d ttid: 0x%x  ",
+		trans->r_log_tid, trans->r_theader.th_type,
+		trans->r_theader.th_num_items, trans->r_theader.th_tid);
+	kdb_printf("itemq: 0x%p\n", trans->r_itemq);
+	if (trans->r_itemq) {
+		item = first_rip = trans->r_itemq;
+		do {
+			/*
+			   kdb_printf("(recovery item: 0x%x) ", item);
+			   kdb_printf("type: %d cnt: %d total: %d\n",
+				   item->ri_type, item->ri_cnt, item->ri_total);
+				   */
+			if ((ITEM_TYPE(item) == XFS_LI_BUF) ||
+			    (ITEM_TYPE(item) == XFS_LI_6_1_BUF) ||
+			    (ITEM_TYPE(item) == XFS_LI_5_3_BUF)) {
+				kdb_printf("BUF:");
+				xfsidbg_xlog_buf_logitem(item);
+			} else if ((ITEM_TYPE(item) == XFS_LI_INODE) ||
+				   (ITEM_TYPE(item) == XFS_LI_6_1_INODE) ||
+				   (ITEM_TYPE(item) == XFS_LI_5_3_INODE)) {
+				kdb_printf("INODE:\n");
+			} else if (ITEM_TYPE(item) == XFS_LI_EFI) {
+				kdb_printf("EFI:\n");
+			} else if (ITEM_TYPE(item) == XFS_LI_EFD) {
+				kdb_printf("EFD:\n");
+			} else if (ITEM_TYPE(item) == XFS_LI_DQUOT) {
+				kdb_printf("DQUOT:\n");
+			} else if ((ITEM_TYPE(item) == XFS_LI_QUOTAOFF)) {
+				kdb_printf("QUOTAOFF:\n");
+			} else {
+				kdb_printf("UNKNOWN LOGITEM 0x%x\n", ITEM_TYPE(item));
+			}
+			item = item->ri_next;
+		} while (item != first_rip);
+	}
+}	/* xfsidbg_xlog_rtrans */
+
+/*
+ * Print out an XFS ticket structure.
+ */
+static void
+xfsidbg_xlog_tic(xlog_ticket_t *tic)
+{
+	static char *t_flags[] = {
+		"INIT",		/* 0x1 */
+		"PERM_RES",	/* 0x2 */
+		"IN_Q",		/* 0x4 */
+		0
+	};
+
+	kdb_printf("xlog_ticket at 0x%p\n", tic);
+	kdb_printf("next: 0x%p	prev: 0x%p  tid: 0x%x  \n",
+		tic->t_next, tic->t_prev, tic->t_tid);
+	kdb_printf("curr_res: %d  unit_res: %d	ocnt: %d  cnt: %d\n",
+		tic->t_curr_res, tic->t_unit_res, (int)tic->t_ocnt,
+		(int)tic->t_cnt);
+	kdb_printf("clientid: %c  \n", tic->t_clientid);
+	printflags(tic->t_flags, t_flags,"ticket");
+	kdb_printf("\n");
+}	/* xfsidbg_xlog_tic */
+
+/*
+ * Print out a single log item.
+ */
+static void
+xfsidbg_xlogitem(xfs_log_item_t *lip)
+{
+	xfs_log_item_t	*bio_lip;
+	static char *lid_type[] = {
+		"???",		/* 0 */
+		"5-3-buf",	/* 1 */
+		"5-3-inode",	/* 2 */
+		"efi",		/* 3 */
+		"efd",		/* 4 */
+		"iunlink",	/* 5 */
+		"6-1-inode",	/* 6 */
+		"6-1-buf",	/* 7 */
+		"inode",	/* 8 */
+		"buf",		/* 9 */
+		"dquot",	/* 10 */
+		0
+		};
+	static char *li_flags[] = {
+		"in ail",	/* 0x1 */
+		0
+		};
+
+	kdb_printf("type %s mountp 0x%p flags ",
+		lid_type[lip->li_type - XFS_LI_5_3_BUF + 1],
+		lip->li_mountp);
+	printflags((uint)(lip->li_flags), li_flags,"log");
+	kdb_printf("\n");
+	kdb_printf("ail forw 0x%p ail back 0x%p lsn %s desc %p ops 0x%p\n",
+		lip->li_ail.ail_forw, lip->li_ail.ail_back,
+		xfs_fmtlsn(&(lip->li_lsn)), lip->li_desc, lip->li_ops);
+	kdb_printf("iodonefunc &0x%p\n", lip->li_cb);
+	if (lip->li_type == XFS_LI_BUF) {
+		bio_lip = lip->li_bio_list;
+		if (bio_lip != NULL) {
+			kdb_printf("iodone list:\n");
+		}
+		while (bio_lip != NULL) {
+			kdb_printf("item 0x%p func 0x%p\n",
+				bio_lip, bio_lip->li_cb);
+			bio_lip = bio_lip->li_bio_list;
+		}
+	}
+	switch (lip->li_type) {
+	case XFS_LI_BUF:
+		xfs_buf_item_print((xfs_buf_log_item_t *)lip, 0);
+		break;
+	case XFS_LI_INODE:
+		xfs_inode_item_print((xfs_inode_log_item_t *)lip, 0);
+		break;
+	case XFS_LI_EFI:
+		xfs_efi_item_print((xfs_efi_log_item_t *)lip, 0);
+		break;
+	case XFS_LI_EFD:
+		xfs_efd_item_print((xfs_efd_log_item_t *)lip, 0);
+		break;
+	case XFS_LI_DQUOT:
+		xfs_dquot_item_print((xfs_dq_logitem_t *)lip, 0);
+		break;
+	case XFS_LI_QUOTAOFF:
+		xfs_qoff_item_print((xfs_qoff_logitem_t *)lip, 0);
+		break;
+
+	default:
+		kdb_printf("Unknown item type %d\n", lip->li_type);
+		break;
+	}
+}
+
+/*
+ * Print out a summary of the AIL hanging off of a mount struct.
+ */
+static void
+xfsidbg_xaildump(xfs_mount_t *mp)
+{
+	xfs_log_item_t *lip;
+	static char *lid_type[] = {
+		"???",		/* 0 */
+		"5-3-buf",	/* 1 */
+		"5-3-inode",	/* 2 */
+		"efi",		/* 3 */
+		"efd",		/* 4 */
+		"iunlink",	/* 5 */
+		"6-1-inode",	/* 6 */
+		"6-1-buf",	/* 7 */
+		"inode",	/* 8 */
+		"buf",		/* 9 */
+		"dquot",	/* 10 */
+		0
+		};
+	static char *li_flags[] = {
+		"in ail",	/* 0x1 */
+		0
+		};
+	int count;
+
+	if ((mp->m_ail.ail_forw == NULL) ||
+	    (mp->m_ail.ail_forw == (xfs_log_item_t *)&mp->m_ail)) {
+		kdb_printf("AIL is empty\n");
+		return;
+	}
+	kdb_printf("AIL for mp 0x%p, oldest first\n", mp);
+	lip = (xfs_log_item_t*)mp->m_ail.ail_forw;
+	for (count = 0; lip; count++) {
+		kdb_printf("[%d] type %s ", count,
+			      lid_type[lip->li_type - XFS_LI_5_3_BUF + 1]);
+		printflags((uint)(lip->li_flags), li_flags, "flags:");
+		kdb_printf("  lsn %s\n	 ", xfs_fmtlsn(&(lip->li_lsn)));
+		switch (lip->li_type) {
+		case XFS_LI_BUF:
+			xfs_buf_item_print((xfs_buf_log_item_t *)lip, 1);
+			break;
+		case XFS_LI_INODE:
+			xfs_inode_item_print((xfs_inode_log_item_t *)lip, 1);
+			break;
+		case XFS_LI_EFI:
+			xfs_efi_item_print((xfs_efi_log_item_t *)lip, 1);
+			break;
+		case XFS_LI_EFD:
+			xfs_efd_item_print((xfs_efd_log_item_t *)lip, 1);
+			break;
+		case XFS_LI_DQUOT:
+			xfs_dquot_item_print((xfs_dq_logitem_t *)lip, 1);
+			break;
+		case XFS_LI_QUOTAOFF:
+			xfs_qoff_item_print((xfs_qoff_logitem_t *)lip, 1);
+			break;
+		default:
+			kdb_printf("Unknown item type %d\n", lip->li_type);
+			break;
+		}
+
+		if (lip->li_ail.ail_forw == (xfs_log_item_t*)&mp->m_ail) {
+			lip = NULL;
+		} else {
+			lip = lip->li_ail.ail_forw;
+		}
+	}
+}
+
+/*
+ * Print xfs mount structure.
+ */
+static void
+xfsidbg_xmount(xfs_mount_t *mp)
+{
+	static char *xmount_flags[] = {
+		"WSYNC",	/* 0x0001 */
+		"INO64",	/* 0x0002 */
+		"RQCHK",	/* 0x0004 */
+		"FSCLEAN",	/* 0x0008 */
+		"FSSHUTDN",	/* 0x0010 */
+		"NOATIME",	/* 0x0020 */
+		"RETERR",	/* 0x0040 */
+		"NOALIGN",	/* 0x0080 */
+		"UNSHRD",	/* 0x0100 */
+		"RGSTRD",	/* 0x0200 */
+		"NORECVR",	/* 0x0400 */
+		"SHRD",		/* 0x0800 */
+		"IOSZ",		/* 0x1000 */
+		"OSYNC",	/* 0x2000 */
+		"NOUUID",	/* 0x4000 */
+		"32BIT",	/* 0x8000 */
+		"IRIXSGID",	/* 0x10000 */
+		"NOLOGFLUSH",	/* 0x20000 */
+		0
+	};
+
+	static char *quota_flags[] = {
+		"UQ",		/* 0x0001 */
+		"UQE",		/* 0x0002 */
+		"UQCHKD",	/* 0x0004 */
+		"PQ",		/* 0x0008 (IRIX ondisk) */
+		"GQE",		/* 0x0010 */
+		"GQCHKD",	/* 0x0020 */
+		"GQ",		/* 0x0040 */
+		"UQACTV",	/* 0x0080 */
+		"GQACTV",	/* 0x0100 */
+		"QMAYBE",	/* 0x0200 */
+		0
+	};
+
+	kdb_printf("xfs_mount at 0x%p\n", mp);
+	kdb_printf("vfsp 0x%p tid 0x%x ail_lock 0x%p &ail 0x%p\n",
+		XFS_MTOVFS(mp), mp->m_tid, &mp->m_ail_lock, &mp->m_ail);
+	kdb_printf("ail_gen 0x%x &sb 0x%p\n",
+		mp->m_ail_gen, &mp->m_sb);
+	kdb_printf("sb_lock 0x%p sb_bp 0x%p dev 0x%x logdev 0x%x rtdev 0x%x\n",
+		&mp->m_sb_lock, mp->m_sb_bp,
+		mp->m_ddev_targp->pbr_dev,
+		mp->m_logdev_targp->pbr_dev,
+		mp->m_rtdev_targp->pbr_dev);
+	kdb_printf("bsize %d agfrotor %d agirotor %d ihash 0x%p ihsize %d\n",
+		mp->m_bsize, mp->m_agfrotor, mp->m_agirotor,
+		mp->m_ihash, mp->m_ihsize);
+	kdb_printf("inodes 0x%p ilock 0x%p ireclaims 0x%x\n",
+		mp->m_inodes, &mp->m_ilock, mp->m_ireclaims);
+	kdb_printf("readio_log 0x%x readio_blocks 0x%x ",
+		mp->m_readio_log, mp->m_readio_blocks);
+	kdb_printf("writeio_log 0x%x writeio_blocks 0x%x\n",
+		mp->m_writeio_log, mp->m_writeio_blocks);
+	kdb_printf("logbufs %d logbsize %d LOG 0x%p\n", mp->m_logbufs,
+		mp->m_logbsize, mp->m_log);
+	kdb_printf("rsumlevels 0x%x rsumsize 0x%x rbmip 0x%p rsumip 0x%p\n",
+		mp->m_rsumlevels, mp->m_rsumsize, mp->m_rbmip, mp->m_rsumip);
+	kdb_printf("rootip 0x%p\n", mp->m_rootip);
+	kdb_printf("dircook_elog %d blkbit_log %d blkbb_log %d agno_log %d\n",
+		mp->m_dircook_elog, mp->m_blkbit_log, mp->m_blkbb_log,
+		mp->m_agno_log);
+	kdb_printf("agino_log %d nreadaheads %d inode cluster size %d\n",
+		mp->m_agino_log, mp->m_nreadaheads,
+		mp->m_inode_cluster_size);
+	kdb_printf("blockmask 0x%x blockwsize 0x%x blockwmask 0x%x\n",
+		mp->m_blockmask, mp->m_blockwsize, mp->m_blockwmask);
+	kdb_printf("alloc_mxr[lf,nd] %d %d alloc_mnr[lf,nd] %d %d\n",
+		mp->m_alloc_mxr[0], mp->m_alloc_mxr[1],
+		mp->m_alloc_mnr[0], mp->m_alloc_mnr[1]);
+	kdb_printf("bmap_dmxr[lfnr,ndnr] %d %d bmap_dmnr[lfnr,ndnr] %d %d\n",
+		mp->m_bmap_dmxr[0], mp->m_bmap_dmxr[1],
+		mp->m_bmap_dmnr[0], mp->m_bmap_dmnr[1]);
+	kdb_printf("inobt_mxr[lf,nd] %d %d inobt_mnr[lf,nd] %d %d\n",
+		mp->m_inobt_mxr[0], mp->m_inobt_mxr[1],
+		mp->m_inobt_mnr[0], mp->m_inobt_mnr[1]);
+	kdb_printf("ag_maxlevels %d bm_maxlevels[d,a] %d %d in_maxlevels %d\n",
+		mp->m_ag_maxlevels, mp->m_bm_maxlevels[0],
+		mp->m_bm_maxlevels[1], mp->m_in_maxlevels);
+	kdb_printf("perag 0x%p &peraglock 0x%p &growlock 0x%p\n",
+		mp->m_perag, &mp->m_peraglock, &mp->m_growlock);
+	printflags(mp->m_flags, xmount_flags,"flags");
+	kdb_printf("ialloc_inos %d ialloc_blks %d litino %d\n",
+		mp->m_ialloc_inos, mp->m_ialloc_blks, mp->m_litino);
+	kdb_printf("attroffset %d da_node_ents %d maxicount %Ld inoalign_mask %d\n",
+		mp->m_attroffset, mp->m_da_node_ents, mp->m_maxicount,
+		mp->m_inoalign_mask);
+	kdb_printf("resblks %Ld resblks_avail %Ld\n", mp->m_resblks,
+		mp->m_resblks_avail);
+#if XFS_BIG_FILESYSTEMS
+	kdb_printf(" inoadd %llx\n", (unsigned long long) mp->m_inoadd);
+#else
+	kdb_printf("\n");
+#endif
+	if (mp->m_quotainfo)
+		kdb_printf("quotainfo 0x%p (uqip = 0x%p, gqip = 0x%p)\n",
+			mp->m_quotainfo,
+			mp->m_quotainfo->qi_uquotaip,
+			mp->m_quotainfo->qi_gquotaip);
+	else
+		kdb_printf("quotainfo NULL\n");
+	printflags(mp->m_qflags, quota_flags,"quotaflags");
+	kdb_printf("\n");
+	kdb_printf("dalign %d swidth %d sinoalign %d attr_magicpct %d dir_magicpct %d\n",
+		mp->m_dalign, mp->m_swidth, mp->m_sinoalign,
+		mp->m_attr_magicpct, mp->m_dir_magicpct);
+	kdb_printf("mk_sharedro %d dirversion %d dirblkfsbs %d &dirops 0x%p\n",
+		mp->m_mk_sharedro, mp->m_dirversion, mp->m_dirblkfsbs,
+		&mp->m_dirops);
+	kdb_printf("dirblksize %d dirdatablk 0x%Lx dirleafblk 0x%Lx dirfreeblk 0x%Lx\n",
+		mp->m_dirblksize,
+		(xfs_dfiloff_t)mp->m_dirdatablk,
+		(xfs_dfiloff_t)mp->m_dirleafblk,
+		(xfs_dfiloff_t)mp->m_dirfreeblk);
+	kdb_printf("chsize %d chash 0x%p\n",
+		mp->m_chsize, mp->m_chash);
+	kdb_printf("m_lstripemask %d\n", mp->m_lstripemask);
+	kdb_printf("m_frozen %d m_active_trans %d\n",
+		mp->m_frozen, mp->m_active_trans.counter);
+	if (mp->m_fsname != NULL)
+		kdb_printf("mountpoint \"%s\"\n", mp->m_fsname);
+	else
+		kdb_printf("No name!!!\n");
+
+}
+
+static void
+xfsidbg_xihash(xfs_mount_t *mp)
+{
+	xfs_ihash_t	*ih;
+	int		i;
+	int		j;
+	int		total;
+	int		numzeros;
+	xfs_inode_t	*ip;
+	int		*hist;
+	int		hist_bytes = mp->m_ihsize * sizeof(int);
+	int		hist2[21];
+
+	hist = (int *) kmalloc(hist_bytes, GFP_KERNEL);
+
+	if (hist == NULL) {
+		kdb_printf("xfsidbg_xihash: kmalloc(%d) failed!\n",
+							hist_bytes);
+		return;
+	}
+
+	for (i = 0; i < mp->m_ihsize; i++) {
+		ih = mp->m_ihash + i;
+		j = 0;
+		for (ip = ih->ih_next; ip != NULL; ip = ip->i_next)
+			j++;
+		hist[i] = j;
+	}
+
+	numzeros = total = 0;
+
+	for (i = 0; i < 21; i++)
+		hist2[i] = 0;
+
+	for (i = 0; i < mp->m_ihsize; i++)  {
+		kdb_printf("%d ", hist[i]);
+		total += hist[i];
+		numzeros += hist[i] == 0 ? 1 : 0;
+		if (hist[i] > 20)
+			j = 20;
+		else
+			j = hist[i];
+
+		if (! (j <= 20)) {
+			kdb_printf("xfsidbg_xihash: (j > 20)/%d @ line # %d\n",
+							j, __LINE__);
+			return;
+		}
+
+		hist2[j]++;
+	}
+
+	kdb_printf("\n");
+
+	kdb_printf("total inodes = %d, average length = %d, adjusted average = %d \n",
+		total, total / mp->m_ihsize,
+		total / (mp->m_ihsize - numzeros));
+
+	for (i = 0; i < 21; i++)  {
+		kdb_printf("%d - %d , ", i, hist2[i]);
+	}
+	kdb_printf("\n");
+	kfree(hist);
+}
+
+/*
+ * Command to print xfs inodes: kp xnode <addr>
+ */
+static void
+xfsidbg_xnode(xfs_inode_t *ip)
+{
+	static char *tab_flags[] = {
+		"grio",		/* XFS_IGRIO */
+		"uiosize",	/* XFS_IUIOSZ */
+		"quiesce",	/* XFS_IQUIESCE */
+		"reclaim",	/* XFS_IRECLAIM */
+		NULL
+	};
+
+	kdb_printf("hash 0x%p next 0x%p prevp 0x%p mount 0x%p\n",
+		ip->i_hash,
+		ip->i_next,
+		ip->i_prevp,
+		ip->i_mount);
+	kdb_printf("mnext 0x%p mprev 0x%p vnode 0x%p \n",
+		ip->i_mnext,
+		ip->i_mprev,
+		XFS_ITOV_NULL(ip));
+	kdb_printf("dev %x ino %s\n",
+		ip->i_mount->m_dev,
+		xfs_fmtino(ip->i_ino, ip->i_mount));
+	kdb_printf("blkno 0x%llx len 0x%x boffset 0x%x\n",
+		(long long) ip->i_blkno,
+		ip->i_len,
+		ip->i_boffset);
+	kdb_printf("transp 0x%p &itemp 0x%p\n",
+		ip->i_transp,
+		ip->i_itemp);
+	kdb_printf("&lock 0x%p &iolock 0x%p",
+		&ip->i_lock,
+		&ip->i_iolock);
+	kdb_printf("&flock 0x%p (%d) pincount 0x%x\n",
+		&ip->i_flock, valusema(&ip->i_flock),
+		xfs_ipincount(ip));
+	kdb_printf("udquotp 0x%p gdquotp 0x%p\n",
+		ip->i_udquot, ip->i_gdquot);
+	kdb_printf("new_size %Lx\n", ip->i_iocore.io_new_size);
+	printflags((int)ip->i_flags, tab_flags, "flags");
+	kdb_printf("\n");
+	kdb_printf("update_core 0x%x update size 0x%x\n",
+		(int)(ip->i_update_core), (int) ip->i_update_size);
+	kdb_printf("gen 0x%x delayed blks %d",
+		ip->i_gen,
+		ip->i_delayed_blks);
+	kdb_printf("\n");
+	kdb_printf("chash 0x%p cnext 0x%p cprev 0x%p\n",
+		ip->i_chash,
+		ip->i_cnext,
+		ip->i_cprev);
+	xfs_xnode_fork("data", &ip->i_df);
+	xfs_xnode_fork("attr", ip->i_afp);
+	kdb_printf("\n");
+	xfs_prdinode_core(&ip->i_d, ARCH_NOCONVERT);
+}
+
+static void
+xfsidbg_xcore(xfs_iocore_t *io)
+{
+	if (IO_IS_XFS(io)) {
+		kdb_printf("io_obj 0x%p (xinode) io_mount 0x%p\n",
+			io->io_obj, io->io_mount);
+	} else {
+		kdb_printf("io_obj 0x%p (dcxvn) io_mount 0x%p\n",
+			io->io_obj, io->io_mount);
+	}
+	kdb_printf("new_size %Lx\n", io->io_new_size);
+}
+
+/*
+ * Command to print xfs inode cluster hash table: kp xchash <addr>
+ */
+static void
+xfsidbg_xchash(xfs_mount_t *mp)
+{
+	int		i;
+	xfs_chash_t	*ch;
+
+	kdb_printf("m_chash 0x%p size %d\n",
+		mp->m_chash, mp->m_chsize);
+	for (i = 0; i < mp->m_chsize; i++) {
+		ch = mp->m_chash + i;
+		kdb_printf("[%3d] ch 0x%p chashlist 0x%p\n", i, ch, ch->ch_list);
+		xfsidbg_xchashlist(ch->ch_list);
+	}
+}
+
+/*
+ * Command to print xfs inode cluster hash list: kp xchashlist <addr>
+ */
+static void
+xfsidbg_xchashlist(xfs_chashlist_t *chl)
+{
+	xfs_inode_t	*ip;
+
+	while (chl != NULL) {
+#ifdef DEBUG
+		kdb_printf("hashlist inode 0x%p blkno %Ld buf 0x%p",
+		       chl->chl_ip, chl->chl_blkno, chl->chl_buf);
+#else
+		kdb_printf("hashlist inode 0x%p blkno %lld",
+		       chl->chl_ip, (long long) chl->chl_blkno);
+#endif
+
+		kdb_printf("\n");
+
+		/* print inodes on chashlist */
+		ip = chl->chl_ip;
+		do {
+			kdb_printf("0x%p ", ip);
+			ip = ip->i_cnext;
+		} while (ip != chl->chl_ip);
+		kdb_printf("\n");
+
+		chl=chl->chl_next;
+	}
+}
+
+/*
+ * Print xfs per-ag data structures for filesystem.
+ */
+static void
+xfsidbg_xperag(xfs_mount_t *mp)
+{
+	xfs_agnumber_t	agno;
+	xfs_perag_t	*pag;
+	int		busy;
+
+	pag = mp->m_perag;
+	for (agno = 0; agno < mp->m_sb.sb_agcount; agno++, pag++) {
+		kdb_printf("ag %d f_init %d i_init %d\n",
+			agno, pag->pagf_init, pag->pagi_init);
+		if (pag->pagf_init)
+			kdb_printf(
+	"    f_levels[b,c] %d,%d f_flcount %d f_freeblks %d f_longest %d\n"
+	"    f__metadata %d\n",
+				pag->pagf_levels[XFS_BTNUM_BNOi],
+				pag->pagf_levels[XFS_BTNUM_CNTi],
+				pag->pagf_flcount, pag->pagf_freeblks,
+				pag->pagf_longest, pag->pagf_metadata);
+		if (pag->pagi_init)
+			kdb_printf("	i_freecount %d i_inodeok %d\n",
+				pag->pagi_freecount, pag->pagi_inodeok);
+
+		for (busy = 0; busy < XFS_PAGB_NUM_SLOTS; busy++) {
+			kdb_printf("	 %04d: start %d length %d tp 0x%p\n",
+				busy,
+				pag->pagb_list[busy].busy_start,
+				pag->pagb_list[busy].busy_length,
+				pag->pagb_list[busy].busy_tp);
+		}
+	}
+}
+
+#ifdef CONFIG_XFS_QUOTA
+static void
+xfsidbg_xqm()
+{
+	if (xfs_Gqm == NULL) {
+		kdb_printf("NULL XQM!!\n");
+		return;
+	}
+
+	kdb_printf("usrhtab 0x%p\tgrphtab 0x%p\tndqfree 0x%x\thashmask 0x%x\n",
+		xfs_Gqm->qm_usr_dqhtable,
+		xfs_Gqm->qm_grp_dqhtable,
+		xfs_Gqm->qm_dqfreelist.qh_nelems,
+		xfs_Gqm->qm_dqhashmask);
+	kdb_printf("&freelist 0x%p, totaldquots 0x%x nrefs 0x%x\n",
+		&xfs_Gqm->qm_dqfreelist,
+		atomic_read(&xfs_Gqm->qm_totaldquots),
+		xfs_Gqm->qm_nrefs);
+}
+#endif
+
+static void
+xfsidbg_xqm_diskdq(xfs_disk_dquot_t *d)
+{
+	kdb_printf("magic 0x%x\tversion 0x%x\tID 0x%x (%d)\t\n",
+		INT_GET(d->d_magic, ARCH_CONVERT),
+		INT_GET(d->d_version, ARCH_CONVERT),
+		INT_GET(d->d_id, ARCH_CONVERT),
+		INT_GET(d->d_id, ARCH_CONVERT));
+	kdb_printf("bhard 0x%llx\tbsoft 0x%llx\tihard 0x%llx\tisoft 0x%llx\n",
+		(unsigned long long)INT_GET(d->d_blk_hardlimit, ARCH_CONVERT),
+		(unsigned long long)INT_GET(d->d_blk_softlimit, ARCH_CONVERT),
+		(unsigned long long)INT_GET(d->d_ino_hardlimit, ARCH_CONVERT),
+		(unsigned long long)INT_GET(d->d_ino_softlimit, ARCH_CONVERT));
+	kdb_printf("bcount 0x%llx icount 0x%llx\n",
+		(unsigned long long)INT_GET(d->d_bcount, ARCH_CONVERT),
+		(unsigned long long)INT_GET(d->d_icount, ARCH_CONVERT));
+	kdb_printf("btimer 0x%x itimer 0x%x \n",
+		(int)INT_GET(d->d_btimer, ARCH_CONVERT),
+		(int)INT_GET(d->d_itimer, ARCH_CONVERT));
+}
+
+static void
+xfsidbg_xqm_dquot(xfs_dquot_t *dqp)
+{
+	static char *qflags[] = {
+		"USR",
+		"GRP",
+		"LCKD",
+		"FLKD",
+		"DIRTY",
+		"WANT",
+		"INACT",
+		"MARKER",
+		0
+	};
+	kdb_printf("mount 0x%p hash 0x%p gdquotp 0x%p HL_next 0x%p HL_prevp 0x%p\n",
+		dqp->q_mount,
+		dqp->q_hash,
+		dqp->q_gdquot,
+		dqp->HL_NEXT,
+		dqp->HL_PREVP);
+	kdb_printf("MPL_next 0x%p MPL_prevp 0x%p FL_next 0x%p FL_prev 0x%p\n",
+		dqp->MPL_NEXT,
+		dqp->MPL_PREVP,
+		dqp->dq_flnext,
+		dqp->dq_flprev);
+
+	kdb_printf("nrefs 0x%x, res_bcount %d, ",
+		dqp->q_nrefs, (int) dqp->q_res_bcount);
+	printflags(dqp->dq_flags, qflags, "flags:");
+	kdb_printf("\nblkno 0x%llx\tboffset 0x%x\n",
+		(unsigned long long) dqp->q_blkno, (int) dqp->q_bufoffset);
+	kdb_printf("qlock 0x%p	flock 0x%p (%s) pincount 0x%x\n",
+		&dqp->q_qlock,
+		&dqp->q_flock,
+		(valusema(&dqp->q_flock) <= 0) ? "LCK" : "UNLKD",
+		dqp->q_pincount);
+	kdb_printf("disk-dquot 0x%p\n", &dqp->q_core);
+	xfsidbg_xqm_diskdq(&dqp->q_core);
+
+}
+
+
+#define XQMIDBG_LIST_PRINT(l, NXT) \
+{ \
+	  xfs_dquot_t	*dqp;\
+	  int i = 0; \
+	  kdb_printf("[#%d dquots]\n", (int) (l)->qh_nelems); \
+	  for (dqp = (l)->qh_next; dqp != NULL; dqp = dqp->NXT) {\
+	   kdb_printf( \
+	      "\t%d. [0x%p] \"%d (%s)\"\t blks = %d, inos = %d refs = %d\n", \
+			 ++i, dqp, (int) INT_GET(dqp->q_core.d_id, ARCH_CONVERT), \
+			 DQFLAGTO_TYPESTR(dqp),	     \
+			 (int) INT_GET(dqp->q_core.d_bcount, ARCH_CONVERT), \
+			 (int) INT_GET(dqp->q_core.d_icount, ARCH_CONVERT), \
+			 (int) dqp->q_nrefs); }\
+	  kdb_printf("\n"); \
+}
+
+static void
+xfsidbg_xqm_dqattached_inos(xfs_mount_t *mp)
+{
+	xfs_inode_t	*ip;
+	int		n = 0;
+
+	ip = mp->m_inodes;
+	do {
+		if (ip->i_mount == NULL) {
+			ip = ip->i_mnext;
+			continue;
+		}
+		if (ip->i_udquot || ip->i_gdquot) {
+			n++;
+			kdb_printf("inode = 0x%p, ino %d: udq 0x%p, gdq 0x%p\n",
+				ip, (int)ip->i_ino, ip->i_udquot, ip->i_gdquot);
+		}
+		ip = ip->i_mnext;
+	} while (ip != mp->m_inodes);
+	kdb_printf("\nNumber of inodes with dquots attached: %d\n", n);
+}
+
+#ifdef	CONFIG_XFS_QUOTA
+static void
+xfsidbg_xqm_freelist_print(xfs_frlist_t *qlist, char *title)
+{
+	xfs_dquot_t *dq;
+	int i = 0;
+	kdb_printf("%s (#%d)\n", title, (int) qlist->qh_nelems);
+	FOREACH_DQUOT_IN_FREELIST(dq, qlist) {
+		kdb_printf("\t%d.\t\"%d (%s:0x%p)\"\t bcnt = %d, icnt = %d "
+		       "refs = %d\n",
+		       ++i, (int) INT_GET(dq->q_core.d_id, ARCH_CONVERT),
+		       DQFLAGTO_TYPESTR(dq), dq,
+		       (int) INT_GET(dq->q_core.d_bcount, ARCH_CONVERT),
+		       (int) INT_GET(dq->q_core.d_icount, ARCH_CONVERT),
+		       (int) dq->q_nrefs);
+	}
+}
+
+static void
+xfsidbg_xqm_freelist(void)
+{
+	if (xfs_Gqm) {
+		xfsidbg_xqm_freelist_print(&(xfs_Gqm->qm_dqfreelist), "Freelist");
+	} else
+		kdb_printf("NULL XQM!!\n");
+}
+
+static void
+xfsidbg_xqm_htab(void)
+{
+	int		i;
+	xfs_dqhash_t	*h;
+
+	if (xfs_Gqm == NULL) {
+		kdb_printf("NULL XQM!!\n");
+		return;
+	}
+	for (i = 0; i <= xfs_Gqm->qm_dqhashmask; i++) {
+		h = &xfs_Gqm->qm_usr_dqhtable[i];
+		if (h->qh_next) {
+			kdb_printf("USR %d: ", i);
+			XQMIDBG_LIST_PRINT(h, HL_NEXT);
+		}
+	}
+	for (i = 0; i <= xfs_Gqm->qm_dqhashmask; i++) {
+		h = &xfs_Gqm->qm_grp_dqhtable[i];
+		if (h->qh_next) {
+			kdb_printf("GRP %d: ", i);
+			XQMIDBG_LIST_PRINT(h, HL_NEXT);
+		}
+	}
+}
+#endif
+
+static void
+xfsidbg_xqm_mplist(xfs_mount_t *mp)
+{
+	if (mp->m_quotainfo == NULL) {
+		kdb_printf("NULL quotainfo\n");
+		return;
+	}
+
+	XQMIDBG_LIST_PRINT(&(mp->m_quotainfo->qi_dqlist), MPL_NEXT);
+
+}
+
+
+static void
+xfsidbg_xqm_qinfo(xfs_mount_t *mp)
+{
+	if (mp == NULL || mp->m_quotainfo == NULL) {
+		kdb_printf("NULL quotainfo\n");
+		return;
+	}
+
+	kdb_printf("uqip 0x%p, gqip 0x%p, &pinlock 0x%p &dqlist 0x%p\n",
+		mp->m_quotainfo->qi_uquotaip,
+		mp->m_quotainfo->qi_gquotaip,
+		&mp->m_quotainfo->qi_pinlock,
+		&mp->m_quotainfo->qi_dqlist);
+
+	kdb_printf("nreclaims %d, btmlimit 0x%x, itmlimit 0x%x, RTbtmlim 0x%x\n",
+		(int)mp->m_quotainfo->qi_dqreclaims,
+		(int)mp->m_quotainfo->qi_btimelimit,
+		(int)mp->m_quotainfo->qi_itimelimit,
+		(int)mp->m_quotainfo->qi_rtbtimelimit);
+
+	kdb_printf("bwarnlim 0x%x, iwarnlim 0x%x, &qofflock 0x%p, "
+		"chunklen 0x%x, dqperchunk 0x%x\n",
+		(int)mp->m_quotainfo->qi_bwarnlimit,
+		(int)mp->m_quotainfo->qi_iwarnlimit,
+		&mp->m_quotainfo->qi_quotaofflock,
+		(int)mp->m_quotainfo->qi_dqchunklen,
+		(int)mp->m_quotainfo->qi_dqperchunk);
+}
+
+static void
+xfsidbg_xqm_tpdqinfo(xfs_trans_t *tp)
+{
+	xfs_dqtrx_t	*qa, *q;
+	int		i,j;
+
+	kdb_printf("dqinfo 0x%p\n", tp->t_dqinfo);
+	if (! tp->t_dqinfo)
+		return;
+	kdb_printf("USR: \n");
+	qa = tp->t_dqinfo->dqa_usrdquots;
+	for (j = 0; j < 2; j++) {
+		for (i = 0; i < XFS_QM_TRANS_MAXDQS; i++) {
+			if (qa[i].qt_dquot == NULL)
+				break;
+			q = &qa[i];
+			kdb_printf(
+  "\"%d\"[0x%p]: bres %d, bres-used %d, bdelta %d, del-delta %d, icnt-delta %d\n",
+				(int) q->qt_dquot->q_core.d_id,
+				q->qt_dquot,
+				(int) q->qt_blk_res,
+				(int) q->qt_blk_res_used,
+				(int) q->qt_bcount_delta,
+				(int) q->qt_delbcnt_delta,
+				(int) q->qt_icount_delta);
+		}
+		if (j == 0) {
+			qa = tp->t_dqinfo->dqa_grpdquots;
+			kdb_printf("GRP: \n");
+		}
+	}
+
+}
+
+
+
+/*
+ * Print xfs superblock.
+ */
+static void
+xfsidbg_xsb(xfs_sb_t *sbp, int convert)
+{
+	xfs_arch_t arch=convert?ARCH_CONVERT:ARCH_NOCONVERT;
+
+	kdb_printf(convert?"<converted>\n":"<unconverted>\n");
+
+	kdb_printf("magicnum 0x%x blocksize 0x%x dblocks %Ld rblocks %Ld\n",
+		INT_GET(sbp->sb_magicnum, arch), INT_GET(sbp->sb_blocksize, arch),
+		INT_GET(sbp->sb_dblocks, arch), INT_GET(sbp->sb_rblocks, arch));
+	kdb_printf("rextents %Ld uuid %s logstart %s\n",
+		INT_GET(sbp->sb_rextents, arch),
+		xfs_fmtuuid(&sbp->sb_uuid),
+		xfs_fmtfsblock(INT_GET(sbp->sb_logstart, arch), NULL));
+	kdb_printf("rootino %s ",
+		xfs_fmtino(INT_GET(sbp->sb_rootino, arch), NULL));
+	kdb_printf("rbmino %s ",
+		xfs_fmtino(INT_GET(sbp->sb_rbmino, arch), NULL));
+	kdb_printf("rsumino %s\n",
+		xfs_fmtino(INT_GET(sbp->sb_rsumino, arch), NULL));
+	kdb_printf("rextsize 0x%x agblocks 0x%x agcount 0x%x rbmblocks 0x%x\n",
+		INT_GET(sbp->sb_rextsize, arch),
+		INT_GET(sbp->sb_agblocks, arch),
+		INT_GET(sbp->sb_agcount, arch),
+		INT_GET(sbp->sb_rbmblocks, arch));
+	kdb_printf("logblocks 0x%x versionnum 0x%x sectsize 0x%x inodesize 0x%x\n",
+		INT_GET(sbp->sb_logblocks, arch),
+		INT_GET(sbp->sb_versionnum, arch),
+		INT_GET(sbp->sb_sectsize, arch),
+		INT_GET(sbp->sb_inodesize, arch));
+	kdb_printf("inopblock 0x%x blocklog 0x%x sectlog 0x%x inodelog 0x%x\n",
+		INT_GET(sbp->sb_inopblock, arch),
+		INT_GET(sbp->sb_blocklog, arch),
+		INT_GET(sbp->sb_sectlog, arch),
+		INT_GET(sbp->sb_inodelog, arch));
+	kdb_printf("inopblog %d agblklog %d rextslog %d inprogress %d imax_pct %d\n",
+		INT_GET(sbp->sb_inopblog, arch),
+		INT_GET(sbp->sb_agblklog, arch),
+		INT_GET(sbp->sb_rextslog, arch),
+		INT_GET(sbp->sb_inprogress, arch),
+		INT_GET(sbp->sb_imax_pct, arch));
+	kdb_printf("icount %Lx ifree %Lx fdblocks %Lx frextents %Lx\n",
+		INT_GET(sbp->sb_icount, arch),
+		INT_GET(sbp->sb_ifree, arch),
+		INT_GET(sbp->sb_fdblocks, arch),
+		INT_GET(sbp->sb_frextents, arch));
+	kdb_printf("uquotino %s ", xfs_fmtino(INT_GET(sbp->sb_uquotino, arch), NULL));
+	kdb_printf("gquotino %s ", xfs_fmtino(INT_GET(sbp->sb_gquotino, arch), NULL));
+	kdb_printf("qflags 0x%x flags 0x%x shared_vn %d inoaligmt %d\n",
+		INT_GET(sbp->sb_qflags, arch), INT_GET(sbp->sb_flags, arch), INT_GET(sbp->sb_shared_vn, arch),
+		INT_GET(sbp->sb_inoalignmt, arch));
+	kdb_printf("unit %d width %d dirblklog %d\n",
+		INT_GET(sbp->sb_unit, arch), INT_GET(sbp->sb_width, arch), INT_GET(sbp->sb_dirblklog, arch));
+	kdb_printf("log sunit %d\n", INT_GET(sbp->sb_logsunit, arch));
+}
+
+
+/*
+ * Print out an XFS transaction structure.  Print summaries for
+ * each of the items.
+ */
+static void
+xfsidbg_xtp(xfs_trans_t *tp)
+{
+	xfs_log_item_chunk_t	*licp;
+	xfs_log_item_desc_t	*lidp;
+	xfs_log_busy_chunk_t	*lbcp;
+	int			i;
+	int			chunk;
+	static char *xtp_flags[] = {
+		"dirty",	/* 0x1 */
+		"sb_dirty",	/* 0x2 */
+		"perm_log_res", /* 0x4 */
+		"sync",		/* 0x08 */
+		"dq_dirty",	/* 0x10 */
+		0
+		};
+	static char *lid_flags[] = {
+		"dirty",	/* 0x1 */
+		"pinned",	/* 0x2 */
+		"sync unlock",	/* 0x4 */
+		0
+		};
+
+	kdb_printf("tp 0x%p type ", tp);
+	switch (tp->t_type) {
+	case XFS_TRANS_SETATTR_NOT_SIZE: kdb_printf("SETATTR_NOT_SIZE");	break;
+	case XFS_TRANS_SETATTR_SIZE:	kdb_printf("SETATTR_SIZE");	break;
+	case XFS_TRANS_INACTIVE:	kdb_printf("INACTIVE");		break;
+	case XFS_TRANS_CREATE:		kdb_printf("CREATE");		break;
+	case XFS_TRANS_CREATE_TRUNC:	kdb_printf("CREATE_TRUNC");	break;
+	case XFS_TRANS_TRUNCATE_FILE:	kdb_printf("TRUNCATE_FILE");	break;
+	case XFS_TRANS_REMOVE:		kdb_printf("REMOVE");		break;
+	case XFS_TRANS_LINK:		kdb_printf("LINK");		break;
+	case XFS_TRANS_RENAME:		kdb_printf("RENAME");		break;
+	case XFS_TRANS_MKDIR:		kdb_printf("MKDIR");		break;
+	case XFS_TRANS_RMDIR:		kdb_printf("RMDIR");		break;
+	case XFS_TRANS_SYMLINK:		kdb_printf("SYMLINK");		break;
+	case XFS_TRANS_SET_DMATTRS:	kdb_printf("SET_DMATTRS");		break;
+	case XFS_TRANS_GROWFS:		kdb_printf("GROWFS");		break;
+	case XFS_TRANS_STRAT_WRITE:	kdb_printf("STRAT_WRITE");		break;
+	case XFS_TRANS_DIOSTRAT:	kdb_printf("DIOSTRAT");		break;
+	case XFS_TRANS_WRITE_SYNC:	kdb_printf("WRITE_SYNC");		break;
+	case XFS_TRANS_WRITEID:		kdb_printf("WRITEID");		break;
+	case XFS_TRANS_ADDAFORK:	kdb_printf("ADDAFORK");		break;
+	case XFS_TRANS_ATTRINVAL:	kdb_printf("ATTRINVAL");		break;
+	case XFS_TRANS_ATRUNCATE:	kdb_printf("ATRUNCATE");		break;
+	case XFS_TRANS_ATTR_SET:	kdb_printf("ATTR_SET");		break;
+	case XFS_TRANS_ATTR_RM:		kdb_printf("ATTR_RM");		break;
+	case XFS_TRANS_ATTR_FLAG:	kdb_printf("ATTR_FLAG");		break;
+	case XFS_TRANS_CLEAR_AGI_BUCKET:  kdb_printf("CLEAR_AGI_BUCKET");	break;
+	case XFS_TRANS_QM_SBCHANGE:	kdb_printf("QM_SBCHANGE");	break;
+	case XFS_TRANS_QM_QUOTAOFF:	kdb_printf("QM_QUOTAOFF");	break;
+	case XFS_TRANS_QM_DQALLOC:	kdb_printf("QM_DQALLOC");		break;
+	case XFS_TRANS_QM_SETQLIM:	kdb_printf("QM_SETQLIM");		break;
+	case XFS_TRANS_QM_DQCLUSTER:	kdb_printf("QM_DQCLUSTER");	break;
+	case XFS_TRANS_QM_QINOCREATE:	kdb_printf("QM_QINOCREATE");	break;
+	case XFS_TRANS_QM_QUOTAOFF_END: kdb_printf("QM_QOFF_END");		break;
+	case XFS_TRANS_SB_UNIT:		kdb_printf("SB_UNIT");		break;
+	case XFS_TRANS_FSYNC_TS:	kdb_printf("FSYNC_TS");		break;
+	case XFS_TRANS_GROWFSRT_ALLOC:	kdb_printf("GROWFSRT_ALLOC");	break;
+	case XFS_TRANS_GROWFSRT_ZERO:	kdb_printf("GROWFSRT_ZERO");	break;
+	case XFS_TRANS_GROWFSRT_FREE:	kdb_printf("GROWFSRT_FREE");	break;
+
+	default:			kdb_printf("0x%x", tp->t_type); break;
+	}
+	kdb_printf(" mount 0x%p\n", tp->t_mountp);
+	kdb_printf("flags ");
+	printflags(tp->t_flags, xtp_flags,"xtp");
+	kdb_printf("\n");
+	kdb_printf("callback 0x%p forw 0x%p back 0x%p\n",
+		&tp->t_logcb, tp->t_forw, tp->t_back);
+	kdb_printf("log res %d block res %d block res used %d\n",
+		tp->t_log_res, tp->t_blk_res, tp->t_blk_res_used);
+	kdb_printf("rt res %d rt res used %d\n", tp->t_rtx_res,
+		tp->t_rtx_res_used);
+	kdb_printf("ticket 0x%lx lsn %s\n",
+		(unsigned long) tp->t_ticket, xfs_fmtlsn(&tp->t_lsn));
+	kdb_printf("callback 0x%p callarg 0x%p\n",
+		tp->t_callback, tp->t_callarg);
+	kdb_printf("icount delta %ld ifree delta %ld\n",
+		tp->t_icount_delta, tp->t_ifree_delta);
+	kdb_printf("blocks delta %ld res blocks delta %ld\n",
+		tp->t_fdblocks_delta, tp->t_res_fdblocks_delta);
+	kdb_printf("rt delta %ld res rt delta %ld\n",
+		tp->t_frextents_delta, tp->t_res_frextents_delta);
+	kdb_printf("ag freeblks delta %ld ag flist delta %ld ag btree delta %ld\n",
+		tp->t_ag_freeblks_delta, tp->t_ag_flist_delta,
+		tp->t_ag_btree_delta);
+	kdb_printf("dblocks delta %ld agcount delta %ld imaxpct delta %ld\n",
+		tp->t_dblocks_delta, tp->t_agcount_delta, tp->t_imaxpct_delta);
+	kdb_printf("rextsize delta %ld rbmblocks delta %ld\n",
+		tp->t_rextsize_delta, tp->t_rbmblocks_delta);
+	kdb_printf("rblocks delta %ld rextents delta %ld rextslog delta %ld\n",
+		tp->t_rblocks_delta, tp->t_rextents_delta,
+		tp->t_rextslog_delta);
+	kdb_printf("dqinfo 0x%p\n", tp->t_dqinfo);
+	kdb_printf("log items:\n");
+	licp = &tp->t_items;
+	chunk = 0;
+	while (licp != NULL) {
+		if (XFS_LIC_ARE_ALL_FREE(licp)) {
+			licp = licp->lic_next;
+			chunk++;
+			continue;
+		}
+		for (i = 0; i < licp->lic_unused; i++) {
+			if (XFS_LIC_ISFREE(licp, i)) {
+				continue;
+			}
+
+			lidp = XFS_LIC_SLOT(licp, i);
+			kdb_printf("\n");
+			kdb_printf("chunk %d index %d item 0x%p size %d\n",
+				chunk, i, lidp->lid_item, lidp->lid_size);
+			kdb_printf("flags ");
+			printflags(lidp->lid_flags, lid_flags,"lic");
+			kdb_printf("\n");
+			xfsidbg_xlogitem(lidp->lid_item);
+		}
+		chunk++;
+		licp = licp->lic_next;
+	}
+
+	kdb_printf("log busy free %d, list:\n", tp->t_busy_free);
+	lbcp = &tp->t_busy;
+	chunk = 0;
+	while (lbcp != NULL) {
+		kdb_printf("Chunk %d at 0x%p next 0x%p free 0x%08x unused %d\n",
+			chunk, lbcp, lbcp->lbc_next, lbcp->lbc_free,
+			lbcp->lbc_unused);
+		for (i = 0; i < XFS_LBC_NUM_SLOTS; i++) {
+			kdb_printf("  %02d: ag %d idx %d\n",
+				i,
+				lbcp->lbc_busy[i].lbc_ag,
+				lbcp->lbc_busy[i].lbc_idx);
+		}
+		lbcp = lbcp->lbc_next;
+	}
+}
+
+static void
+xfsidbg_xtrans_res(
+	xfs_mount_t	*mp)
+{
+	xfs_trans_reservations_t	*xtrp;
+
+	xtrp = &mp->m_reservations;
+	kdb_printf("write: %d\ttruncate: %d\trename: %d\n",
+		xtrp->tr_write, xtrp->tr_itruncate, xtrp->tr_rename);
+	kdb_printf("link: %d\tremove: %d\tsymlink: %d\n",
+		xtrp->tr_link, xtrp->tr_remove, xtrp->tr_symlink);
+	kdb_printf("create: %d\tmkdir: %d\tifree: %d\n",
+		xtrp->tr_create, xtrp->tr_mkdir, xtrp->tr_ifree);
+	kdb_printf("ichange: %d\tgrowdata: %d\tswrite: %d\n",
+		xtrp->tr_ichange, xtrp->tr_growdata, xtrp->tr_swrite);
+	kdb_printf("addafork: %d\twriteid: %d\tattrinval: %d\n",
+		xtrp->tr_addafork, xtrp->tr_writeid, xtrp->tr_attrinval);
+	kdb_printf("attrset: %d\tattrrm: %d\tclearagi: %d\n",
+		xtrp->tr_attrset, xtrp->tr_attrrm, xtrp->tr_clearagi);
+	kdb_printf("growrtalloc: %d\tgrowrtzero: %d\tgrowrtfree: %d\n",
+		xtrp->tr_growrtalloc, xtrp->tr_growrtzero, xtrp->tr_growrtfree);
+}
+
+module_init(xfsidbg_init)
+module_exit(xfsidbg_exit)
diff -Nru a/include/asm-alpha/unistd.h b/include/asm-alpha/unistd.h
--- a/include/asm-alpha/unistd.h	Mon Oct  7 11:25:33 2002
+++ b/include/asm-alpha/unistd.h	Mon Oct  7 11:25:33 2002
@@ -533,12 +533,6 @@
 #include <linux/signal.h>
 #include <asm/ptrace.h>
 
-extern void sys_idle(void);
-static inline void idle(void)
-{
-	sys_idle();
-}
-
 extern long sys_open(const char *, int, int);
 static inline long open(const char * name, int mode, int flags)
 {
@@ -597,28 +591,11 @@
 	return sys_setsid();
 }
 
-extern long sys_sync(void);
-static inline long sync(void)
-{
-	return sys_sync();
-}
-
 struct rusage;
 extern asmlinkage long sys_wait4(pid_t, unsigned int *, int, struct rusage *);
 static inline pid_t waitpid(int pid, int * wait_stat, int flags)
 {
 	return sys_wait4(pid, wait_stat, flags, NULL);
-}
-
-static inline pid_t wait(int * wait_stat)
-{
-	return waitpid(-1,wait_stat,0);
-}
-
-extern long sys_delete_module(const char *name);
-static inline long delete_module(const char *name)
-{
-	return sys_delete_module(name);
 }
 
 #endif
diff -Nru a/include/asm-arm/unistd.h b/include/asm-arm/unistd.h
--- a/include/asm-arm/unistd.h	Mon Oct  7 11:25:39 2002
+++ b/include/asm-arm/unistd.h	Mon Oct  7 11:25:39 2002
@@ -403,24 +403,6 @@
 struct rusage;
 asmlinkage long sys_wait4(pid_t pid,unsigned int * stat_addr, int options, struct rusage * ru);
 
-static inline long idle(void)
-{
-	extern long sys_idle(void);
-	return sys_idle();
-}
-
-static inline long pause(void)
-{
-	extern long sys_pause(void);
-	return sys_pause();
-}
-
-static inline long sync(void)
-{
-	extern long sys_sync(void);
-	return sys_sync();
-}
-
 static inline pid_t setsid(void)
 {
 	extern long sys_setsid(void);
@@ -472,17 +454,6 @@
 static inline pid_t waitpid(pid_t pid, int *wait_stat, int options)
 {
 	return sys_wait4((int)pid, wait_stat, options, NULL);
-}
-
-static inline long delete_module(const char *name)
-{
-	extern long sys_delete_module(const char *name);
-	return sys_delete_module(name);
-}
-
-static inline pid_t wait(int * wait_stat)
-{
-	return sys_wait4(-1, wait_stat, 0, NULL);
 }
 
 /*
diff -Nru a/include/asm-cris/unistd.h b/include/asm-cris/unistd.h
--- a/include/asm-cris/unistd.h	Mon Oct  7 11:25:31 2002
+++ b/include/asm-cris/unistd.h	Mon Oct  7 11:25:31 2002
@@ -365,12 +365,6 @@
  * some others too.
  */
 #define __NR__exit __NR_exit
-static inline _syscall0(int,idle)
-static inline _syscall0(int,fork)
-static inline _syscall2(int,clone,unsigned long,flags,char *,esp)
-static inline _syscall0(int,pause)
-static inline _syscall0(int,setup)
-static inline _syscall0(int,sync)
 static inline _syscall0(pid_t,setsid)
 static inline _syscall3(int,write,int,fd,const char *,buf,off_t,count)
 static inline _syscall1(int,dup,int,fd)
@@ -384,14 +378,6 @@
   /* the following are just while developing the elinux port! */
 
 static inline _syscall3(int,read,int,fd,char *,buf,off_t,count)
-static inline _syscall2(int,socketcall,int,call,unsigned long *,args)
-static inline _syscall3(int,ioctl,unsigned int,fd,unsigned int,cmd,unsigned long,arg)
-static inline _syscall5(int,mount,const char *,a,const char *,b,const char *,c,unsigned long,rwflag,const void *,data)
-
-static inline pid_t wait(int * wait_stat)
-{
-	return waitpid(-1,wait_stat,0);
-}
 
 #endif
 
diff -Nru a/include/asm-i386/unistd.h b/include/asm-i386/unistd.h
--- a/include/asm-i386/unistd.h	Mon Oct  7 11:25:36 2002
+++ b/include/asm-i386/unistd.h	Mon Oct  7 11:25:36 2002
@@ -362,8 +362,6 @@
  * some others too.
  */
 #define __NR__exit __NR_exit
-static inline _syscall0(int,pause)
-static inline _syscall0(int,sync)
 static inline _syscall0(pid_t,setsid)
 static inline _syscall3(int,write,int,fd,const char *,buf,off_t,count)
 static inline _syscall3(int,read,int,fd,char *,buf,off_t,count)
@@ -374,12 +372,6 @@
 static inline _syscall1(int,close,int,fd)
 static inline _syscall1(int,_exit,int,exitcode)
 static inline _syscall3(pid_t,waitpid,pid_t,pid,int *,wait_stat,int,options)
-static inline _syscall1(int,delete_module,const char *,name)
-
-static inline pid_t wait(int * wait_stat)
-{
-	return waitpid(-1,wait_stat,0);
-}
 
 #endif
 
diff -Nru a/include/asm-ia64/unistd.h b/include/asm-ia64/unistd.h
--- a/include/asm-ia64/unistd.h	Mon Oct  7 11:25:26 2002
+++ b/include/asm-ia64/unistd.h	Mon Oct  7 11:25:26 2002
@@ -309,7 +309,6 @@
 
 struct rusage;
 
-static inline _syscall0(int,sync)
 static inline _syscall0(pid_t,setsid)
 static inline _syscall3(int,write,int,fd,const char *,buf,off_t,count)
 static inline _syscall3(int,read,int,fd,char *,buf,off_t,count)
@@ -319,7 +318,6 @@
 static inline _syscall3(int,open,const char *,file,int,flag,int,mode)
 static inline _syscall1(int,close,int,fd)
 static inline _syscall4(pid_t,wait4,pid_t,pid,int *,wait_stat,int,options,struct rusage*, rusage)
-static inline _syscall1(int,delete_module,const char *,name)
 static inline _syscall2(pid_t,clone,unsigned long,flags,void*,sp);
 
 #define __NR__exit __NR_exit
@@ -329,12 +327,6 @@
 waitpid (int pid, int *wait_stat, int flags)
 {
 	return wait4(pid, wait_stat, flags, NULL);
-}
-
-static inline pid_t
-wait (int * wait_stat)
-{
-	return wait4(-1, wait_stat, 0, 0);
 }
 
 #endif /* __KERNEL_SYSCALLS__ */
diff -Nru a/include/asm-m68k/unistd.h b/include/asm-m68k/unistd.h
--- a/include/asm-m68k/unistd.h	Mon Oct  7 11:25:35 2002
+++ b/include/asm-m68k/unistd.h	Mon Oct  7 11:25:35 2002
@@ -350,8 +350,6 @@
  * some others too.
  */
 #define __NR__exit __NR_exit
-static inline _syscall0(int,pause)
-static inline _syscall0(int,sync)
 static inline _syscall0(pid_t,setsid)
 static inline _syscall3(int,write,int,fd,const char *,buf,off_t,count)
 static inline _syscall3(int,read,int,fd,char *,buf,off_t,count)
@@ -362,12 +360,6 @@
 static inline _syscall1(int,close,int,fd)
 static inline _syscall1(int,_exit,int,exitcode)
 static inline _syscall3(pid_t,waitpid,pid_t,pid,int *,wait_stat,int,options)
-static inline _syscall1(int,delete_module,const char *,name)
-
-static inline pid_t wait(int * wait_stat)
-{
-	return waitpid(-1,wait_stat,0);
-}
 
 #endif
 
diff -Nru a/include/asm-mips/unistd.h b/include/asm-mips/unistd.h
--- a/include/asm-mips/unistd.h	Mon Oct  7 11:25:25 2002
+++ b/include/asm-mips/unistd.h	Mon Oct  7 11:25:25 2002
@@ -467,7 +467,6 @@
  * some others too.
  */
 #define __NR__exit __NR_exit
-static inline _syscall0(int,sync)
 static inline _syscall0(pid_t,setsid)
 static inline _syscall3(int,write,int,fd,const char *,buf,off_t,count)
 static inline _syscall3(int,read,int,fd,char *,buf,off_t,count)
@@ -478,12 +477,6 @@
 static inline _syscall1(int,close,int,fd)
 static inline _syscall1(int,_exit,int,exitcode)
 static inline _syscall3(pid_t,waitpid,pid_t,pid,int *,wait_stat,int,options)
-static inline _syscall1(int,delete_module,const char *,name)
-
-static inline pid_t wait(int * wait_stat)
-{
-	return waitpid(-1,wait_stat,0);
-}
 
 #endif /* !defined (__KERNEL_SYSCALLS__) */
 #endif /* !defined (_LANGUAGE_ASSEMBLY) */
diff -Nru a/include/asm-mips64/unistd.h b/include/asm-mips64/unistd.h
--- a/include/asm-mips64/unistd.h	Mon Oct  7 11:25:37 2002
+++ b/include/asm-mips64/unistd.h	Mon Oct  7 11:25:37 2002
@@ -793,7 +793,6 @@
  * some others too.
  */
 #define __NR__exit __NR_exit
-static inline _syscall0(int,sync)
 static inline _syscall0(pid_t,setsid)
 static inline _syscall3(int,write,int,fd,const char *,buf,off_t,count)
 static inline _syscall3(int,read,int,fd,char *,buf,off_t,count)
@@ -804,12 +803,6 @@
 static inline _syscall1(int,close,int,fd)
 static inline _syscall1(int,_exit,int,exitcode)
 static inline _syscall3(pid_t,waitpid,pid_t,pid,int *,wait_stat,int,options)
-static inline _syscall1(int,delete_module,const char *,name)
-
-static inline pid_t wait(int * wait_stat)
-{
-	return waitpid(-1,wait_stat,0);
-}
 
 #endif /* !defined (__KERNEL_SYSCALLS__) */
 #endif /* !defined (_LANGUAGE_ASSEMBLY) */
diff -Nru a/include/asm-parisc/unistd.h b/include/asm-parisc/unistd.h
--- a/include/asm-parisc/unistd.h	Mon Oct  7 11:25:25 2002
+++ b/include/asm-parisc/unistd.h	Mon Oct  7 11:25:25 2002
@@ -806,24 +806,6 @@
 
 #ifdef __KERNEL_SYSCALLS__
 
-static inline int idle(void)
-{
-	extern int sys_idle(void);
-	return sys_idle();
-}
-
-static inline int pause(void)
-{
-	extern int sys_pause(void);
-	return sys_pause();
-}
-
-static inline int sync(void)
-{
-	extern int sys_sync(void);
-	return sys_sync();
-}
-
 static inline pid_t setsid(void)
 {
 	extern int sys_setsid(void);
@@ -875,18 +857,6 @@
 {
 	extern int sys_wait4(int, int *, int, struct rusage *);
 	return sys_wait4((int)pid, wait_stat, options, NULL);
-}
-
-static inline int delete_module(const char *name)
-{
-	extern int sys_delete_module(const char *name);
-	return sys_delete_module(name);
-}
-
-static inline pid_t wait(int * wait_stat)
-{
-	extern int sys_wait4(int, int *, int, struct rusage *);
-	return sys_wait4(-1, wait_stat, 0, NULL);
 }
 
 static inline int execve(char *filename, char * argv [],
diff -Nru a/include/asm-ppc/unistd.h b/include/asm-ppc/unistd.h
--- a/include/asm-ppc/unistd.h	Mon Oct  7 11:25:26 2002
+++ b/include/asm-ppc/unistd.h	Mon Oct  7 11:25:26 2002
@@ -436,10 +436,6 @@
 extern int close(int fd);
 extern pid_t waitpid(pid_t pid, int *wait_stat, int options);
 
-static inline pid_t wait(int * wait_stat) 
-{
-	return waitpid(-1, wait_stat, 0);
-}
 #endif /* __KERNEL_SYSCALLS__ */
 
 /*
diff -Nru a/include/asm-ppc64/unistd.h b/include/asm-ppc64/unistd.h
--- a/include/asm-ppc64/unistd.h	Mon Oct  7 11:25:25 2002
+++ b/include/asm-ppc64/unistd.h	Mon Oct  7 11:25:25 2002
@@ -431,8 +431,6 @@
  * System call prototypes.
  */
 #define __NR__exit __NR_exit
-static inline _syscall0(int,pause)
-static inline _syscall0(int,sync)
 static inline _syscall0(pid_t,setsid)
 static inline _syscall3(int,write,int,fd,const char *,buf,off_t,count)
 static inline _syscall3(int,read,int,fd,char *,buf,off_t,count)
@@ -443,12 +441,6 @@
 static inline _syscall1(int,close,int,fd)
 static inline _syscall1(int,_exit,int,exitcode)
 static inline _syscall3(pid_t,waitpid,pid_t,pid,int *,wait_stat,int,options)
-static inline _syscall1(int,delete_module,const char *,name)
-
-static inline pid_t wait(int * wait_stat) 
-{
-	return waitpid(-1,wait_stat,0);
-}
 
 #endif /* __KERNEL_SYSCALLS__ */
 
diff -Nru a/include/asm-s390/bitops.h b/include/asm-s390/bitops.h
--- a/include/asm-s390/bitops.h	Mon Oct  7 11:25:25 2002
+++ b/include/asm-s390/bitops.h	Mon Oct  7 11:25:25 2002
@@ -59,8 +59,8 @@
 
 	addr = (unsigned long) ptr;
 #if ALIGN_CS == 1
-	addr ^= addr & 3;		/* align address to 4 */
 	nr += (addr & 3) << 3;		/* add alignment to bit number */
+	addr ^= addr & 3;		/* align address to 4 */
 #endif
 	addr += (nr ^ (nr & 31)) >> 3;	/* calculate address for CS */
 	mask = 1UL << (nr & 31);	/* make OR mask */
@@ -84,8 +84,8 @@
 
 	addr = (unsigned long) ptr;
 #if ALIGN_CS == 1
-	addr ^= addr & 3;		/* align address to 4 */
 	nr += (addr & 3) << 3;		/* add alignment to bit number */
+	addr ^= addr & 3;		/* align address to 4 */
 #endif
 	addr += (nr ^ (nr & 31)) >> 3;	/* calculate address for CS */
 	mask = ~(1UL << (nr & 31));	/* make AND mask */
@@ -109,8 +109,8 @@
 
 	addr = (unsigned long) ptr;
 #if ALIGN_CS == 1
-	addr ^= addr & 3;		/* align address to 4 */
 	nr += (addr & 3) << 3;		/* add alignment to bit number */
+	addr ^= addr & 3;		/* align address to 4 */
 #endif
 	addr += (nr ^ (nr & 31)) >> 3;	/* calculate address for CS */
 	mask = 1UL << (nr & 31);	/* make XOR mask */
@@ -160,8 +160,8 @@
 
 	addr = (unsigned long) ptr;
 #if ALIGN_CS == 1
-	addr ^= addr & 3;		/* align address to 4 */
 	nr += (addr & 3) << 3;		/* add alignment to bit number */
+	addr ^= addr & 3;		/* align address to 4 */
 #endif
 	addr += (nr ^ (nr & 31)) >> 3;	/* calculate address for CS */
 	mask = ~(1UL << (nr & 31));	/* make AND mask */
@@ -186,8 +186,8 @@
 
 	addr = (unsigned long) ptr;
 #if ALIGN_CS == 1
-	addr ^= addr & 3;		/* align address to 4 */
 	nr += (addr & 3) << 3;		/* add alignment to bit number */
+	addr ^= addr & 3;		/* align address to 4 */
 #endif
 	addr += (nr ^ (nr & 31)) >> 3;	/* calculate address for CS */
 	mask = 1UL << (nr & 31);	/* make XOR mask */
diff -Nru a/include/asm-s390/checksum.h b/include/asm-s390/checksum.h
--- a/include/asm-s390/checksum.h	Mon Oct  7 11:25:25 2002
+++ b/include/asm-s390/checksum.h	Mon Oct  7 11:25:25 2002
@@ -27,13 +27,27 @@
  *
  * 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);
+static inline unsigned int
+csum_partial(const unsigned char * buff, int len, unsigned int sum)
+{
+	register_pair rp;
+	/*
+	 * Experiments with ethernet and slip connections show that buf
+	 * is aligned on either a 2-byte or 4-byte boundary.
+	 */
+	rp.subreg.even = (unsigned long) buff;
+	rp.subreg.odd = (unsigned long) len;
+	__asm__ __volatile__ (
+		"0:  cksm %0,%1\n"	/* do checksum on longs */
+		"    jo   0b\n"
+		: "+&d" (sum), "+&a" (rp) : : "cc" );
+	return sum;
+}
 
 /*
  * csum_partial as an inline function
  */
-extern inline unsigned int 
+static inline unsigned int 
 csum_partial_inline(const unsigned char * buff, int len, unsigned int sum)
 {
 	register_pair rp;
@@ -55,7 +69,7 @@
  * better 64-bit) boundary
  */
 
-extern inline unsigned int 
+static inline unsigned int 
 csum_partial_copy(const char *src, char *dst, int len,unsigned int sum)
 {
 	memcpy(dst,src,len);
@@ -71,7 +85,7 @@
  * Copy from userspace and compute checksum.  If we catch an exception
  * then zero the rest of the buffer.
  */
-extern inline unsigned int 
+static inline unsigned int 
 csum_partial_copy_from_user (const char *src, char *dst,
                                           int len, unsigned int sum,
                                           int *err_ptr)
@@ -88,7 +102,7 @@
 }
 
 
-extern inline unsigned int
+static inline unsigned int
 csum_partial_copy_nocheck (const char *src, char *dst, int len, unsigned int sum)
 {
         memcpy(dst,src,len);
@@ -98,10 +112,7 @@
 /*
  *      Fold a partial checksum without adding pseudo headers
  */
-#if 1
-unsigned short csum_fold(unsigned int sum);
-#else
-extern inline unsigned short
+static inline unsigned short
 csum_fold(unsigned int sum)
 {
 	register_pair rp;
@@ -116,14 +127,13 @@
 		: "+&d" (sum), "=d" (rp) : : "cc" );
 	return ((unsigned short) ~sum);
 }
-#endif
 
 /*
  *	This is a version of ip_compute_csum() optimized for IP headers,
  *	which always checksum on 4 octet boundaries.
  *
  */
-extern inline unsigned short
+static inline unsigned short
 ip_fast_csum(unsigned char *iph, unsigned int ihl)
 {
 	register_pair rp;
@@ -143,7 +153,7 @@
  * computes the checksum of the TCP/UDP pseudo-header
  * returns a 32-bit checksum
  */
-extern inline unsigned int 
+static inline unsigned int 
 csum_tcpudp_nofold(unsigned long saddr, unsigned long daddr,
                    unsigned short len, unsigned short proto,
                    unsigned int sum)
@@ -176,7 +186,7 @@
  * returns a 16-bit checksum, already complemented
  */
 
-extern inline unsigned short int
+static inline unsigned short int
 csum_tcpudp_magic(unsigned long saddr, unsigned long daddr,
                   unsigned short len, unsigned short proto,
                   unsigned int sum)
@@ -189,7 +199,7 @@
  * in icmp.c
  */
 
-extern inline unsigned short
+static inline unsigned short
 ip_compute_csum(unsigned char * buff, int len)
 {
 	return csum_fold(csum_partial(buff, len, 0));
diff -Nru a/include/asm-s390/dasd.h b/include/asm-s390/dasd.h
--- a/include/asm-s390/dasd.h	Mon Oct  7 11:25:26 2002
+++ b/include/asm-s390/dasd.h	Mon Oct  7 11:25:26 2002
@@ -13,6 +13,8 @@
  * 12/06/01 DASD_API_VERSION 2 - binary compatible to 0 (new BIODASDINFO2)
  * 01/23/02 DASD_API_VERSION 3 - added BIODASDPSRD (and BIODASDENAPAV) IOCTL
  * 02/15/02 DASD_API_VERSION 4 - added BIODASDSATTR IOCTL
+ * ##/##/## DASD_API_VERSION 5 - added boxed dasd support TOBEDONE
+ * 21/06/02 DASD_API_VERSION 6 - fixed HDIO_GETGEO: geo.start is in sectors!
  *         
  */
 
@@ -22,7 +24,7 @@
 
 #define DASD_IOCTL_LETTER 'D'
 
-#define DASD_API_VERSION 4
+#define DASD_API_VERSION 6
 
 /* 
  * struct dasd_information2_t
diff -Nru a/include/asm-s390/debug.h b/include/asm-s390/debug.h
--- a/include/asm-s390/debug.h	Mon Oct  7 11:25:25 2002
+++ b/include/asm-s390/debug.h	Mon Oct  7 11:25:25 2002
@@ -160,7 +160,8 @@
 }
 
 extern debug_entry_t *
-debug_sprintf_event(debug_info_t* id,int level,char *string,...);
+debug_sprintf_event(debug_info_t* id,int level,char *string,...)
+	__attribute__ ((format(printf, 3, 4)));
 
 
 extern inline debug_entry_t* 
@@ -195,7 +196,8 @@
 
 
 extern debug_entry_t *
-debug_sprintf_exception(debug_info_t* id,int level,char *string,...);
+debug_sprintf_exception(debug_info_t* id,int level,char *string,...)
+	__attribute__ ((format(printf, 3, 4)));
 
 int debug_register_view(debug_info_t* id, struct debug_view* view);
 int debug_unregister_view(debug_info_t* id, struct debug_view* view);
diff -Nru a/include/asm-s390/hardirq.h b/include/asm-s390/hardirq.h
--- a/include/asm-s390/hardirq.h	Mon Oct  7 11:25:40 2002
+++ b/include/asm-s390/hardirq.h	Mon Oct  7 11:25:40 2002
@@ -14,15 +14,13 @@
 
 #include <linux/config.h>
 #include <linux/threads.h>
-#include <asm/lowcore.h>
 #include <linux/sched.h>
 #include <linux/cache.h>
+#include <asm/lowcore.h>
 
 /* entry.S is sensitive to the offsets of these fields */
 typedef struct {
 	unsigned int __softirq_pending;
-	unsigned int __local_irq_count;
-	unsigned int __local_bh_count;
 	unsigned int __syscall_count;
 	struct task_struct * __ksoftirqd_task; /* waitqueue is too large */
 } ____cacheline_aligned irq_cpustat_t;
@@ -30,64 +28,81 @@
 #include <linux/irq_cpustat.h>	/* Standard mappings for irq_cpustat_t above */
 
 /*
- * Are we in an interrupt context? Either doing bottom half
- * or hardware interrupt processing?
+ * We put the hardirq and softirq counter into the preemption
+ * counter. The bitmask has the following meaning:
+ *
+ * - bits 0-7 are the preemption count (max preemption depth: 256)
+ * - bits 8-15 are the softirq count (max # of softirqs: 256)
+ * - bits 16-23 are the hardirq count (max # of hardirqs: 256)
+ *
+ * - ( bit 26 is the PREEMPT_ACTIVE flag. )
+ *
+ * PREEMPT_MASK: 0x000000ff
+ * SOFTIRQ_MASK: 0x0000ff00
+ * HARDIRQ_MASK: 0x00010000
  */
-#define in_interrupt() ({ int __cpu = smp_processor_id(); \
-	(local_irq_count(__cpu) + local_bh_count(__cpu) != 0); })
-
-#define in_irq() (local_irq_count(smp_processor_id()) != 0)
-  
-#ifndef CONFIG_SMP
 
-#define hardirq_trylock(cpu)	(local_irq_count(cpu) == 0)
-#define hardirq_endlock(cpu)	do { } while (0)
-  
-#define hardirq_enter(cpu)	(local_irq_count(cpu)++)
-#define hardirq_exit(cpu)	(local_irq_count(cpu)--)
-
-#define synchronize_irq()	do { } while (0)
-
-#else	/* CONFIG_SMP */
-
-#include <asm/atomic.h>
-#include <asm/smp.h>
-
-extern atomic_t global_irq_holder;
-extern atomic_t global_irq_lock;
-extern atomic_t global_irq_count;
-
-static inline void release_irqlock(int cpu)
-{
-	/* if we didn't own the irq lock, just ignore.. */
-	if (atomic_read(&global_irq_holder) ==  cpu) {
-		atomic_set(&global_irq_holder,NO_PROC_ID);
-		atomic_set(&global_irq_lock,0);
-	}
-}
-
-static inline void hardirq_enter(int cpu)
-{
-        ++local_irq_count(cpu);
-	atomic_inc(&global_irq_count);
-}
-
-static inline void hardirq_exit(int cpu)
-{
-	atomic_dec(&global_irq_count);
-        --local_irq_count(cpu);
-}
-
-static inline int hardirq_trylock(int cpu)
-{
-	return !atomic_read(&global_irq_count) &&
-	       !atomic_read(&global_irq_lock);
-}
+#define PREEMPT_BITS	8
+#define SOFTIRQ_BITS	8
+#define HARDIRQ_BITS	1
+
+#define PREEMPT_SHIFT	0
+#define SOFTIRQ_SHIFT	(PREEMPT_SHIFT + PREEMPT_BITS)
+#define HARDIRQ_SHIFT	(SOFTIRQ_SHIFT + SOFTIRQ_BITS)
+
+#define __MASK(x)	((1UL << (x))-1)
+
+#define PREEMPT_MASK	(__MASK(PREEMPT_BITS) << PREEMPT_SHIFT)
+#define SOFTIRQ_MASK	(__MASK(SOFTIRQ_BITS) << SOFTIRQ_SHIFT)
+#define HARDIRQ_MASK	(__MASK(HARDIRQ_BITS) << HARDIRQ_SHIFT)
+
+#define hardirq_count()	(preempt_count() & HARDIRQ_MASK)
+#define softirq_count()	(preempt_count() & SOFTIRQ_MASK)
+#define irq_count()	(preempt_count() & (HARDIRQ_MASK | SOFTIRQ_MASK))
+
+#define PREEMPT_OFFSET	(1UL << PREEMPT_SHIFT)
+#define SOFTIRQ_OFFSET	(1UL << SOFTIRQ_SHIFT)
+#define HARDIRQ_OFFSET	(1UL << HARDIRQ_SHIFT)
 
-#define hardirq_endlock(cpu)	do { } while (0)
+/*
+ * Are we doing bottom half or hardware interrupt processing?
+ * Are we in a softirq context? Interrupt context?
+ */
+#define in_irq()		(hardirq_count())
+#define in_softirq()		(softirq_count())
+#define in_interrupt()		(irq_count())
+
+
+#define hardirq_trylock()	(!in_interrupt())
+#define hardirq_endlock()	do { } while (0)
+
+#define irq_enter()		(preempt_count() += HARDIRQ_OFFSET)
+
+extern void do_call_softirq(void);
+
+#if CONFIG_PREEMPT
+# define in_atomic()	(in_interrupt() || preempt_count() == PREEMPT_ACTIVE)
+# define IRQ_EXIT_OFFSET (HARDIRQ_OFFSET-1)
+#else
+# define in_atomic()	(preempt_count() != 0)
+# define IRQ_EXIT_OFFSET HARDIRQ_OFFSET
+#endif
+
+#define irq_exit()							\
+do {									\
+	preempt_count() -= IRQ_EXIT_OFFSET;				\
+	if (!in_interrupt() && softirq_pending(smp_processor_id()))	\
+		/* Use the async. stack for softirq */			\
+		do_call_softirq();					\
+	preempt_enable_no_resched();					\
+} while (0)
 
-extern void synchronize_irq(void);
+#ifndef CONFIG_SMP
+# define synchronize_irq(irq)	barrier()
+#else
+  extern void synchronize_irq(unsigned int irq);
+#endif /* CONFIG_SMP */
 
-#endif	/* CONFIG_SMP */
+extern void show_stack(unsigned long * esp);
 
 #endif /* __ASM_HARDIRQ_H */
diff -Nru a/include/asm-s390/irq.h b/include/asm-s390/irq.h
--- a/include/asm-s390/irq.h	Mon Oct  7 11:25:37 2002
+++ b/include/asm-s390/irq.h	Mon Oct  7 11:25:37 2002
@@ -637,6 +637,12 @@
                               const char              *devname,
                               void                    *dev_id);
 
+extern int s390_request_console_irq (int irq,
+			  void (*handler) (int, void *, struct pt_regs *),
+			  unsigned long irqflags,
+			  const char *devname,
+			  void *dev_id);
+
 extern int set_cons_dev(int irq);
 extern int wait_cons_dev(int irq);
 extern schib_t *s390_get_schib( int irq );
@@ -860,28 +866,8 @@
      __u32 vrdccrft : 8;    /* real device feature (output) */
      } __attribute__ ((packed,aligned(4))) diag210_t;
 
-void VM_virtual_device_info( __u16      devno,   /* device number */
-                             senseid_t *ps );    /* ptr to senseID data */
+extern int diag210( diag210_t * addr);
 
-extern __inline__ int diag210( diag210_t * addr)
-{
-        int ccode;
-
-        __asm__ __volatile__(
-#ifdef CONFIG_ARCH_S390X
-                "   sam31\n"
-                "   diag  %1,0,0x210\n"
-                "   sam64\n"
-#else
-                "   diag  %1,0,0x210\n"
-#endif
-                "   ipm   %0\n"
-                "   srl   %0,28"
-                : "=d" (ccode) 
-		: "a" (addr)
-                : "cc" );
-        return ccode;
-}
 extern __inline__ int chsc( chsc_area_t * chsc_area)
 {
 	int cc;
@@ -895,67 +881,6 @@
 		: "cc" );
 	
 	return cc;
-}
-
-/*
- * Various low-level irq details needed by irq.c, process.c,
- * time.c, io_apic.c and smp.c
- *
- * Interrupt entry/exit code at both C and assembly level
- */
-
-#ifdef CONFIG_SMP
-
-#include <asm/atomic.h>
-
-static inline void irq_enter(int cpu, unsigned int irq)
-{
-        hardirq_enter(cpu);
-        while (atomic_read(&global_irq_lock) != 0) {
-                eieio();
-        }
-}
-
-static inline void irq_exit(int cpu, unsigned int irq)
-{
-        hardirq_exit(cpu);
-        release_irqlock(cpu);
-}
-
-
-#else
-
-#define irq_enter(cpu, irq)     (++local_irq_count(cpu))
-#define irq_exit(cpu, irq)      (--local_irq_count(cpu))
-
-#endif
-
-#define __STR(x) #x
-#define STR(x) __STR(x)
-
-/*
- * x86 profiling function, SMP safe. We might want to do this in
- * assembly totally?
- * is this ever used anyway?
- */
-extern char _stext;
-static inline void s390_do_profile (unsigned long addr)
-{
-        if (prof_buffer && current->pid) {
-#ifndef CONFIG_ARCH_S390X
-                addr &= 0x7fffffff;
-#endif
-                addr -= (unsigned long) &_stext;
-                addr >>= prof_shift;
-                /*
-                 * Don't ignore out-of-bounds EIP values silently,
-                 * put them into the last histogram slot, so if
-                 * present, they will show up as a sharp peak.
-                 */
-                if (addr > prof_len-1)
-                        addr = prof_len-1;
-                atomic_inc((atomic_t *)&prof_buffer[addr]);
-        }
 }
 
 #include <asm/s390io.h>
diff -Nru a/include/asm-s390/kmap_types.h b/include/asm-s390/kmap_types.h
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/include/asm-s390/kmap_types.h	Mon Oct  7 11:25:40 2002
@@ -0,0 +1,21 @@
+#ifdef __KERNEL__
+#ifndef _ASM_KMAP_TYPES_H
+#define _ASM_KMAP_TYPES_H
+
+enum km_type {
+	KM_BOUNCE_READ,
+	KM_SKB_SUNRPC_DATA,
+	KM_SKB_DATA_SOFTIRQ,
+	KM_USER0,
+	KM_USER1,
+	KM_BIO_SRC_IRQ,
+	KM_BIO_DST_IRQ,
+	KM_PTE0,
+	KM_PTE1,
+	KM_IRQ0,
+	KM_IRQ1,
+	KM_TYPE_NR
+};
+
+#endif
+#endif /* __KERNEL__ */
diff -Nru a/include/asm-s390/lowcore.h b/include/asm-s390/lowcore.h
--- a/include/asm-s390/lowcore.h	Mon Oct  7 11:25:35 2002
+++ b/include/asm-s390/lowcore.h	Mon Oct  7 11:25:35 2002
@@ -52,41 +52,12 @@
 
 #define __LC_PFAULT_INTPARM             0x080
 
-/* interrupt handler start with all io, external and mcck interrupt disabled */
-
-#define _RESTART_PSW_MASK    0x00080000
-#define _EXT_PSW_MASK        0x04080000
-#define _PGM_PSW_MASK        0x04080000
-#define _SVC_PSW_MASK        0x04080000
-#define _MCCK_PSW_MASK       0x04080000
-#define _IO_PSW_MASK         0x04080000
-#define _USER_PSW_MASK       0x070DC000/* DAT, IO, EXT, Home-space         */
-#define _WAIT_PSW_MASK       0x070E0000/* DAT, IO, EXT, Wait, Home-space   */
-#define _DW_PSW_MASK         0x000A0000/* disabled wait PSW mask           */
-
-#define _PRIMARY_MASK        0x0000    /* MASK for SACF                    */
-#define _SECONDARY_MASK      0x0100    /* MASK for SACF                    */
-#define _ACCESS_MASK         0x0200    /* MASK for SACF                    */
-#define _HOME_MASK           0x0300    /* MASK for SACF                    */
-
-#define _PSW_PRIM_SPACE_MODE 0x00000000
-#define _PSW_SEC_SPACE_MODE  0x00008000
-#define _PSW_ACC_REG_MODE    0x00004000
-#define _PSW_HOME_SPACE_MODE 0x0000C000
-
-#define _PSW_WAIT_MASK_BIT   0x00020000 /* Wait bit */
-#define _PSW_IO_MASK_BIT     0x02000000 /* IO bit */
-#define _PSW_IO_WAIT         0x02020000 /* IO & Wait bit */
-
-/* we run in 31 Bit mode */
-#define _ADDR_31             0x80000000
-
 #ifndef __ASSEMBLY__
 
 #include <linux/config.h>
-#include <asm/processor.h>
 #include <linux/types.h>
 #include <asm/atomic.h>
+#include <asm/processor.h>
 #include <asm/sigp.h>
 
 void restart_int_handler(void);
@@ -176,25 +147,16 @@
 	__u8         pad12[0x1000-0xe04];      /* 0xe04 */
 } __attribute__((packed)); /* End structure*/
 
+#define S390_lowcore (*((struct _lowcore *) 0))
+extern struct _lowcore *lowcore_ptr[];
+
 extern __inline__ void set_prefix(__u32 address)
 {
         __asm__ __volatile__ ("spx %0" : : "m" (address) : "memory" );
 }
 
-#define S390_lowcore (*((struct _lowcore *) 0))
-extern struct _lowcore *lowcore_ptr[];
-
-#ifndef CONFIG_SMP
-#define get_cpu_lowcore(cpu)      (&S390_lowcore)
-#define safe_get_cpu_lowcore(cpu) (&S390_lowcore)
-#else
-#define get_cpu_lowcore(cpu)      (lowcore_ptr[(cpu)])
-#define safe_get_cpu_lowcore(cpu) \
-        ((cpu) == smp_processor_id() ? &S390_lowcore : lowcore_ptr[(cpu)])
-#endif
-#endif /* __ASSEMBLY__ */
-
 #define __PANIC_MAGIC           0xDEADC0DE
 
 #endif
 
+#endif
diff -Nru a/include/asm-s390/param.h b/include/asm-s390/param.h
--- a/include/asm-s390/param.h	Mon Oct  7 11:25:25 2002
+++ b/include/asm-s390/param.h	Mon Oct  7 11:25:25 2002
@@ -9,6 +9,12 @@
 #ifndef _ASMS390_PARAM_H
 #define _ASMS390_PARAM_H
 
+#ifdef __KERNEL__
+# define HZ		100		/* Internal kernel timer frequency */
+# define USER_HZ	100		/* .. some user interfaces are in "ticks" */
+# define CLOCKS_PER_SEC	(USER_HZ)	/* like times() */
+#endif
+
 #ifndef HZ
 #define HZ 100
 #endif
@@ -24,9 +30,5 @@
 #endif
 
 #define MAXHOSTNAMELEN	64	/* max length of hostname */
-
-#ifdef __KERNEL__
-# define CLOCKS_PER_SEC		HZ	/* frequency at which times() counts */
-#endif
 
 #endif
diff -Nru a/include/asm-s390/pgalloc.h b/include/asm-s390/pgalloc.h
--- a/include/asm-s390/pgalloc.h	Mon Oct  7 11:25:36 2002
+++ b/include/asm-s390/pgalloc.h	Mon Oct  7 11:25:36 2002
@@ -16,6 +16,8 @@
 #include <linux/config.h>
 #include <asm/processor.h>
 #include <linux/threads.h>
+#include <linux/gfp.h>
+#include <linux/mm.h>
 
 #define check_pgt_cache()	do {} while (0)
 
diff -Nru a/include/asm-s390/pgtable.h b/include/asm-s390/pgtable.h
--- a/include/asm-s390/pgtable.h	Mon Oct  7 11:25:36 2002
+++ b/include/asm-s390/pgtable.h	Mon Oct  7 11:25:36 2002
@@ -176,6 +176,8 @@
 #define _SEGMENT_TABLE  (_USER_SEG_TABLE_LEN|0x80000000|0x100)
 #define _KERNSEG_TABLE  (_KERNEL_SEG_TABLE_LEN)
 
+#define USER_STD_MASK           0x00000080UL
+
 /*
  * No mapping available
  */
diff -Nru a/include/asm-s390/processor.h b/include/asm-s390/processor.h
--- a/include/asm-s390/processor.h	Mon Oct  7 11:25:35 2002
+++ b/include/asm-s390/processor.h	Mon Oct  7 11:25:35 2002
@@ -101,8 +101,8 @@
 
 /* need to define ... */
 #define start_thread(regs, new_psw, new_stackp) do {            \
-        regs->psw.mask  = _USER_PSW_MASK;                       \
-        regs->psw.addr  = new_psw | 0x80000000;                 \
+        regs->psw.mask  = PSW_USER_BITS;                        \
+        regs->psw.addr  = new_psw | PSW_ADDR_AMODE31;           \
         regs->gprs[15]  = new_stackp ;                          \
 } while (0)
 
@@ -137,19 +137,6 @@
 #define cpu_relax()	barrier()
 
 /*
- * Set of msr bits that gdb can change on behalf of a process.
- */
-/* Only let our hackers near the condition codes */
-#define PSW_MASK_DEBUGCHANGE    0x00003000UL
-/* Don't let em near the addressing mode either */    
-#define PSW_ADDR_DEBUGCHANGE    0x7FFFFFFFUL
-#define PSW_ADDR_MASK           0x7FFFFFFFUL
-/* Program event recording mask */    
-#define PSW_PER_MASK            0x40000000UL
-#define USER_STD_MASK           0x00000080UL
-#define PSW_PROBLEM_STATE       0x00010000UL
-
-/*
  * Set PSW mask to specified value, while leaving the
  * PSW addr pointing to the next instruction.
  */
@@ -178,7 +165,8 @@
 	unsigned long reg;
 	psw_t wait_psw;
 
-	wait_psw.mask = 0x070e0000;
+	wait_psw.mask = PSW_BASE_BITS | PSW_MASK_IO | PSW_MASK_EXT |
+		PSW_MASK_MCHECK | PSW_MASK_WAIT;
 	asm volatile (
 		"    basr %0,0\n"
 		"0:  la   %0,1f-0b(%0)\n"
@@ -200,7 +188,7 @@
         psw_t *dw_psw = (psw_t *)(((unsigned long) &psw_buffer+sizeof(psw_t)-1)
                                   & -sizeof(psw_t));
 
-        dw_psw->mask = 0x000a0000;
+        dw_psw->mask = PSW_BASE_BITS | PSW_MASK_WAIT;
         dw_psw->addr = code;
         /* 
          * Store status and then load disabled wait psw,
diff -Nru a/include/asm-s390/ptrace.h b/include/asm-s390/ptrace.h
--- a/include/asm-s390/ptrace.h	Mon Oct  7 11:25:26 2002
+++ b/include/asm-s390/ptrace.h	Mon Oct  7 11:25:26 2002
@@ -114,7 +114,6 @@
 #include <linux/config.h>
 #include <linux/stddef.h>
 #include <linux/types.h>
-
 #include <asm/setup.h>
 
 /* this typedef defines how a Program Status Word looks like */
@@ -124,10 +123,32 @@
         __u32   addr;
 } __attribute__ ((aligned(8))) psw_t;
 
-#ifdef __KERNEL__
-#define FIX_PSW(addr) ((unsigned long)(addr)|0x80000000UL)
-#define ADDR_BITS_REMOVE(addr) ((addr)&0x7fffffff)
-#endif
+#define PSW_MASK_PER		0x40000000UL
+#define PSW_MASK_DAT		0x04000000UL
+#define PSW_MASK_IO		0x02000000UL
+#define PSW_MASK_EXT		0x01000000UL
+#define PSW_MASK_KEY		0x00F00000UL
+#define PSW_MASK_MCHECK		0x00040000UL
+#define PSW_MASK_WAIT		0x00020000UL
+#define PSW_MASK_PSTATE		0x00010000UL
+#define PSW_MASK_ASC		0x0000C000UL
+#define PSW_MASK_CC		0x00003000UL
+#define PSW_MASK_PM		0x00000F00UL
+
+#define PSW_ADDR_AMODE31	0x80000000UL
+#define PSW_ADDR_INSN		0x7FFFFFFFUL
+
+#define PSW_BASE_BITS		0x00080000UL
+
+#define PSW_ASC_PRIMARY		0x00000000UL
+#define PSW_ASC_ACCREG		0x00004000UL
+#define PSW_ASC_SECONDARY	0x00008000UL
+#define PSW_ASC_HOME		0x0000C000UL
+
+#define PSW_KERNEL_BITS	(PSW_BASE_BITS | PSW_MASK_DAT | PSW_ASC_PRIMARY)
+#define PSW_USER_BITS	(PSW_BASE_BITS | PSW_MASK_DAT | PSW_ASC_HOME | \
+			 PSW_MASK_IO | PSW_MASK_EXT | PSW_MASK_MCHECK | \
+			 PSW_MASK_PSTATE)
 
 typedef union
 {
@@ -328,8 +349,8 @@
 };
 
 #ifdef __KERNEL__
-#define user_mode(regs) (((regs)->psw.mask & PSW_PROBLEM_STATE) != 0)
-#define instruction_pointer(regs) ((regs)->psw.addr)
+#define user_mode(regs) (((regs)->psw.mask & PSW_MASK_PSTATE) != 0)
+#define instruction_pointer(regs) ((regs)->psw.addr & PSW_MASK_INSN)
 extern void show_regs(struct pt_regs * regs);
 #endif
 
diff -Nru a/include/asm-s390/rwsem.h b/include/asm-s390/rwsem.h
--- a/include/asm-s390/rwsem.h	Mon Oct  7 11:25:35 2002
+++ b/include/asm-s390/rwsem.h	Mon Oct  7 11:25:35 2002
@@ -48,9 +48,11 @@
 
 struct rwsem_waiter;
 
-extern struct rw_semaphore *rwsem_down_read_failed(struct rw_semaphore *sem);
-extern struct rw_semaphore *rwsem_down_write_failed(struct rw_semaphore *sem);
+extern struct rw_semaphore *rwsem_down_read_failed(struct rw_semaphore *);
+extern struct rw_semaphore *rwsem_down_write_failed(struct rw_semaphore *);
 extern struct rw_semaphore *rwsem_wake(struct rw_semaphore *);
+extern struct rw_semaphore *rwsem_downgrade_wake(struct rw_semaphore *);
+extern struct rw_semaphore *rwsem_downgrade_write(struct rw_semaphore *);
 
 /*
  * the semaphore definition
@@ -105,6 +107,27 @@
 }
 
 /*
+ * trylock for reading -- returns 1 if successful, 0 if contention
+ */
+static inline int __down_read_trylock(struct rw_semaphore *sem)
+{
+	signed long old, new;
+
+	__asm__ __volatile__(
+		"   l    %0,0(%2)\n"
+		"0: ltr  %1,%0\n"
+		"   jm   1f\n"
+		"   ahi  %1,%3\n"
+		"   cs   %0,%1,0(%2)\n"
+		"   jl   0b\n"
+		"1:"
+                : "=&d" (old), "=&d" (new)
+		: "a" (&sem->count), "i" (RWSEM_ACTIVE_READ_BIAS)
+		: "cc", "memory" );
+	return old >= 0 ? 1 : 0;
+}
+
+/*
  * lock for writing
  */
 static inline void __down_write(struct rw_semaphore *sem)
@@ -126,6 +149,26 @@
 }
 
 /*
+ * trylock for writing -- returns 1 if successful, 0 if contention
+ */
+static inline int __down_write_trylock(struct rw_semaphore *sem)
+{
+	signed long old;
+
+	__asm__ __volatile__(
+		"   l    %0,0(%1)\n"
+		"0: ltr  %0,%0\n"
+		"   jnz  1f\n"
+		"   cs   %0,%2,0(%1)\n"
+		"   jl   0b\n"
+		"1:"
+                : "=&d" (old)
+		: "a" (&sem->count), "d" (RWSEM_ACTIVE_WRITE_BIAS)
+		: "cc", "memory" );
+	return (old == RWSEM_UNLOCKED_VALUE) ? 1 : 0;
+}
+
+/*
  * unlock after reading
  */
 static inline void __up_read(struct rw_semaphore *sem)
@@ -166,6 +209,27 @@
 	if (new < 0)
 		if ((new & RWSEM_ACTIVE_MASK) == 0)
 			rwsem_wake(sem);
+}
+
+/*
+ * downgrade write lock to read lock
+ */
+static inline void __downgrade_write(struct rw_semaphore *sem)
+{
+	signed long old, new, tmp;
+
+	tmp = -RWSEM_WAITING_BIAS;
+	__asm__ __volatile__(
+		"   l    %0,0(%2)\n"
+		"0: lr   %1,%0\n"
+		"   a    %1,%3\n"
+		"   cs   %0,%1,0(%2)\n"
+		"   jl   0b"
+                : "=&d" (old), "=&d" (new)
+		: "a" (&sem->count), "m" (tmp)
+		: "cc", "memory" );
+	if (new > 1) // FIXME: is this correct ?!?
+		rwsem_downgrade_wake(sem);
 }
 
 /*
diff -Nru a/include/asm-s390/s390io.h b/include/asm-s390/s390io.h
--- a/include/asm-s390/s390io.h	Mon Oct  7 11:25:36 2002
+++ b/include/asm-s390/s390io.h	Mon Oct  7 11:25:36 2002
@@ -9,22 +9,25 @@
 #ifndef __s390io_h
 #define __s390io_h
 
+#include <linux/device.h>
+
 /*
  * IRQ data structure used by I/O subroutines
  *
  * Note : If bit flags are added, the "unused" value must be
  *        decremented accordingly !
  */
-typedef struct _ioinfo {
+typedef struct subchannel {
      unsigned int  irq;           /* aka. subchannel number */
      spinlock_t    irq_lock;      /* irq lock */
-     void          *private_data; /* pointer to private data */
-
-     struct _ioinfo *prev;
-     struct _ioinfo *next;
 
      __u8          st;            /* subchannel type */
 
+     void          *private_data; /* pointer to private data */
+
+     struct subchannel *prev;
+     struct subchannel *next;
+
      union {
         unsigned int info;
         struct {
@@ -78,8 +81,26 @@
      unsigned long qflag;         /* queued flags */
      __u8          qlpm;          /* queued logical path mask */
      ssd_info_t    ssd_info;      /* subchannel description */
+     struct device dev;		  /* entry in device tree */
+} __attribute__ ((aligned(8))) ioinfo_t;
+
 
-   } __attribute__ ((aligned(8))) ioinfo_t;
+/*
+ * There are four different subchannel types, but we are currently
+ * only interested in I/O subchannels. This means there is only
+ * one subchannel_driver, other subchannels belonging to css_bus_type
+ * are simply ignored.
+ */
+struct subchannel_driver {
+	enum {
+		SUBCHANNEL_TYPE_IO      = 0,
+		SUBCHANNEL_TYPE_CHSC    = 1,
+		SUBCHANNEL_TYPE_MESSAGE = 2,
+		SUBCHANNEL_TYPE_ADM     = 3,
+	} st;			  /* subchannel type */
+	struct device_driver drv; /* entry in driver tree */
+};
+extern struct bus_type css_bus_type;
 
 #define IOINFO_FLAGS_BUSY    0x80000000
 #define IOINFO_FLAGS_OPER    0x40000000
diff -Nru a/include/asm-s390/smp.h b/include/asm-s390/smp.h
--- a/include/asm-s390/smp.h	Mon Oct  7 11:25:35 2002
+++ b/include/asm-s390/smp.h	Mon Oct  7 11:25:35 2002
@@ -11,7 +11,7 @@
 
 #include <linux/config.h>
 #include <linux/threads.h>
-#include <linux/ptrace.h>
+#include <linux/bitops.h>
 
 #if defined(__KERNEL__) && defined(CONFIG_SMP) && !defined(__ASSEMBLY__)
 
@@ -29,6 +29,7 @@
 } sigp_info;
 
 extern volatile unsigned long cpu_online_map;
+extern unsigned long cpu_possible_map;
 
 #define NO_PROC_ID		0xFF		/* No processor magic marker */
 
@@ -46,14 +47,20 @@
 
 #define smp_processor_id() (current_thread_info()->cpu)
 
-extern __inline__ int cpu_logical_map(int cpu)
+#define cpu_online(cpu) (cpu_online_map & (1<<(cpu)))
+#define cpu_possible(cpu) (cpu_possible_map & (1<<(cpu)))
+
+extern inline unsigned int num_online_cpus(void)
 {
-        return cpu;
+	return hweight32(cpu_online_map);
 }
 
-extern __inline__ int cpu_number_map(int cpu)
+extern inline int any_online_cpu(unsigned int mask)
 {
-        return cpu;
+	if (mask & cpu_online_map)
+		return __ffs(mask & cpu_online_map);
+
+	return -1;
 }
 
 extern __inline__ __u16 hard_smp_processor_id(void)
diff -Nru a/include/asm-s390/softirq.h b/include/asm-s390/softirq.h
--- a/include/asm-s390/softirq.h	Mon Oct  7 11:25:26 2002
+++ b/include/asm-s390/softirq.h	Mon Oct  7 11:25:26 2002
@@ -9,34 +9,27 @@
 #ifndef __ASM_SOFTIRQ_H
 #define __ASM_SOFTIRQ_H
 
-#ifndef __LINUX_SMP_H
 #include <linux/smp.h>
-#endif
+#include <linux/preempt.h>
 
 #include <asm/atomic.h>
 #include <asm/hardirq.h>
 #include <asm/lowcore.h>
 
-#define __cpu_bh_enable(cpu) \
-                do { barrier(); local_bh_count(cpu)--; } while (0)
-#define cpu_bh_disable(cpu) \
-                do { local_bh_count(cpu)++; barrier(); } while (0)
-
-#define local_bh_disable()      cpu_bh_disable(smp_processor_id())
-#define __local_bh_enable()     __cpu_bh_enable(smp_processor_id())
-
-#define in_softirq() (local_bh_count(smp_processor_id()) != 0)
+#define local_bh_disable() \
+		do { preempt_count() += SOFTIRQ_OFFSET; barrier(); } while (0)
+#define __local_bh_enable() \
+		do { barrier(); preempt_count() -= SOFTIRQ_OFFSET; } while (0)
 
 extern void do_call_softirq(void);
 
-#define local_bh_enable()			          	        \
-do {							                \
-        unsigned int *ptr = &local_bh_count(smp_processor_id());        \
-        barrier();                                                      \
-        if (!--*ptr)							\
-		if (softirq_pending(smp_processor_id()))		\
-			/* Use the async. stack for softirq */		\
-			do_call_softirq();				\
+#define local_bh_enable()						\
+do {									\
+	__local_bh_enable();						\
+	if (!in_interrupt() && softirq_pending(smp_processor_id()))	\
+		/* Use the async. stack for softirq */			\
+		do_call_softirq();					\
+	preempt_check_resched();					\
 } while (0)
 
 #endif	/* __ASM_SOFTIRQ_H */
diff -Nru a/include/asm-s390/spinlock.h b/include/asm-s390/spinlock.h
--- a/include/asm-s390/spinlock.h	Mon Oct  7 11:25:40 2002
+++ b/include/asm-s390/spinlock.h	Mon Oct  7 11:25:40 2002
@@ -76,6 +76,8 @@
 
 #define rwlock_init(x)	do { *(x) = RW_LOCK_UNLOCKED; } while(0)
 
+#define rwlock_is_locked(x) ((x)->lock != 0)
+
 #define _raw_read_lock(rw)   \
         asm volatile("   l     2,0(%1)\n"   \
                      "   j     1f\n"     \
diff -Nru a/include/asm-s390/system.h b/include/asm-s390/system.h
--- a/include/asm-s390/system.h	Mon Oct  7 11:25:39 2002
+++ b/include/asm-s390/system.h	Mon Oct  7 11:25:39 2002
@@ -12,96 +12,207 @@
 #define __ASM_SYSTEM_H
 
 #include <linux/config.h>
+#include <linux/kernel.h>
 #include <asm/types.h>
+#include <asm/ptrace.h>
+#include <asm/setup.h>
+
 #ifdef __KERNEL__
-#include <asm/lowcore.h>
-#endif
-#include <linux/kernel.h>
+
+struct task_struct;
+
+extern struct task_struct *resume(void *, void *);
+
+static inline void save_fp_regs(s390_fp_regs *fpregs)
+{
+	asm volatile (
+		"   std   0,8(%0)\n"
+		"   std   2,24(%0)\n"
+		"   std   4,40(%0)\n"
+		"   std   6,56(%0)"
+		: : "a" (fpregs) : "memory" );
+	if (!MACHINE_HAS_IEEE)
+		return;
+	asm volatile(
+		"   stfpc 0(%0)\n"
+		"   std   1,16(%0)\n"
+		"   std   3,32(%0)\n"
+		"   std   5,48(%0)\n"
+		"   std   7,64(%0)\n"
+		"   std   8,72(%0)\n"
+		"   std   9,80(%0)\n"
+		"   std   10,88(%0)\n"
+		"   std   11,96(%0)\n"
+		"   std   12,104(%0)\n"
+		"   std   13,112(%0)\n"
+		"   std   14,120(%0)\n"
+		"   std   15,128(%0)\n"
+		: : "a" (fpregs) : "memory" );
+}
+
+static inline void restore_fp_regs(s390_fp_regs *fpregs)
+{
+	asm volatile (
+		"   ld    0,8(%0)\n"
+		"   ld    2,24(%0)\n"
+		"   ld    4,40(%0)\n"
+		"   ld    6,56(%0)"
+		: : "a" (fpregs));
+	if (!MACHINE_HAS_IEEE)
+		return;
+	asm volatile(
+		"   lfpc  0(%0)\n"
+		"   ld    1,16(%0)\n"
+		"   ld    3,32(%0)\n"
+		"   ld    5,48(%0)\n"
+		"   ld    7,64(%0)\n"
+		"   ld    8,72(%0)\n"
+		"   ld    9,80(%0)\n"
+		"   ld    10,88(%0)\n"
+		"   ld    11,96(%0)\n"
+		"   ld    12,104(%0)\n"
+		"   ld    13,112(%0)\n"
+		"   ld    14,120(%0)\n"
+		"   ld    15,128(%0)\n"
+		: : "a" (fpregs));
+}
 
 #define switch_to(prev,next,last) do {					     \
 	if (prev == next)						     \
 		break;							     \
-	save_fp_regs1(&prev->thread.fp_regs);				     \
-	restore_fp_regs1(&next->thread.fp_regs);			     \
+	save_fp_regs(&prev->thread.fp_regs);				     \
+	restore_fp_regs(&next->thread.fp_regs);				     \
 	resume(prev,next);						     \
 } while (0)
 
-struct task_struct;
-
 #define nop() __asm__ __volatile__ ("nop")
 
-#define xchg(ptr,x) ((__typeof__(*(ptr)))__xchg((unsigned long)(x),(ptr),sizeof(*(ptr))))
-
-extern void __misaligned_u16(void);
-extern void __misaligned_u32(void);
+#define xchg(ptr,x) \
+  ((__typeof__(*(ptr)))__xchg((unsigned long)(x),(ptr),sizeof(*(ptr))))
 
 static inline unsigned long __xchg(unsigned long x, void * ptr, int size)
 {
+	unsigned long addr, old;
+	int shift;
+
         switch (size) {
-                case 1:
-                        asm volatile (
-                                "   lhi   1,3\n"
-                                "   nr    1,%0\n"     /* isolate last 2 bits */
-                                "   xr    %0,1\n"     /* align ptr */
-                                "   bras  2,0f\n"
-                                "   icm   1,8,3(%1)\n"   /* for ptr&3 == 0 */
-                                "   stcm  0,8,3(%1)\n"
-                                "   icm   1,4,3(%1)\n"   /* for ptr&3 == 1 */
-                                "   stcm  0,4,3(%1)\n"
-                                "   icm   1,2,3(%1)\n"   /* for ptr&3 == 2 */
-                                "   stcm  0,2,3(%1)\n"
-                                "   icm   1,1,3(%1)\n"   /* for ptr&3 == 3 */
-                                "   stcm  0,1,3(%1)\n"
-                                "0: sll   1,3\n"
-                                "   la    2,0(1,2)\n" /* r2 points to an icm */
-                                "   l     0,0(%0)\n"  /* get fullword */
-                                "1: lr    1,0\n"      /* cs loop */
-                                "   ex    0,0(2)\n"   /* insert x */
-                                "   cs    0,1,0(%0)\n"
-                                "   jl    1b\n"
-                                "   ex    0,4(2)"     /* store *ptr to x */
-                                : "+a&" (ptr) : "a" (&x)
-                                : "memory", "cc", "0", "1", "2");
-			break;
-                case 2:
-                        if(((__u32)ptr)&1)
-				__misaligned_u16();
-                        asm volatile (
-                                "   lhi   1,2\n"
-                                "   nr    1,%0\n"     /* isolate bit 2^1 */
-                                "   xr    %0,1\n"     /* align ptr */
-                                "   bras  2,0f\n"
-                                "   icm   1,12,2(%1)\n"   /* for ptr&2 == 0 */
-                                "   stcm  0,12,2(%1)\n"
-                                "   icm   1,3,2(%1)\n"    /* for ptr&2 == 1 */
-                                "   stcm  0,3,2(%1)\n"
-                                "0: sll   1,2\n"
-                                "   la    2,0(1,2)\n" /* r2 points to an icm */
-                                "   l     0,0(%0)\n"  /* get fullword */
-                                "1: lr    1,0\n"      /* cs loop */
-                                "   ex    0,0(2)\n"   /* insert x */
-                                "   cs    0,1,0(%0)\n"
-                                "   jl    1b\n"
-                                "   ex    0,4(2)"     /* store *ptr to x */
-                                : "+a&" (ptr) : "a" (&x)
-                                : "memory", "cc", "0", "1", "2");
-                        break;
-                case 4:
-                        if(((__u32)ptr)&3)
-				__misaligned_u32();
-                        asm volatile (
-                                "    l   0,0(%1)\n"
-                                "0:  cs  0,%0,0(%1)\n"
-                                "    jl  0b\n"
-                                "    lr  %0,0\n"
-                                : "+d&" (x) : "a" (ptr)
-                                : "memory", "cc", "0" );
-                        break;
+	case 1:
+		addr = (unsigned long) ptr;
+		shift = (3 ^ (addr & 3)) << 3;
+		addr ^= addr & 3;
+		asm volatile(
+			"    l   %0,0(%3)\n"
+			"0:  lr  0,%0\n"
+			"    nr  0,%2\n"
+			"    or  0,%1\n"
+			"    cs  %0,0,0(%3)\n"
+			"    jl  0b\n"
+			: "=&d" (old)
+			: "d" (x << shift), "d" (~(255 << shift)), "a" (addr)
+			: "memory", "cc", "0" );
+		x = old >> shift;
+		break;
+	case 2:
+		addr = (unsigned long) ptr;
+		shift = (2 ^ (addr & 2)) << 3;
+		addr ^= addr & 2;
+		asm volatile(
+			"    l   %0,0(%3)\n"
+			"0:  lr  0,%0\n"
+			"    nr  0,%2\n"
+			"    or  0,%1\n"
+			"    cs  %0,0,0(%3)\n"
+			"    jl  0b\n"
+			: "=&d" (old) 
+			: "d" (x << shift), "d" (~(65535 << shift)), "a" (addr)
+			: "memory", "cc", "0" );
+		x = old >> shift;
+		break;
+	case 4:
+		asm volatile (
+			"    l   %0,0(%2)\n"
+			"0:  cs  %0,%1,0(%2)\n"
+			"    jl  0b\n"
+			: "=&d" (old) : "d" (x), "a" (ptr)
+			: "memory", "cc", "0" );
+		x = old;
+		break;
         }
         return x;
 }
 
 /*
+ * Atomic compare and exchange.  Compare OLD with MEM, if identical,
+ * store NEW in MEM.  Return the initial value in MEM.  Success is
+ * indicated by comparing RETURN with OLD.
+ */
+
+#define __HAVE_ARCH_CMPXCHG 1
+
+#define cmpxchg(ptr,o,n)\
+	((__typeof__(*(ptr)))__cmpxchg((ptr),(unsigned long)(o),\
+					(unsigned long)(n),sizeof(*(ptr))))
+
+static inline unsigned long
+__cmpxchg(volatile void *ptr, unsigned long old, unsigned long new, int size)
+{
+	unsigned long addr, prev, tmp;
+	int shift;
+
+        switch (size) {
+	case 1:
+		addr = (unsigned long) ptr;
+		shift = (3 ^ (addr & 3)) << 3;
+		addr ^= addr & 3;
+		asm volatile(
+			"    l   %0,0(%4)\n"
+			"0:  nr  %0,%5\n"
+                        "    lr  %1,%0\n"
+			"    or  %0,%2\n"
+			"    or  %1,%3\n"
+			"    cs  %0,%1,0(%4)\n"
+			"    jnl 1f\n"
+			"    xr  %1,%0\n"
+			"    nr  %1,%5\n"
+			"    jnz 0b\n"
+			"1:"
+			: "=&d" (prev), "=&d" (tmp)
+			: "d" (old << shift), "d" (new << shift), "a" (ptr),
+			  "d" (~(255 << shift))
+			: "memory", "cc" );
+		return prev >> shift;
+	case 2:
+		addr = (unsigned long) ptr;
+		shift = (2 ^ (addr & 2)) << 3;
+		addr ^= addr & 2;
+		asm volatile(
+			"    l   %0,0(%4)\n"
+			"0:  nr  %0,%5\n"
+                        "    lr  %1,%0\n"
+			"    or  %0,%2\n"
+			"    or  %1,%3\n"
+			"    cs  %0,%1,0(%4)\n"
+			"    jnl 1f\n"
+			"    xr  %1,%0\n"
+			"    nr  %1,%5\n"
+			"    jnz 0b\n"
+			"1:"
+			: "=&d" (prev), "=&d" (tmp)
+			: "d" (old << shift), "d" (new << shift), "a" (ptr),
+			  "d" (~(65535 << shift))
+			: "memory", "cc" );
+		return prev >> shift;
+	case 4:
+		asm volatile (
+			"    cs  %0,%2,0(%3)\n"
+			: "=&d" (prev) : "0" (old), "d" (new), "a" (ptr)
+			: "memory", "cc" );
+		return prev;
+        }
+        return old;
+}
+
+/*
  * Force strict CPU ordering.
  * And yes, this is required on UP too when we're talking
  * to devices.
@@ -130,22 +241,29 @@
 #define local_irq_enable() ({ \
         __u8 __dummy; \
         __asm__ __volatile__ ( \
-                "stosm 0(%0),0x03" : : "a" (&__dummy) : "memory"); \
+                "stosm 0(%1),0x03" : "=m" (__dummy) : "a" (&__dummy) ); \
         })
 
 #define local_irq_disable() ({ \
         __u32 __flags; \
         __asm__ __volatile__ ( \
-                "stnsm 0(%0),0xFC" : : "a" (&__flags) : "memory"); \
+                "stnsm 0(%1),0xFC" : "=m" (__flags) : "a" (&__flags) ); \
         __flags; \
         })
 
 #define local_save_flags(x) \
-        __asm__ __volatile__("stosm 0(%0),0" : : "a" (&x) : "memory")
+        __asm__ __volatile__("stosm 0(%1),0" : "=m" (x) : "a" (&x) )
 
 #define local_irq_restore(x) \
         __asm__ __volatile__("ssm   0(%0)" : : "a" (&x) : "memory")
 
+#define irqs_disabled()			\
+({					\
+	unsigned long flags;		\
+	local_save_flags(flags);	\
+        !((flags >> 24) & 3);		\
+})
+
 #define __load_psw(psw) \
 	__asm__ __volatile__("lpsw 0(%0)" : : "a" (&psw) : "cc" );
 
@@ -210,16 +328,6 @@
 
 #ifdef CONFIG_SMP
 
-extern void __global_cli(void);
-extern void __global_sti(void);
-
-extern unsigned long __global_save_flags(void);
-extern void __global_restore_flags(unsigned long);
-#define cli() __global_cli()
-#define sti() __global_sti()
-#define save_flags(x) ((x)=__global_save_flags())
-#define restore_flags(x) __global_restore_flags(x)
-
 extern void smp_ctl_set_bit(int cr, int bit);
 extern void smp_ctl_clear_bit(int cr, int bit);
 #define ctl_set_bit(cr, bit) smp_ctl_set_bit(cr, bit)
@@ -227,32 +335,16 @@
 
 #else
 
-#define cli() local_irq_disable()
-#define sti() local_irq_enable()
-#define save_flags(x) local_save_flags(x)
-#define restore_flags(x) local_irq_restore(x)
-
 #define ctl_set_bit(cr, bit) __ctl_set_bit(cr, bit)
 #define ctl_clear_bit(cr, bit) __ctl_clear_bit(cr, bit)
 
-
-#endif
-
-#ifdef __KERNEL__
-extern struct task_struct *resume(void *, void *);
-
-extern int save_fp_regs1(s390_fp_regs *fpregs);
-extern void save_fp_regs(s390_fp_regs *fpregs);
-extern int restore_fp_regs1(s390_fp_regs *fpregs);
-extern void restore_fp_regs(s390_fp_regs *fpregs);
+#endif /* CONFIG_SMP */
 
 extern void (*_machine_restart)(char *command);
 extern void (*_machine_halt)(void);
 extern void (*_machine_power_off)(void);
 
-#endif
+#endif /* __KERNEL__ */
 
 #endif
-
-
 
diff -Nru a/include/asm-s390/thread_info.h b/include/asm-s390/thread_info.h
--- a/include/asm-s390/thread_info.h	Mon Oct  7 11:25:36 2002
+++ b/include/asm-s390/thread_info.h	Mon Oct  7 11:25:36 2002
@@ -25,11 +25,9 @@
 	struct exec_domain	*exec_domain;	/* execution domain */
 	unsigned long		flags;		/* low level flags */
 	unsigned int		cpu;		/* current CPU */
-	int			preempt_count; /* 0 => preemptable, <0 => BUG */
+	unsigned int		preempt_count; /* 0 => preemptable */
 };
 
-#define PREEMPT_ACTIVE		0x4000000
-
 /*
  * macros/functions for gaining access to the thread information structure
  */
@@ -83,5 +81,7 @@
 #define _TIF_POLLING_NRFLAG	(1<<TIF_POLLING_NRFLAG)
 
 #endif /* __KERNEL__ */
+
+#define PREEMPT_ACTIVE		0x4000000
 
 #endif /* _ASM_THREAD_INFO_H */
diff -Nru a/include/asm-s390/tlbflush.h b/include/asm-s390/tlbflush.h
--- a/include/asm-s390/tlbflush.h	Mon Oct  7 11:25:39 2002
+++ b/include/asm-s390/tlbflush.h	Mon Oct  7 11:25:39 2002
@@ -91,8 +91,7 @@
 
 static inline void __flush_tlb_mm(struct mm_struct * mm)
 {
-	if ((smp_num_cpus > 1) &&
-	    ((atomic_read(&mm->mm_count) != 1) ||
+	if (((atomic_read(&mm->mm_count) != 1) ||
 	     (mm->cpu_vm_mask != (1UL << smp_processor_id())))) {
 		mm->cpu_vm_mask = (1UL << smp_processor_id());
 		global_flush_tlb();
diff -Nru a/include/asm-s390/unistd.h b/include/asm-s390/unistd.h
--- a/include/asm-s390/unistd.h	Mon Oct  7 11:25:40 2002
+++ b/include/asm-s390/unistd.h	Mon Oct  7 11:25:40 2002
@@ -231,6 +231,16 @@
 #define __NR_futex		238
 #define __NR_sched_setaffinity	239
 #define __NR_sched_getaffinity	240
+#define __NR_security		241	/* syscall for security modules */
+/*
+ * Number 242 is reserved for tux
+ */
+#define __NR_io_setup		243
+#define __NR_io_destroy		244
+#define __NR_io_getevents	245
+#define __NR_io_submit		246
+#define __NR_io_cancel		247
+#define __NR_exit_group		248
 
 
 /* user-visible error numbers are in the range -1 - -122: see <asm-s390/errno.h> */
@@ -368,9 +378,6 @@
  * some others too.
  */
 #define __NR__exit __NR_exit
-static inline _syscall0(int,idle)
-static inline _syscall0(int,pause)
-static inline _syscall0(int,sync)
 static inline _syscall0(pid_t,setsid)
 static inline _syscall3(int,write,int,fd,const char *,buf,off_t,count)
 static inline _syscall3(int,read,int,fd,char *,buf,off_t,count)
@@ -380,7 +387,6 @@
 static inline _syscall3(int,open,const char *,file,int,flag,int,mode)
 static inline _syscall1(int,close,int,fd)
 static inline _syscall1(int,_exit,int,exitcode)
-static inline _syscall1(int,delete_module,const char *,name)
 static inline _syscall2(long,stat,char *,filename,struct stat *,statbuf)
 
 struct rusage;
@@ -388,11 +394,6 @@
 static inline pid_t waitpid(int pid, int *wait_stat, int flags)
 {
 	return sys_wait4(pid, wait_stat, flags, NULL);
-}
-
-static inline pid_t wait(int * wait_stat)
-{
-        return waitpid(-1,wait_stat,0);
 }
 
 #endif
diff -Nru a/include/asm-s390x/bitops.h b/include/asm-s390x/bitops.h
--- a/include/asm-s390x/bitops.h	Mon Oct  7 11:25:39 2002
+++ b/include/asm-s390x/bitops.h	Mon Oct  7 11:25:39 2002
@@ -63,8 +63,8 @@
 
 	addr = (unsigned long) ptr;
 #if ALIGN_CS == 1
-	addr ^= addr & 7;		/* align address to 8 */
 	nr += (addr & 7) << 3;		/* add alignment to bit number */
+	addr ^= addr & 7;		/* align address to 8 */
 #endif
 	addr += (nr ^ (nr & 63)) >> 3;	/* calculate address for CS */
 	mask = 1UL << (nr & 63);	/* make OR mask */
@@ -88,8 +88,8 @@
 
 	addr = (unsigned long) ptr;
 #if ALIGN_CS == 1
-	addr ^= addr & 7;		/* align address to 8 */
 	nr += (addr & 7) << 3;		/* add alignment to bit number */
+	addr ^= addr & 7;		/* align address to 8 */
 #endif
 	addr += (nr ^ (nr & 63)) >> 3;	/* calculate address for CS */
 	mask = ~(1UL << (nr & 63));	/* make AND mask */
@@ -113,8 +113,8 @@
 
 	addr = (unsigned long) ptr;
 #if ALIGN_CS == 1
-	addr ^= addr & 7;		/* align address to 8 */
 	nr += (addr & 7) << 3;		/* add alignment to bit number */
+	addr ^= addr & 7;		/* align address to 8 */
 #endif
 	addr += (nr ^ (nr & 63)) >> 3;	/* calculate address for CS */
 	mask = 1UL << (nr & 63);	/* make XOR mask */
@@ -139,8 +139,8 @@
 
 	addr = (unsigned long) ptr;
 #if ALIGN_CS == 1
-	addr ^= addr & 7;		/* align address to 8 */
 	nr += (addr & 7) << 3;		/* add alignment to bit number */
+	addr ^= addr & 7;		/* align address to 8 */
 #endif
 	addr += (nr ^ (nr & 63)) >> 3;	/* calculate address for CS */
 	mask = 1UL << (nr & 63);	/* make OR/test mask */
@@ -166,8 +166,8 @@
 
 	addr = (unsigned long) ptr;
 #if ALIGN_CS == 1
-	addr ^= addr & 7;		/* align address to 8 */
 	nr += (addr & 7) << 3;		/* add alignment to bit number */
+	addr ^= addr & 7;		/* align address to 8 */
 #endif
 	addr += (nr ^ (nr & 63)) >> 3;	/* calculate address for CS */
 	mask = ~(1UL << (nr & 63));	/* make AND mask */
@@ -193,8 +193,8 @@
 
 	addr = (unsigned long) ptr;
 #if ALIGN_CS == 1
-	addr ^= addr & 7;		/* align address to 8 */
 	nr += (addr & 7) << 3;		/* add alignment to bit number */
+	addr ^= addr & 7;		/* align address to 8 */
 #endif
 	addr += (nr ^ (nr & 63)) >> 3;	/* calculate address for CS */
 	mask = 1UL << (nr & 63);	/* make XOR mask */
@@ -811,7 +811,14 @@
  * hweightN: returns the hamming weight (i.e. the number
  * of bits set) of a N-bit word
  */
-
+#define hweight64(x)						\
+({								\
+	unsigned long __x = (x);				\
+	unsigned int __w;					\
+	__w = generic_hweight32((unsigned int) __x);		\
+	__w += generic_hweight32((unsigned int) (__x>>32));	\
+	__w;							\
+})
 #define hweight32(x) generic_hweight32(x)
 #define hweight16(x) generic_hweight16(x)
 #define hweight8(x) generic_hweight8(x)
diff -Nru a/include/asm-s390x/checksum.h b/include/asm-s390x/checksum.h
--- a/include/asm-s390x/checksum.h	Mon Oct  7 11:25:38 2002
+++ b/include/asm-s390x/checksum.h	Mon Oct  7 11:25:38 2002
@@ -27,13 +27,29 @@
  *
  * 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);
+static inline unsigned int
+csum_partial(const unsigned char * buff, int len, unsigned int sum)
+{
+	/*
+	 * Experiments with ethernet and slip connections show that buff
+	 * is aligned on either a 2-byte or 4-byte boundary.
+	 */
+        __asm__ __volatile__ (
+                "    lgr  2,%1\n"    /* address in gpr 2 */
+                "    lgfr 3,%2\n"    /* length in gpr 3 */
+                "0:  cksm %0,2\n"    /* do checksum on longs */
+                "    jo   0b\n"
+                : "+&d" (sum)
+                : "d" (buff), "d" (len)
+                : "cc", "2", "3" );
+        return sum;
+	
+}
 
 /*
  * csum_partial as an inline function
  */
-extern inline unsigned int 
+static inline unsigned int 
 csum_partial_inline(const unsigned char * buff, int len, unsigned int sum)
 {
 	__asm__ __volatile__ (
@@ -55,7 +71,7 @@
  * better 64-bit) boundary
  */
 
-extern inline unsigned int 
+static inline unsigned int 
 csum_partial_copy(const char *src, char *dst, int len,unsigned int sum)
 {
 	memcpy(dst,src,len);
@@ -71,7 +87,7 @@
  * Copy from userspace and compute checksum.  If we catch an exception
  * then zero the rest of the buffer.
  */
-extern inline unsigned int 
+static inline unsigned int 
 csum_partial_copy_from_user (const char *src, char *dst,
                                           int len, unsigned int sum,
                                           int *err_ptr)
@@ -87,7 +103,7 @@
 	return csum_partial(dst, len, sum);
 }
 
-extern inline unsigned int
+static inline unsigned int
 csum_partial_copy_nocheck (const char *src, char *dst, int len, unsigned int sum)
 {
         memcpy(dst,src,len);
@@ -97,7 +113,7 @@
 /*
  *      Fold a partial checksum without adding pseudo headers
  */
-extern inline unsigned short
+static inline unsigned short
 csum_fold(unsigned int sum)
 {
 	__asm__ __volatile__ (
@@ -116,7 +132,7 @@
  *	which always checksum on 4 octet boundaries.
  *
  */
-extern inline unsigned short
+static inline unsigned short
 ip_fast_csum(unsigned char *iph, unsigned int ihl)
 {
 	unsigned long sum;
@@ -137,7 +153,7 @@
  * computes the checksum of the TCP/UDP pseudo-header
  * returns a 32-bit checksum
  */
-extern inline unsigned int 
+static inline unsigned int 
 csum_tcpudp_nofold(unsigned long saddr, unsigned long daddr,
                    unsigned short len, unsigned short proto,
                    unsigned int sum)
@@ -170,7 +186,7 @@
  * returns a 16-bit checksum, already complemented
  */
 
-extern inline unsigned short int
+static inline unsigned short int
 csum_tcpudp_magic(unsigned long saddr, unsigned long daddr,
                   unsigned short len, unsigned short proto,
                   unsigned int sum)
@@ -183,7 +199,7 @@
  * in icmp.c
  */
 
-extern inline unsigned short
+static inline unsigned short
 ip_compute_csum(unsigned char * buff, int len)
 {
 	return csum_fold(csum_partial_inline(buff, len, 0));
diff -Nru a/include/asm-s390x/dasd.h b/include/asm-s390x/dasd.h
--- a/include/asm-s390x/dasd.h	Mon Oct  7 11:25:29 2002
+++ b/include/asm-s390x/dasd.h	Mon Oct  7 11:25:29 2002
@@ -13,6 +13,8 @@
  * 12/06/01 DASD_API_VERSION 2 - binary compatible to 0 (new BIODASDINFO2)
  * 01/23/02 DASD_API_VERSION 3 - added BIODASDPSRD (and BIODASDENAPAV) IOCTL
  * 02/15/02 DASD_API_VERSION 4 - added BIODASDSATTR IOCTL
+ * ##/##/## DASD_API_VERSION 5 - added boxed dasd support TOBEDONE
+ * 21/06/02 DASD_API_VERSION 6 - fixed HDIO_GETGEO: geo.start is in sectors!
  *         
  */
 
@@ -22,7 +24,7 @@
 
 #define DASD_IOCTL_LETTER 'D'
 
-#define DASD_API_VERSION 4
+#define DASD_API_VERSION 6
 
 /* 
  * struct dasd_information2_t
diff -Nru a/include/asm-s390x/debug.h b/include/asm-s390x/debug.h
--- a/include/asm-s390x/debug.h	Mon Oct  7 11:25:35 2002
+++ b/include/asm-s390x/debug.h	Mon Oct  7 11:25:35 2002
@@ -160,7 +160,8 @@
 }
 
 extern debug_entry_t *
-debug_sprintf_event(debug_info_t* id,int level,char *string,...);
+debug_sprintf_event(debug_info_t* id,int level,char *string,...)
+	__attribute__ ((format(printf, 3, 4)));
 
 
 extern inline debug_entry_t* 
@@ -195,7 +196,8 @@
 
 
 extern debug_entry_t *
-debug_sprintf_exception(debug_info_t* id,int level,char *string,...);
+debug_sprintf_exception(debug_info_t* id,int level,char *string,...)
+	__attribute__ ((format(printf, 3, 4)));
 
 int debug_register_view(debug_info_t* id, struct debug_view* view);
 int debug_unregister_view(debug_info_t* id, struct debug_view* view);
diff -Nru a/include/asm-s390x/hardirq.h b/include/asm-s390x/hardirq.h
--- a/include/asm-s390x/hardirq.h	Mon Oct  7 11:25:25 2002
+++ b/include/asm-s390x/hardirq.h	Mon Oct  7 11:25:25 2002
@@ -21,8 +21,6 @@
 /* entry.S is sensitive to the offsets of these fields */
 typedef struct {
 	unsigned int __softirq_pending;
-	unsigned int __local_irq_count;
-	unsigned int __local_bh_count;
 	unsigned int __syscall_count;
 	struct task_struct * __ksoftirqd_task; /* waitqueue is too large */
 } ____cacheline_aligned irq_cpustat_t;
@@ -30,64 +28,82 @@
 #include <linux/irq_cpustat.h>	/* Standard mappings for irq_cpustat_t above */
 
 /*
- * Are we in an interrupt context? Either doing bottom half
- * or hardware interrupt processing?
+ * We put the hardirq and softirq counter into the preemption
+ * counter. The bitmask has the following meaning:
+ *
+ * - bits 0-7 are the preemption count (max preemption depth: 256)
+ * - bits 8-15 are the softirq count (max # of softirqs: 256)
+ * - bits 16-23 are the hardirq count (max # of hardirqs: 256)
+ *
+ * - ( bit 26 is the PREEMPT_ACTIVE flag. )
+ *
+ * PREEMPT_MASK: 0x000000ff
+ * SOFTIRQ_MASK: 0x0000ff00
+ * HARDIRQ_MASK: 0x00010000
  */
-#define in_interrupt() ({ int __cpu = smp_processor_id(); \
-	(local_irq_count(__cpu) + local_bh_count(__cpu) != 0); })
 
-#define in_irq() (local_irq_count(smp_processor_id()) != 0)
+#define PREEMPT_BITS	8
+#define SOFTIRQ_BITS	8
+// FIXME: we have 2^16 i/o and 2^16 external interrupts...
+#define HARDIRQ_BITS	1
+
+#define PREEMPT_SHIFT	0
+#define SOFTIRQ_SHIFT	(PREEMPT_SHIFT + PREEMPT_BITS)
+#define HARDIRQ_SHIFT	(SOFTIRQ_SHIFT + SOFTIRQ_BITS)
+
+#define __MASK(x)	((1UL << (x))-1)
+
+#define PREEMPT_MASK	(__MASK(PREEMPT_BITS) << PREEMPT_SHIFT)
+#define SOFTIRQ_MASK	(__MASK(SOFTIRQ_BITS) << SOFTIRQ_SHIFT)
+#define HARDIRQ_MASK	(__MASK(HARDIRQ_BITS) << HARDIRQ_SHIFT)
+
+#define hardirq_count()	(preempt_count() & HARDIRQ_MASK)
+#define softirq_count()	(preempt_count() & SOFTIRQ_MASK)
+#define irq_count()	(preempt_count() & (HARDIRQ_MASK | SOFTIRQ_MASK))
+
+#define PREEMPT_OFFSET	(1UL << PREEMPT_SHIFT)
+#define SOFTIRQ_OFFSET	(1UL << SOFTIRQ_SHIFT)
+#define HARDIRQ_OFFSET	(1UL << HARDIRQ_SHIFT)
 
-#ifndef CONFIG_SMP
-  
-#define hardirq_trylock(cpu)	(local_irq_count(cpu) == 0)
-#define hardirq_endlock(cpu)	do { } while (0)
-  
-#define hardirq_enter(cpu)	(local_irq_count(cpu)++)
-#define hardirq_exit(cpu)	(local_irq_count(cpu)--)
-
-#define synchronize_irq()	do { } while (0)
+/*
+ * Are we doing bottom half or hardware interrupt processing?
+ * Are we in a softirq context? Interrupt context?
+ */
+#define in_irq()		(hardirq_count())
+#define in_softirq()		(softirq_count())
+#define in_interrupt()		(irq_count())
 
-#else
 
-#include <asm/atomic.h>
-#include <asm/smp.h>
+#define hardirq_trylock()	(!in_interrupt())
+#define hardirq_endlock()	do { } while (0)
 
-extern atomic_t global_irq_holder;
-extern atomic_t global_irq_lock;
-extern atomic_t global_irq_count;
-
-static inline void release_irqlock(int cpu)
-{
-	/* if we didn't own the irq lock, just ignore.. */
-	if (atomic_read(&global_irq_holder) ==  cpu) {
-		atomic_set(&global_irq_holder,NO_PROC_ID);
-                atomic_set(&global_irq_lock,0);
-	}
-}
-
-static inline void hardirq_enter(int cpu)
-{
-        ++local_irq_count(cpu);
-	atomic_inc(&global_irq_count);
-}
-
-static inline void hardirq_exit(int cpu)
-{
-	atomic_dec(&global_irq_count);
-        --local_irq_count(cpu);
-}
-
-static inline int hardirq_trylock(int cpu)
-{
-	return !atomic_read(&global_irq_count) && 
-               !atomic_read(&global_irq_lock);
-}
+#define irq_enter()		(preempt_count() += HARDIRQ_OFFSET)
 
-#define hardirq_endlock(cpu)	do { } while (0)
+extern void do_call_softirq(void);
 
-extern void synchronize_irq(void);
+#if CONFIG_PREEMPT
+# define in_atomic()	(in_interrupt() || preempt_count() == PREEMPT_ACTIVE)
+# define IRQ_EXIT_OFFSET (HARDIRQ_OFFSET-1)
+#else
+# define in_atomic()	(preempt_count() != 0)
+# define IRQ_EXIT_OFFSET HARDIRQ_OFFSET
+#endif
+
+#define irq_exit()							\
+do {									\
+	preempt_count() -= IRQ_EXIT_OFFSET;				\
+	if (!in_interrupt() && softirq_pending(smp_processor_id()))	\
+		/* Use the async. stack for softirq */			\
+		do_call_softirq();					\
+	preempt_enable_no_resched();					\
+} while (0)
 
+#ifndef CONFIG_SMP
+# define synchronize_irq(irq)	barrier()
+#else
+  extern void synchronize_irq(unsigned int irq);
 #endif /* CONFIG_SMP */
+
+extern void show_stack(unsigned long * esp);
 
 #endif /* __ASM_HARDIRQ_H */
diff -Nru a/include/asm-s390x/irq.h b/include/asm-s390x/irq.h
--- a/include/asm-s390x/irq.h	Mon Oct  7 11:25:39 2002
+++ b/include/asm-s390x/irq.h	Mon Oct  7 11:25:39 2002
@@ -637,6 +637,12 @@
                               const char              *devname,
                               void                    *dev_id);
 
+extern int s390_request_console_irq (int irq,
+			  void (*handler) (int, void *, struct pt_regs *),
+			  unsigned long irqflags,
+			  const char *devname,
+			  void *dev_id);
+
 extern int set_cons_dev(int irq);
 extern int wait_cons_dev(int irq);
 extern schib_t *s390_get_schib( int irq );
@@ -860,28 +866,8 @@
      __u32 vrdccrft : 8;    /* real device feature (output) */
      } __attribute__ ((packed,aligned(4))) diag210_t;
 
-void VM_virtual_device_info( __u16      devno,   /* device number */
-                             senseid_t *ps );    /* ptr to senseID data */
+extern int diag210( diag210_t * addr);
 
-extern __inline__ int diag210( diag210_t * addr)
-{
-        int ccode;
-
-        __asm__ __volatile__(
-#ifdef CONFIG_ARCH_S390X
-                "   sam31\n"
-                "   diag  %1,0,0x210\n"
-                "   sam64\n"
-#else
-                "   diag  %1,0,0x210\n"
-#endif
-                "   ipm   %0\n"
-                "   srl   %0,28"
-                : "=d" (ccode) 
-		: "a" (addr)
-                : "cc" );
-        return ccode;
-}
 extern __inline__ int chsc( chsc_area_t * chsc_area)
 {
 	int cc;
@@ -895,67 +881,6 @@
 		: "cc" );
 	
 	return cc;
-}
-
-/*
- * Various low-level irq details needed by irq.c, process.c,
- * time.c, io_apic.c and smp.c
- *
- * Interrupt entry/exit code at both C and assembly level
- */
-
-#ifdef CONFIG_SMP
-
-#include <asm/atomic.h>
-
-static inline void irq_enter(int cpu, unsigned int irq)
-{
-        hardirq_enter(cpu);
-        while (atomic_read(&global_irq_lock) != 0) {
-                eieio();
-        }
-}
-
-static inline void irq_exit(int cpu, unsigned int irq)
-{
-        hardirq_exit(cpu);
-        release_irqlock(cpu);
-}
-
-
-#else
-
-#define irq_enter(cpu, irq)     (++local_irq_count(cpu))
-#define irq_exit(cpu, irq)      (--local_irq_count(cpu))
-
-#endif
-
-#define __STR(x) #x
-#define STR(x) __STR(x)
-
-/*
- * x86 profiling function, SMP safe. We might want to do this in
- * assembly totally?
- * is this ever used anyway?
- */
-extern char _stext;
-static inline void s390_do_profile (unsigned long addr)
-{
-        if (prof_buffer && current->pid) {
-#ifndef CONFIG_ARCH_S390X
-                addr &= 0x7fffffff;
-#endif
-                addr -= (unsigned long) &_stext;
-                addr >>= prof_shift;
-                /*
-                 * Don't ignore out-of-bounds EIP values silently,
-                 * put them into the last histogram slot, so if
-                 * present, they will show up as a sharp peak.
-                 */
-                if (addr > prof_len-1)
-                        addr = prof_len-1;
-                atomic_inc((atomic_t *)&prof_buffer[addr]);
-        }
 }
 
 #include <asm/s390io.h>
diff -Nru a/include/asm-s390x/kmap_types.h b/include/asm-s390x/kmap_types.h
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/include/asm-s390x/kmap_types.h	Mon Oct  7 11:26:06 2002
@@ -0,0 +1,21 @@
+#ifdef __KERNEL__
+#ifndef _ASM_KMAP_TYPES_H
+#define _ASM_KMAP_TYPES_H
+
+enum km_type {
+	KM_BOUNCE_READ,
+	KM_SKB_SUNRPC_DATA,
+	KM_SKB_DATA_SOFTIRQ,
+	KM_USER0,
+	KM_USER1,
+	KM_BIO_SRC_IRQ,
+	KM_BIO_DST_IRQ,
+	KM_PTE0,
+	KM_PTE1,
+	KM_IRQ0,
+	KM_IRQ1,
+	KM_TYPE_NR
+};
+
+#endif
+#endif /* __KERNEL__ */
diff -Nru a/include/asm-s390x/lowcore.h b/include/asm-s390x/lowcore.h
--- a/include/asm-s390x/lowcore.h	Mon Oct  7 11:25:36 2002
+++ b/include/asm-s390x/lowcore.h	Mon Oct  7 11:25:36 2002
@@ -38,6 +38,8 @@
 #define __LC_IO_INT_WORD                0x0C0
 #define __LC_MCCK_CODE                  0x0E8
 
+#define __LC_DIAG44_OPCODE		0x214
+
 #define __LC_SAVE_AREA                  0xC00
 #define __LC_KERNEL_STACK               0xD40
 #define __LC_ASYNC_STACK                0xD48
@@ -54,32 +56,6 @@
 
 #define __LC_PFAULT_INTPARM             0x11B8
 
-/* interrupt handler start with all io, external and mcck interrupt disabled */
-
-#define _RESTART_PSW_MASK    0x0000000180000000
-#define _EXT_PSW_MASK        0x0400000180000000
-#define _PGM_PSW_MASK        0x0400000180000000
-#define _SVC_PSW_MASK        0x0400000180000000
-#define _MCCK_PSW_MASK       0x0400000180000000
-#define _IO_PSW_MASK         0x0400000180000000
-#define _USER_PSW_MASK       0x0705C00180000000
-#define _WAIT_PSW_MASK       0x0706000180000000
-#define _DW_PSW_MASK         0x0002000180000000
-
-#define _PRIMARY_MASK        0x0000    /* MASK for SACF                    */
-#define _SECONDARY_MASK      0x0100    /* MASK for SACF                    */
-#define _ACCESS_MASK         0x0200    /* MASK for SACF                    */
-#define _HOME_MASK           0x0300    /* MASK for SACF                    */
-
-#define _PSW_PRIM_SPACE_MODE 0x0000000000000000
-#define _PSW_SEC_SPACE_MODE  0x0000800000000000
-#define _PSW_ACC_REG_MODE    0x0000400000000000
-#define _PSW_HOME_SPACE_MODE 0x0000C00000000000
-
-#define _PSW_WAIT_MASK_BIT   0x0002000000000000
-#define _PSW_IO_MASK_BIT     0x0200000000000000
-#define _PSW_IO_WAIT         0x0202000000000000
-
 #ifndef __ASSEMBLY__
 
 #include <linux/config.h>
@@ -146,7 +122,8 @@
 	psw_t        io_new_psw;               /* 0x1f0 */
         psw_t        return_psw;               /* 0x200 */
 	__u32        sync_io_word;             /* 0x210 */
-        __u8         pad8[0xc00-0x214];        /* 0x214 */
+	__u32        diag44_opcode;            /* 0x214 */
+        __u8         pad8[0xc00-0x218];        /* 0x218 */
         /* System info area */
 	__u64        save_area[16];            /* 0xc00 */
         __u8         pad9[0xd40-0xc80];        /* 0xc80 */
@@ -191,25 +168,17 @@
 	__u8         pad17[0x2000-0x1400];      /* 0x1400 */
 } __attribute__((packed)); /* End structure*/
 
+#define S390_lowcore (*((struct _lowcore *) 0))
+extern struct _lowcore *lowcore_ptr[];
+
 extern __inline__ void set_prefix(__u32 address)
 {
         __asm__ __volatile__ ("spx %0" : : "m" (address) : "memory" );
 }
 
-#define S390_lowcore (*((struct _lowcore *) 0))
-extern struct _lowcore *lowcore_ptr[];
+#define __PANIC_MAGIC           0xDEADC0DE
 
-#ifndef CONFIG_SMP
-#define get_cpu_lowcore(cpu)      (&S390_lowcore)
-#define safe_get_cpu_lowcore(cpu) (&S390_lowcore)
-#else
-#define get_cpu_lowcore(cpu)      (lowcore_ptr[(cpu)])
-#define safe_get_cpu_lowcore(cpu) \
-        ((cpu) == smp_processor_id() ? &S390_lowcore : lowcore_ptr[(cpu)])
 #endif
-#endif /* __ASSEMBLY__ */
-
-#define __PANIC_MAGIC           0xDEADC0DE
 
 #endif
 
diff -Nru a/include/asm-s390x/param.h b/include/asm-s390x/param.h
--- a/include/asm-s390x/param.h	Mon Oct  7 11:25:25 2002
+++ b/include/asm-s390x/param.h	Mon Oct  7 11:25:25 2002
@@ -9,11 +9,14 @@
 #ifndef _ASMS390_PARAM_H
 #define _ASMS390_PARAM_H
 
-#ifndef HZ
-#define HZ 100
 #ifdef __KERNEL__
-#define hz_to_std(a) (a)
+# define HZ		100		/* Internal kernel timer frequency */
+# define USER_HZ	100		/* .. some user interfaces are in "ticks" */
+# define CLOCKS_PER_SEC	(USER_HZ)	/* like times() */
 #endif
+
+#ifndef HZ
+#define HZ 100
 #endif
 
 #define EXEC_PAGESIZE	4096
@@ -28,8 +31,4 @@
 
 #define MAXHOSTNAMELEN	64	/* max length of hostname */
 
-#ifdef __KERNEL__
-# define CLOCKS_PER_SEC		HZ	/* frequency at which times() counts */
-#endif
-                                 
 #endif
diff -Nru a/include/asm-s390x/pgalloc.h b/include/asm-s390x/pgalloc.h
--- a/include/asm-s390x/pgalloc.h	Mon Oct  7 11:25:26 2002
+++ b/include/asm-s390x/pgalloc.h	Mon Oct  7 11:25:26 2002
@@ -16,6 +16,8 @@
 #include <linux/config.h>
 #include <asm/processor.h>
 #include <linux/threads.h>
+#include <linux/gfp.h>
+#include <linux/mm.h>
 
 #define check_pgt_cache()	do { } while (0)
 
diff -Nru a/include/asm-s390x/pgtable.h b/include/asm-s390x/pgtable.h
--- a/include/asm-s390x/pgtable.h	Mon Oct  7 11:25:33 2002
+++ b/include/asm-s390x/pgtable.h	Mon Oct  7 11:25:33 2002
@@ -168,6 +168,8 @@
 #define _REGION_TABLE       (_REGION_THIRD|_REGION_THIRD_LEN|0x40|0x100)
 #define _KERN_REGION_TABLE  (_REGION_THIRD|_REGION_THIRD_LEN)
 
+#define USER_STD_MASK           0x0000000000000080UL
+
 /* Bits in the storage key */
 #define _PAGE_CHANGED    0x02          /* HW changed bit                   */
 #define _PAGE_REFERENCED 0x04          /* HW referenced bit                */
diff -Nru a/include/asm-s390x/processor.h b/include/asm-s390x/processor.h
--- a/include/asm-s390x/processor.h	Mon Oct  7 11:25:28 2002
+++ b/include/asm-s390x/processor.h	Mon Oct  7 11:25:28 2002
@@ -111,13 +111,13 @@
 
 /* need to define ... */
 #define start_thread(regs, new_psw, new_stackp) do {            \
-        regs->psw.mask  = _USER_PSW_MASK;                       \
+        regs->psw.mask  = PSW_USER_BITS;                        \
         regs->psw.addr  = new_psw;                              \
         regs->gprs[15]  = new_stackp;                           \
 } while (0)
 
 #define start_thread31(regs, new_psw, new_stackp) do {          \
-	regs->psw.mask  = _USER_PSW_MASK & ~(1L << 32);		\
+	regs->psw.mask  = PSW_USER32_BITS;			\
         regs->psw.addr  = new_psw;                              \
         regs->gprs[15]  = new_stackp;                           \
 } while (0)
@@ -154,19 +154,6 @@
 #define cpu_relax()	barrier()
 
 /*
- * Set of msr bits that gdb can change on behalf of a process.
- */
-/* Only let our hackers near the condition codes */
-#define PSW_MASK_DEBUGCHANGE    0x0000300000000000UL
-/* Don't let em near the addressing mode either */    
-#define PSW_ADDR_DEBUGCHANGE    0xFFFFFFFFFFFFFFFFUL
-#define PSW_ADDR_MASK           0xFFFFFFFFFFFFFFFFUL
-/* Program event recording mask */    
-#define PSW_PER_MASK            0x4000000000000000UL
-#define USER_STD_MASK           0x0000000000000080UL
-#define PSW_PROBLEM_STATE       0x0001000000000000UL
-
-/*
  * Set PSW mask to specified value, while leaving the
  * PSW addr pointing to the next instruction.
  */
@@ -194,7 +181,8 @@
 	unsigned long reg;
 	psw_t wait_psw;
 
-	wait_psw.mask = 0x0706000180000000;
+	wait_psw.mask = PSW_BASE_BITS | PSW_MASK_IO | PSW_MASK_EXT |
+		PSW_MASK_MCHECK | PSW_MASK_WAIT;
 	asm volatile (
 		"    larl  %0,0f\n"
 		"    stg   %0,8(%1)\n"
@@ -214,7 +202,7 @@
         psw_t *dw_psw = (psw_t *)(((unsigned long) &psw_buffer+sizeof(psw_t)-1)
                                   & -sizeof(psw_t));
 
-        dw_psw->mask = 0x0002000180000000;
+        dw_psw->mask = PSW_BASE_BITS | PSW_MASK_WAIT;
         dw_psw->addr = code;
         /* 
          * Store status and then load disabled wait psw,
diff -Nru a/include/asm-s390x/ptrace.h b/include/asm-s390x/ptrace.h
--- a/include/asm-s390x/ptrace.h	Mon Oct  7 11:25:26 2002
+++ b/include/asm-s390x/ptrace.h	Mon Oct  7 11:25:26 2002
@@ -104,10 +104,33 @@
         __u64   addr;
 } __attribute__ ((aligned(8))) psw_t;
 
-#ifdef __KERNEL__
-#define FIX_PSW(addr) ((unsigned long)(addr))
-#define ADDR_BITS_REMOVE(addr) ((addr))
-#endif
+#define PSW_MASK_PER		0x4000000000000000UL
+#define PSW_MASK_DAT		0x0400000000000000UL
+#define PSW_MASK_IO		0x0200000000000000UL
+#define PSW_MASK_EXT		0x0100000000000000UL
+#define PSW_MASK_KEY		0x00F0000000000000UL
+#define PSW_MASK_MCHECK		0x0004000000000000UL
+#define PSW_MASK_WAIT		0x0002000000000000UL
+#define PSW_MASK_PSTATE		0x0001000000000000UL
+#define PSW_MASK_ASC		0x0000C00000000000UL
+#define PSW_MASK_CC		0x0000300000000000UL
+#define PSW_MASK_PM		0x00000F0000000000UL
+
+#define PSW_BASE_BITS		0x0000000180000000UL
+#define PSW_BASE32_BITS		0x0000000080000000UL
+
+#define PSW_ASC_PRIMARY		0x0000000000000000UL
+#define PSW_ASC_ACCREG		0x0000400000000000UL
+#define PSW_ASC_SECONDARY	0x0000800000000000UL
+#define PSW_ASC_HOME		0x0000C00000000000UL
+
+#define PSW_KERNEL_BITS	(PSW_BASE_BITS | PSW_MASK_DAT | PSW_ASC_PRIMARY)
+#define PSW_USER_BITS	(PSW_BASE_BITS | PSW_MASK_DAT | PSW_ASC_HOME | \
+			 PSW_MASK_IO | PSW_MASK_EXT | PSW_MASK_MCHECK | \
+			 PSW_MASK_PSTATE)
+#define PSW_USER32_BITS (PSW_BASE32_BITS | PSW_MASK_DAT | PSW_ASC_HOME | \
+			 PSW_MASK_IO | PSW_MASK_EXT | PSW_MASK_MCHECK | \
+			 PSW_MASK_PSTATE)
 
 typedef union
 {
@@ -309,7 +332,7 @@
 };
 
 #ifdef __KERNEL__
-#define user_mode(regs) (((regs)->psw.mask & PSW_PROBLEM_STATE) != 0)
+#define user_mode(regs) (((regs)->psw.mask & PSW_MASK_PSTATE) != 0)
 #define instruction_pointer(regs) ((regs)->psw.addr)
 extern void show_regs(struct pt_regs * regs);
 #endif
diff -Nru a/include/asm-s390x/rwsem.h b/include/asm-s390x/rwsem.h
--- a/include/asm-s390x/rwsem.h	Mon Oct  7 11:25:28 2002
+++ b/include/asm-s390x/rwsem.h	Mon Oct  7 11:25:28 2002
@@ -48,9 +48,11 @@
 
 struct rwsem_waiter;
 
-extern struct rw_semaphore *rwsem_down_read_failed(struct rw_semaphore *sem);
-extern struct rw_semaphore *rwsem_down_write_failed(struct rw_semaphore *sem);
+extern struct rw_semaphore *rwsem_down_read_failed(struct rw_semaphore *);
+extern struct rw_semaphore *rwsem_down_write_failed(struct rw_semaphore *);
 extern struct rw_semaphore *rwsem_wake(struct rw_semaphore *);
+extern struct rw_semaphore *rwsem_downgrade_wake(struct rw_semaphore *);
+extern struct rw_semaphore *rwsem_downgrade_write(struct rw_semaphore *);
 
 /*
  * the semaphore definition
@@ -105,6 +107,27 @@
 }
 
 /*
+ * trylock for reading -- returns 1 if successful, 0 if contention
+ */
+static inline int __down_read_trylock(struct rw_semaphore *sem)
+{
+	signed long old, new;
+
+	__asm__ __volatile__(
+		"   lg   %0,0(%2)\n"
+		"0: ltgr %1,%0\n"
+		"   jm   1f\n"
+		"   aghi %1,%3\n"
+		"   csg  %0,%1,0(%2)\n"
+		"   jl   0b\n"
+		"1:"
+                : "=&d" (old), "=&d" (new)
+		: "a" (&sem->count), "i" (RWSEM_ACTIVE_READ_BIAS)
+		: "cc", "memory" );
+	return old >= 0 ? 1 : 0;
+}
+
+/*
  * lock for writing
  */
 static inline void __down_write(struct rw_semaphore *sem)
@@ -126,6 +149,26 @@
 }
 
 /*
+ * trylock for writing -- returns 1 if successful, 0 if contention
+ */
+static inline int __down_write_trylock(struct rw_semaphore *sem)
+{
+	signed long old;
+
+	__asm__ __volatile__(
+		"   lg   %0,0(%1)\n"
+		"0: ltgr %0,%0\n"
+		"   jnz  1f\n"
+		"   csg  %0,%2,0(%1)\n"
+		"   jl   0b\n"
+		"1:"
+                : "=&d" (old)
+		: "a" (&sem->count), "d" (RWSEM_ACTIVE_WRITE_BIAS)
+		: "cc", "memory" );
+	return (old == RWSEM_UNLOCKED_VALUE) ? 1 : 0;
+}
+
+/*
  * unlock after reading
  */
 static inline void __up_read(struct rw_semaphore *sem)
@@ -166,6 +209,27 @@
 	if (new < 0)
 		if ((new & RWSEM_ACTIVE_MASK) == 0)
 			rwsem_wake(sem);
+}
+
+/*
+ * downgrade write lock to read lock
+ */
+static inline void __downgrade_write(struct rw_semaphore *sem)
+{
+	signed long old, new, tmp;
+
+	tmp = -RWSEM_WAITING_BIAS;
+	__asm__ __volatile__(
+		"   lg   %0,0(%2)\n"
+		"0: lgr  %1,%0\n"
+		"   ag   %1,%3\n"
+		"   csg  %0,%1,0(%2)\n"
+		"   jl   0b"
+                : "=&d" (old), "=&d" (new)
+		: "a" (&sem->count), "m" (tmp)
+		: "cc", "memory" );
+	if (new > 1) // FIXME: is this correct ?!?
+		rwsem_downgrade_wake(sem);
 }
 
 /*
diff -Nru a/include/asm-s390x/s390io.h b/include/asm-s390x/s390io.h
--- a/include/asm-s390x/s390io.h	Mon Oct  7 11:25:40 2002
+++ b/include/asm-s390x/s390io.h	Mon Oct  7 11:25:40 2002
@@ -9,21 +9,24 @@
 #ifndef __s390io_h
 #define __s390io_h
 
+#include <linux/device.h>
+
 /*
  * IRQ data structure used by I/O subroutines
  *
  * Note : If bit flags are added, the "unused" value must be
  *        decremented accordingly !
  */
-typedef struct _ioinfo {
+typedef struct subchannel {
      unsigned int  irq;           /* aka. subchannel number */
      spinlock_t    irq_lock;      /* irq lock */
-     void          *private_data; /* pointer to private data */
 
-     struct _ioinfo *prev;
-     struct _ioinfo *next;
+     __u8          st;            /* subchannel type */
+
+     void          *private_data; /* pointer to private data */
 
-     __u8          st;            /* subchannel type */	
+     struct subchannel *prev;
+     struct subchannel *next;
 
      union {
         unsigned int info;
@@ -78,8 +81,26 @@
      unsigned long qflag;         /* queued flags */
      __u8          qlpm;          /* queued logical path mask */
      ssd_info_t    ssd_info;      /* subchannel description */
+     struct device dev;		  /* entry in device tree */
+} __attribute__ ((aligned(8))) ioinfo_t;
 
-   } __attribute__ ((aligned(8))) ioinfo_t;
+
+/*
+ * There are four different subchannel types, but we are currently
+ * only interested in I/O subchannels. This means there is only
+ * one subchannel_driver, other subchannels belonging to css_bus_type
+ * are simply ignored.
+ */
+struct subchannel_driver {
+	enum {
+		SUBCHANNEL_TYPE_IO      = 0,
+		SUBCHANNEL_TYPE_CHSC    = 1,
+		SUBCHANNEL_TYPE_MESSAGE = 2,
+		SUBCHANNEL_TYPE_ADM     = 3,
+	} st;			  /* subchannel type */
+	struct device_driver drv; /* entry in driver tree */
+};
+extern struct bus_type css_bus_type;
 
 #define IOINFO_FLAGS_BUSY    0x80000000
 #define IOINFO_FLAGS_OPER    0x40000000
diff -Nru a/include/asm-s390x/setup.h b/include/asm-s390x/setup.h
--- a/include/asm-s390x/setup.h	Mon Oct  7 11:25:36 2002
+++ b/include/asm-s390x/setup.h	Mon Oct  7 11:25:36 2002
@@ -25,11 +25,12 @@
  */
 extern unsigned long machine_flags;
 
-#define MACHINE_IS_VM    (machine_flags & 1)
-#define MACHINE_IS_P390  (machine_flags & 4)
-#define MACHINE_HAS_MVPG (machine_flags & 16)
+#define MACHINE_IS_VM		(machine_flags & 1)
+#define MACHINE_IS_P390		(machine_flags & 4)
+#define MACHINE_HAS_MVPG	(machine_flags & 16)
+#define MACHINE_HAS_DIAG44	(machine_flags & 32)
 
-#define MACHINE_HAS_HWC  (!MACHINE_IS_P390)
+#define MACHINE_HAS_HWC		(!MACHINE_IS_P390)
 
 /*
  * Console mode. Override with conmode=
diff -Nru a/include/asm-s390x/smp.h b/include/asm-s390x/smp.h
--- a/include/asm-s390x/smp.h	Mon Oct  7 11:25:35 2002
+++ b/include/asm-s390x/smp.h	Mon Oct  7 11:25:35 2002
@@ -11,7 +11,7 @@
 
 #include <linux/config.h>
 #include <linux/threads.h>
-#include <linux/ptrace.h>
+#include <linux/bitops.h>
 
 #if defined(__KERNEL__) && defined(CONFIG_SMP) && !defined(__ASSEMBLY__)
 
@@ -29,6 +29,7 @@
 } sigp_info;
 
 extern volatile unsigned long cpu_online_map;
+extern volatile unsigned long cpu_possible_map;
 
 #define NO_PROC_ID		0xFF		/* No processor magic marker */
 
@@ -46,14 +47,20 @@
 
 #define smp_processor_id() (current_thread_info()->cpu)
 
-extern __inline__ int cpu_logical_map(int cpu)
+#define cpu_online(cpu) (cpu_online_map & (1<<(cpu)))
+#define cpu_possible(cpu) (cpu_possible_map & (1<<(cpu)))
+
+extern inline unsigned int num_online_cpus(void)
 {
-        return cpu;
+	return hweight64(cpu_online_map);
 }
 
-extern __inline__ int cpu_number_map(int cpu)
+extern inline int any_online_cpu(unsigned int mask)
 {
-        return cpu;
+	if (mask & cpu_online_map)
+		return __ffs(mask & cpu_online_map);
+
+	return -1;
 }
 
 extern __inline__ __u16 hard_smp_processor_id(void)
diff -Nru a/include/asm-s390x/softirq.h b/include/asm-s390x/softirq.h
--- a/include/asm-s390x/softirq.h	Mon Oct  7 11:25:26 2002
+++ b/include/asm-s390x/softirq.h	Mon Oct  7 11:25:26 2002
@@ -9,34 +9,27 @@
 #ifndef __ASM_SOFTIRQ_H
 #define __ASM_SOFTIRQ_H
 
-#ifndef __LINUX_SMP_H
 #include <linux/smp.h>
-#endif
+#include <linux/preempt.h>
 
 #include <asm/atomic.h>
 #include <asm/hardirq.h>
 #include <asm/lowcore.h>
 
-#define __cpu_bh_enable(cpu) \
-                do { barrier(); local_bh_count(cpu)--; } while (0)
-#define cpu_bh_disable(cpu) \
-                do { local_bh_count(cpu)++; barrier(); } while (0)
-
-#define local_bh_disable()      cpu_bh_disable(smp_processor_id())
-#define __local_bh_enable()     __cpu_bh_enable(smp_processor_id())
-
-#define in_softirq() (local_bh_count(smp_processor_id()) != 0)
+#define local_bh_disable() \
+		do { preempt_count() += SOFTIRQ_OFFSET; barrier(); } while (0)
+#define __local_bh_enable() \
+		do { barrier(); preempt_count() -= SOFTIRQ_OFFSET; } while (0)
 
 extern void do_call_softirq(void);
 
-#define local_bh_enable()			          	        \
-do {							                \
-        unsigned int *ptr = &local_bh_count(smp_processor_id());        \
-        barrier();                                                      \
-        if (!--*ptr)							\
-		if (softirq_pending(smp_processor_id()))		\
-			/* Use the async. stack for softirq */		\
-			do_call_softirq();				\
+#define local_bh_enable()						\
+do {									\
+	__local_bh_enable();						\
+	if (!in_interrupt() && softirq_pending(smp_processor_id()))	\
+		/* Use the async. stack for softirq */			\
+		do_call_softirq();					\
+	preempt_check_resched();					\
 } while (0)
 
 #endif	/* __ASM_SOFTIRQ_H */
diff -Nru a/include/asm-s390x/spinlock.h b/include/asm-s390x/spinlock.h
--- a/include/asm-s390x/spinlock.h	Mon Oct  7 11:25:26 2002
+++ b/include/asm-s390x/spinlock.h	Mon Oct  7 11:25:26 2002
@@ -12,6 +12,20 @@
 #define __ASM_SPINLOCK_H
 
 /*
+ * Grmph, take care of %&#! user space programs that include
+ * asm/spinlock.h. The diagnose is only available in kernel
+ * context.
+ */
+#ifdef __KERNEL__
+#include <asm/lowcore.h>
+#define __DIAG44_INSN "ex"
+#define __DIAG44_OPERAND __LC_DIAG44_OPCODE
+#else
+#define __DIAG44_INSN "#"
+#define __DIAG44_OPERAND 0
+#endif
+
+/*
  * Simple spin lock operations.  There are two variants, one clears IRQ's
  * on the local processor, one does not.
  *
@@ -31,12 +45,13 @@
 {
 	unsigned long reg1, reg2;
         __asm__ __volatile("    bras  %1,1f\n"
-                           "0:  # diag  0,0,68\n"
+                           "0:  " __DIAG44_INSN " 0,%4\n"
                            "1:  slr   %0,%0\n"
                            "    cs    %0,%1,0(%3)\n"
                            "    jl    0b\n"
                            : "=&d" (reg1), "=&d" (reg2), "+m" (lp->lock)
-                           : "a" (&lp->lock) : "cc" );
+                           : "a" (&lp->lock), "i" (__DIAG44_OPERAND)
+			   : "cc" );
 }
 
 extern inline int _raw_spin_trylock(spinlock_t *lp)
@@ -76,46 +91,52 @@
 
 #define rwlock_init(x)	do { *(x) = RW_LOCK_UNLOCKED; } while(0)
 
+#define rwlock_is_locked(x) ((x)->lock != 0)
+
 #define _raw_read_lock(rw)   \
         asm volatile("   lg    2,0(%1)\n"   \
                      "   j     1f\n"     \
-                     "0: # diag  0,0,68\n" \
+                     "0: " __DIAG44_INSN " 0,%2\n" \
                      "1: nihh  2,0x7fff\n" /* clear high (=write) bit */ \
                      "   la    3,1(2)\n"   /* one more reader */  \
                      "   csg   2,3,0(%1)\n" /* try to write new value */ \
                      "   jl    0b"       \
-                     : "+m" ((rw)->lock) : "a" (&(rw)->lock) \
+                     : "+m" ((rw)->lock) \
+		     : "a" (&(rw)->lock), "i" (__DIAG44_OPERAND) \
 		     : "2", "3", "cc" )
 
 #define _raw_read_unlock(rw) \
         asm volatile("   lg    2,0(%1)\n"   \
                      "   j     1f\n"     \
-                     "0: # diag  0,0,68\n" \
+                     "0: " __DIAG44_INSN " 0,%2\n" \
                      "1: lgr   3,2\n"    \
                      "   bctgr 3,0\n"    /* one less reader */ \
                      "   csg   2,3,0(%1)\n" \
                      "   jl    0b"       \
-                     : "+m" ((rw)->lock) : "a" (&(rw)->lock) \
+                     : "+m" ((rw)->lock) \
+		     : "a" (&(rw)->lock), "i" (__DIAG44_OPERAND) \
 		     : "2", "3", "cc" )
 
 #define _raw_write_lock(rw) \
         asm volatile("   llihh 3,0x8000\n" /* new lock value = 0x80...0 */ \
                      "   j     1f\n"       \
-                     "0: # diag  0,0,68\n"   \
+                     "0: " __DIAG44_INSN " 0,%2\n"   \
                      "1: slgr  2,2\n"      /* old lock value must be 0 */ \
                      "   csg   2,3,0(%1)\n" \
                      "   jl    0b"         \
-                     : "+m" ((rw)->lock) : "a" (&(rw)->lock) \
+                     : "+m" ((rw)->lock) \
+		     : "a" (&(rw)->lock), "i" (__DIAG44_OPERAND) \
 		     : "2", "3", "cc" )
 
 #define _raw_write_unlock(rw) \
         asm volatile("   slgr  3,3\n"      /* new lock value = 0 */ \
                      "   j     1f\n"       \
-                     "0: # diag  0,0,68\n"   \
+                     "0: " __DIAG44_INSN " 0,%2\n"   \
                      "1: llihh 2,0x8000\n" /* old lock value must be 0x8..0 */\
                      "   csg   2,3,0(%1)\n"   \
                      "   jl    0b"         \
-                     : "+m" ((rw)->lock) : "a" (&(rw)->lock) \
+                     : "+m" ((rw)->lock) \
+		     : "a" (&(rw)->lock), "i" (__DIAG44_OPERAND) \
 		     : "2", "3", "cc" )
 
 #endif /* __ASM_SPINLOCK_H */
diff -Nru a/include/asm-s390x/system.h b/include/asm-s390x/system.h
--- a/include/asm-s390x/system.h	Mon Oct  7 11:25:39 2002
+++ b/include/asm-s390x/system.h	Mon Oct  7 11:25:39 2002
@@ -12,13 +12,64 @@
 #define __ASM_SYSTEM_H
 
 #include <linux/config.h>
+#include <linux/kernel.h>
 #include <asm/types.h>
+#include <asm/ptrace.h>
+#include <asm/setup.h>
+
 #ifdef __KERNEL__
-#include <asm/lowcore.h>
-#endif
-#include <linux/kernel.h>
 
-#define switch_to(prev,next),last do {					     \
+struct task_struct;
+
+extern struct task_struct *resume(void *, void *);
+
+static inline void save_fp_regs(s390_fp_regs *fpregs)
+{
+	asm volatile (
+		"   stfpc 0(%0)\n"
+		"   std   0,8(%0)\n"
+		"   std   1,16(%0)\n"
+		"   std   2,24(%0)\n"
+		"   std   3,32(%0)\n"
+		"   std   4,40(%0)\n"
+		"   std   5,48(%0)\n"
+		"   std   6,56(%0)\n"
+		"   std   7,64(%0)\n"
+		"   std   8,72(%0)\n"
+		"   std   9,80(%0)\n"
+		"   std   10,88(%0)\n"
+		"   std   11,96(%0)\n"
+		"   std   12,104(%0)\n"
+		"   std   13,112(%0)\n"
+		"   std   14,120(%0)\n"
+		"   std   15,128(%0)\n"
+		: : "a" (fpregs) : "memory" );
+}
+
+static inline void restore_fp_regs(s390_fp_regs *fpregs)
+{
+	asm volatile (
+		"   lfpc  0(%0)\n"
+		"   ld    0,8(%0)\n"
+		"   ld    1,16(%0)\n"
+		"   ld    2,24(%0)\n"
+		"   ld    3,32(%0)\n"
+		"   ld    4,40(%0)\n"
+		"   ld    5,48(%0)\n"
+		"   ld    6,56(%0)\n"
+		"   ld    7,64(%0)\n"
+		"   ld    8,72(%0)\n"
+		"   ld    9,80(%0)\n"
+		"   ld    10,88(%0)\n"
+		"   ld    11,96(%0)\n"
+		"   ld    12,104(%0)\n"
+		"   ld    13,112(%0)\n"
+		"   ld    14,120(%0)\n"
+		"   ld    15,128(%0)\n"
+		: : "a" (fpregs));
+}
+
+#define switch_to(prev,next,last) do {					     \
 	if (prev == next)						     \
 		break;							     \
 	save_fp_regs(&prev->thread.fp_regs);				     \
@@ -26,8 +77,6 @@
 	resume(prev,next);						     \
 } while (0)
 
-struct task_struct;
-
 #define nop() __asm__ __volatile__ ("nop")
 
 #define xchg(ptr,x) \
@@ -39,82 +88,142 @@
 
 static inline unsigned long __xchg(unsigned long x, void * ptr, int size)
 {
+	unsigned long addr, old;
+	int shift;
+
         switch (size) {
-                case 1:
-                        asm volatile (
-                                "   lghi  1,3\n"
-                                "   nr    1,%0\n"     /* isolate last 2 bits */
-                                "   xr    %0,1\n"     /* align ptr */
-                                "   bras  2,0f\n"
-                                "   icm   1,8,7(%1)\n"   /* for ptr&3 == 0 */
-                                "   stcm  0,8,7(%1)\n"
-                                "   icm   1,4,7(%1)\n"   /* for ptr&3 == 1 */
-                                "   stcm  0,4,7(%1)\n"
-                                "   icm   1,2,7(%1)\n"   /* for ptr&3 == 2 */
-                                "   stcm  0,2,7(%1)\n"
-                                "   icm   1,1,7(%1)\n"   /* for ptr&3 == 3 */
-                                "   stcm  0,1,7(%1)\n"
-                                "0: sll   1,3\n"
-                                "   la    2,0(1,2)\n" /* r2 points to an icm */
-                                "   l     0,0(%0)\n"  /* get fullword */
-                                "1: lr    1,0\n"      /* cs loop */
-                                "   ex    0,0(2)\n"   /* insert x */
-                                "   cs    0,1,0(%0)\n"
-                                "   jl    1b\n"
-                                "   ex    0,4(2)"     /* store *ptr to x */
-                                : "+&a" (ptr) : "a" (&x)
-                                : "memory", "cc", "0", "1", "2");
-			break;
-                case 2:
-                        if(((addr_t)ptr)&1)
-				__misaligned_u16();
-                        asm volatile (
-                                "   lghi  1,2\n"
-                                "   nr    1,%0\n"     /* isolate bit 2^1 */
-                                "   xr    %0,1\n"     /* align ptr */
-                                "   bras  2,0f\n"
-                                "   icm   1,12,6(%1)\n"   /* for ptr&2 == 0 */
-                                "   stcm  0,12,6(%1)\n"
-                                "   icm   1,3,2(%1)\n"    /* for ptr&2 == 1 */
-                                "   stcm  0,3,2(%1)\n"
-                                "0: sll   1,2\n"
-                                "   la    2,0(1,2)\n" /* r2 points to an icm */
-                                "   l     0,0(%0)\n"  /* get fullword */
-                                "1: lr    1,0\n"      /* cs loop */
-                                "   ex    0,0(2)\n"   /* insert x */
-                                "   cs    0,1,0(%0)\n"
-                                "   jl    1b\n"
-                                "   ex    0,4(2)"     /* store *ptr to x */
-                                : "+&a" (ptr) : "a" (&x)
-                                : "memory", "cc", "0", "1", "2");
-                        break;
-                case 4:
-                        if(((addr_t)ptr)&3)
-				__misaligned_u32();
-                        asm volatile (
-                                "    l    0,0(%1)\n"
-                                "0:  cs   0,%0,0(%1)\n"
-                                "    jl   0b\n"
-                                "    lgfr %0,0\n"
-                                : "+d" (x) : "a" (ptr)
-                                : "memory", "cc", "0" );
-                        break;
-                case 8:
-                        if(((addr_t)ptr)&7)
-				__misaligned_u64();
-                        asm volatile (
-                                "    lg  0,0(%1)\n"
-                                "0:  csg 0,%0,0(%1)\n"
-                                "    jl  0b\n"
-                                "    lgr %0,0\n"
-                                : "+d" (x) : "a" (ptr)
-                                : "memory", "cc", "0" );
-                        break;
+	case 1:
+		addr = (unsigned long) ptr;
+		shift = (3 ^ (addr & 3)) << 3;
+		addr ^= addr & 3;
+		asm volatile(
+			"    l   %0,0(%3)\n"
+			"0:  lr  0,%0\n"
+			"    nr  0,%2\n"
+			"    or  0,%1\n"
+			"    cs  %0,0,0(%3)\n"
+			"    jl  0b\n"
+			: "=&d" (old)
+			: "d" (x << shift), "d" (~(255 << shift)), "a" (addr)
+			: "memory", "cc", "0" );
+		x = old >> shift;
+		break;
+	case 2:
+		addr = (unsigned long) ptr;
+		shift = (2 ^ (addr & 2)) << 3;
+		addr ^= addr & 2;
+		asm volatile(
+			"    l   %0,0(%3)\n"
+			"0:  lr  0,%0\n"
+			"    nr  0,%2\n"
+			"    or  0,%1\n"
+			"    cs  %0,0,0(%3)\n"
+			"    jl  0b\n"
+			: "=&d" (old) 
+			: "d" (x << shift), "d" (~(65535 << shift)), "a" (addr)
+			: "memory", "cc", "0" );
+		x = old >> shift;
+		break;
+	case 4:
+		asm volatile (
+			"    l   %0,0(%2)\n"
+			"0:  cs  %0,%1,0(%2)\n"
+			"    jl  0b\n"
+			: "=&d" (old) : "d" (x), "a" (ptr)
+			: "memory", "cc", "0" );
+		x = old;
+		break;
+	case 8:
+		asm volatile (
+			"    lg  %0,0(%2)\n"
+			"0:  csg %0,%1,0(%2)\n"
+			"    jl  0b\n"
+			: "=&d" (old) : "d" (x), "a" (ptr)
+			: "memory", "cc", "0" );
+		x = old;
+		break;
         }
         return x;
 }
 
 /*
+ * Atomic compare and exchange.  Compare OLD with MEM, if identical,
+ * store NEW in MEM.  Return the initial value in MEM.  Success is
+ * indicated by comparing RETURN with OLD.
+ */
+
+#define __HAVE_ARCH_CMPXCHG 1
+
+#define cmpxchg(ptr,o,n)\
+	((__typeof__(*(ptr)))__cmpxchg((ptr),(unsigned long)(o),\
+					(unsigned long)(n),sizeof(*(ptr))))
+
+static inline unsigned long
+__cmpxchg(volatile void *ptr, unsigned long old, unsigned long new, int size)
+{
+	unsigned long addr, prev, tmp;
+	int shift;
+
+        switch (size) {
+	case 1:
+		addr = (unsigned long) ptr;
+		shift = (3 ^ (addr & 3)) << 3;
+		addr ^= addr & 3;
+		asm volatile(
+			"    l   %0,0(%4)\n"
+			"0:  nr  %0,%5\n"
+                        "    lr  %1,%0\n"
+			"    or  %0,%2\n"
+			"    or  %1,%3\n"
+			"    cs  %0,%1,0(%4)\n"
+			"    jnl 1f\n"
+			"    xr  %1,%0\n"
+			"    nr  %1,%5\n"
+			"    jnz 0b\n"
+			"1:"
+			: "=&d" (prev), "=&d" (tmp)
+			: "d" (old << shift), "d" (new << shift), "a" (ptr),
+			  "d" (~(255 << shift))
+			: "memory", "cc" );
+		return prev >> shift;
+	case 2:
+		addr = (unsigned long) ptr;
+		shift = (2 ^ (addr & 2)) << 3;
+		addr ^= addr & 2;
+		asm volatile(
+			"    l   %0,0(%4)\n"
+			"0:  nr  %0,%5\n"
+                        "    lr  %1,%0\n"
+			"    or  %0,%2\n"
+			"    or  %1,%3\n"
+			"    cs  %0,%1,0(%4)\n"
+			"    jnl 1f\n"
+			"    xr  %1,%0\n"
+			"    nr  %1,%5\n"
+			"    jnz 0b\n"
+			"1:"
+			: "=&d" (prev), "=&d" (tmp)
+			: "d" (old << shift), "d" (new << shift), "a" (ptr),
+			  "d" (~(65535 << shift))
+			: "memory", "cc" );
+		return prev >> shift;
+	case 4:
+		asm volatile (
+			"    cs  %0,%2,0(%3)\n"
+			: "=&d" (prev) : "0" (old), "d" (new), "a" (ptr)
+			: "memory", "cc" );
+		return prev;
+	case 8:
+		asm volatile (
+			"    csg %0,%2,0(%3)\n"
+			: "=&d" (prev) : "0" (old), "d" (new), "a" (ptr)
+			: "memory", "cc" );
+		return prev;
+        }
+        return old;
+}
+
+/*
  * Force strict CPU ordering.
  * And yes, this is required on UP too when we're talking
  * to devices.
@@ -142,21 +251,28 @@
 #define local_irq_enable() ({ \
         unsigned long __dummy; \
         __asm__ __volatile__ ( \
-                "stosm 0(%0),0x03" : : "a" (&__dummy) : "memory"); \
+                "stosm 0(%1),0x03" : "=m" (__dummy) : "a" (&__dummy) ); \
         })
 
 #define local_irq_disable() ({ \
         unsigned long __flags; \
         __asm__ __volatile__ ( \
-                "stnsm 0(%0),0xFC" : : "a" (&__flags) : "memory"); \
+                "stnsm 0(%1),0xfc" : "=m" (__flags) : "a" (&__flags) ); \
         __flags; \
         })
 
 #define local_save_flags(x) \
-        __asm__ __volatile__("stosm 0(%0),0" : : "a" (&x) : "memory")
+        __asm__ __volatile__("stosm 0(%1),0" : "=m" (x) : "a" (&x) )
 
 #define local_irq_restore(x) \
-        __asm__ __volatile__("ssm   0(%0)" : : "a" (&x) : "memory")
+        __asm__ __volatile__("ssm   0(%0)" : : "a" (&x) )
+
+#define irqs_disabled()			\
+({					\
+	unsigned long flags;		\
+	local_save_flags(flags);	\
+        !((flags >> 56) & 3);		\
+})
 
 #define __load_psw(psw) \
         __asm__ __volatile__("lpswe 0(%0)" : : "a" (&psw) : "cc" );
@@ -220,16 +336,6 @@
 
 #ifdef CONFIG_SMP
 
-extern void __global_cli(void);
-extern void __global_sti(void);
-
-extern unsigned long __global_save_flags(void);
-extern void __global_restore_flags(unsigned long);
-#define cli() __global_cli()
-#define sti() __global_sti()
-#define save_flags(x) ((x)=__global_save_flags())
-#define restore_flags(x) __global_restore_flags(x)
-
 extern void smp_ctl_set_bit(int cr, int bit);
 extern void smp_ctl_clear_bit(int cr, int bit);
 #define ctl_set_bit(cr, bit) smp_ctl_set_bit(cr, bit)
@@ -237,32 +343,16 @@
 
 #else
 
-#define cli() local_irq_disable()
-#define sti() local_irq_enable()
-#define save_flags(x) local_save_flags(x)
-#define restore_flags(x) local_irq_restore(x)
-
 #define ctl_set_bit(cr, bit) __ctl_set_bit(cr, bit)
 #define ctl_clear_bit(cr, bit) __ctl_clear_bit(cr, bit)
 
-
-#endif
-
-#ifdef __KERNEL__
-extern struct task_struct *resume(void *, void *);
-
-extern int save_fp_regs1(s390_fp_regs *fpregs);
-extern void save_fp_regs(s390_fp_regs *fpregs);
-extern int restore_fp_regs1(s390_fp_regs *fpregs);
-extern void restore_fp_regs(s390_fp_regs *fpregs);
+#endif /* CONFIG_SMP */
 
 extern void (*_machine_restart)(char *command);
 extern void (*_machine_halt)(void);
 extern void (*_machine_power_off)(void);
 
-#endif
+#endif /* __KERNEL __ */
 
 #endif
-
-
 
diff -Nru a/include/asm-s390x/thread_info.h b/include/asm-s390x/thread_info.h
--- a/include/asm-s390x/thread_info.h	Mon Oct  7 11:25:36 2002
+++ b/include/asm-s390x/thread_info.h	Mon Oct  7 11:25:36 2002
@@ -25,11 +25,9 @@
 	struct exec_domain	*exec_domain;	/* execution domain */
 	unsigned long		flags;		/* low level flags */
 	unsigned int		cpu;		/* current CPU */
-	int 			preempt_count; /* 0 => preemptable, <0 => BUG */
+	unsigned int		preempt_count;  /* 0 => preemptable */
 };
 
-#define PREEMPT_ACTIVE		0x4000000
-
 /*
  * macros/functions for gaining access to the thread information structure
  */
@@ -83,5 +81,7 @@
 #define _TIF_POLLING_NRFLAG	(1<<TIF_POLLING_NRFLAG)
 
 #endif /* __KERNEL__ */
+
+#define PREEMPT_ACTIVE		0x4000000
 
 #endif /* _ASM_THREAD_INFO_H */
diff -Nru a/include/asm-s390x/tlbflush.h b/include/asm-s390x/tlbflush.h
--- a/include/asm-s390x/tlbflush.h	Mon Oct  7 11:25:36 2002
+++ b/include/asm-s390x/tlbflush.h	Mon Oct  7 11:25:36 2002
@@ -88,8 +88,7 @@
 
 static inline void __flush_tlb_mm(struct mm_struct * mm)
 {
-	if ((smp_num_cpus > 1) &&
-	    ((atomic_read(&mm->mm_count) != 1) ||
+	if (((atomic_read(&mm->mm_count) != 1) ||
 	     (mm->cpu_vm_mask != (1UL << smp_processor_id())))) {
 		mm->cpu_vm_mask = (1UL << smp_processor_id());
 		global_flush_tlb();
diff -Nru a/include/asm-s390x/unistd.h b/include/asm-s390x/unistd.h
--- a/include/asm-s390x/unistd.h	Mon Oct  7 11:25:36 2002
+++ b/include/asm-s390x/unistd.h	Mon Oct  7 11:25:36 2002
@@ -198,6 +198,16 @@
 #define __NR_futex		238
 #define __NR_sched_setaffinity	239
 #define __NR_sched_getaffinity	240
+#define __NR_security		241	/* syscall for security modules */
+/*
+ * Number 242 is reserved for tux
+ */
+#define __NR_io_setup		243
+#define __NR_io_destroy		244
+#define __NR_io_getevents	245
+#define __NR_io_submit		246
+#define __NR_io_cancel		247
+#define __NR_exit_group		248
 
 
 /* user-visible error numbers are in the range -1 - -122: see <asm-s390/errno.h> */
@@ -335,9 +345,6 @@
  * some others too.
  */
 #define __NR__exit __NR_exit
-static inline _syscall0(int,idle)
-static inline _syscall0(int,pause)
-static inline _syscall0(int,sync)
 static inline _syscall0(pid_t,setsid)
 static inline _syscall3(int,write,int,fd,const char *,buf,off_t,count)
 static inline _syscall3(int,read,int,fd,char *,buf,off_t,count)
@@ -347,7 +354,6 @@
 static inline _syscall3(int,open,const char *,file,int,flag,int,mode)
 static inline _syscall1(int,close,int,fd)
 static inline _syscall1(int,_exit,int,exitcode)
-static inline _syscall1(int,delete_module,const char *,name)
 static inline _syscall2(long,stat,char *,filename,struct stat *,statbuf)
 
 struct rusage;
@@ -355,11 +361,6 @@
 static inline pid_t waitpid(int pid, int *wait_stat, int flags)
 {
 	return sys_wait4(pid, wait_stat, flags, NULL);
-}
-
-static inline pid_t wait(int * wait_stat)
-{
-        return waitpid(-1,wait_stat,0);
 }
 
 #endif
diff -Nru a/include/asm-sh/unistd.h b/include/asm-sh/unistd.h
--- a/include/asm-sh/unistd.h	Mon Oct  7 11:25:37 2002
+++ b/include/asm-sh/unistd.h	Mon Oct  7 11:25:37 2002
@@ -347,8 +347,6 @@
  * some others too.
  */
 #define __NR__exit __NR_exit
-static __inline__ _syscall0(int,pause)
-static __inline__ _syscall0(int,sync)
 static __inline__ _syscall0(pid_t,setsid)
 static __inline__ _syscall3(int,write,int,fd,const char *,buf,off_t,count)
 static __inline__ _syscall3(int,read,int,fd,char *,buf,off_t,count)
@@ -359,12 +357,7 @@
 static __inline__ _syscall1(int,close,int,fd)
 static __inline__ _syscall1(int,_exit,int,exitcode)
 static __inline__ _syscall3(pid_t,waitpid,pid_t,pid,int *,wait_stat,int,options)
-static __inline__ _syscall1(int,delete_module,const char *,name)
 
-static __inline__ pid_t wait(int * wait_stat)
-{
-	return waitpid(-1,wait_stat,0);
-}
 #endif
 
 /*
diff -Nru a/include/asm-sparc/highmem.h b/include/asm-sparc/highmem.h
--- a/include/asm-sparc/highmem.h	Mon Oct  7 11:25:39 2002
+++ b/include/asm-sparc/highmem.h	Mon Oct  7 11:25:39 2002
@@ -20,13 +20,8 @@
 
 #ifdef __KERNEL__
 
-#include <linux/init.h>
 #include <linux/interrupt.h>
-#include <asm/vaddrs.h>
 #include <asm/kmap_types.h>
-#include <asm/pgtable.h>
-#include <asm/cacheflush.h>
-#include <asm/tlbflush.h>
 
 /* undef for production */
 #define HIGHMEM_DEBUG 1
@@ -72,81 +67,8 @@
 	kunmap_high(page);
 }
 
-/*
- * The use of kmap_atomic/kunmap_atomic is discouraged - kmap/kunmap
- * gives a more generic (and caching) interface. But kmap_atomic can
- * be used in IRQ contexts, so in some (very limited) cases we need
- * it.
- */
-static inline void *kmap_atomic(struct page *page, enum km_type type)
-{
-	unsigned long idx;
-	unsigned long vaddr;
-
-	inc_preempt_count();
-	if (page < highmem_start_page)
-		return page_address(page);
-
-	idx = type + KM_TYPE_NR*smp_processor_id();
-	vaddr = FIX_KMAP_BEGIN + idx * PAGE_SIZE;
-
-/* XXX Fix - Anton */
-#if 0
-	__flush_cache_one(vaddr);
-#else
-	flush_cache_all();
-#endif
-
-#if HIGHMEM_DEBUG
-	if (!pte_none(*(kmap_pte+idx)))
-		BUG();
-#endif
-	set_pte(kmap_pte+idx, mk_pte(page, kmap_prot));
-/* XXX Fix - Anton */
-#if 0
-	__flush_tlb_one(vaddr);
-#else
-	flush_tlb_all();
-#endif
-
-	return (void*) vaddr;
-}
-
-static inline void kunmap_atomic(void *kvaddr, enum km_type type)
-{
-	unsigned long vaddr = (unsigned long) kvaddr;
-	unsigned long idx = type + KM_TYPE_NR*smp_processor_id();
-
-	if (vaddr < FIX_KMAP_BEGIN) { // FIXME
-		dec_preempt_count();
-		return;
-	}
-
-	if (vaddr != FIX_KMAP_BEGIN + idx * PAGE_SIZE)
-		BUG();
-
-/* XXX Fix - Anton */
-#if 0
-	__flush_cache_one(vaddr);
-#else
-	flush_cache_all();
-#endif
-
-#ifdef HIGHMEM_DEBUG
-	/*
-	 * force other mappings to Oops if they'll try to access
-	 * this pte without first remap it
-	 */
-	pte_clear(kmap_pte+idx);
-/* XXX Fix - Anton */
-#if 0
-	__flush_tlb_one(vaddr);
-#else
-	flush_tlb_all();
-#endif
-#endif
-	dec_preempt_count();
-}
+extern void *kmap_atomic(struct page *page, enum km_type type);
+extern void kunmap_atomic(void *kvaddr, enum km_type type);
 
 static inline struct page *kmap_atomic_to_page(void *ptr)
 {
diff -Nru a/include/asm-sparc/unistd.h b/include/asm-sparc/unistd.h
--- a/include/asm-sparc/unistd.h	Mon Oct  7 11:25:25 2002
+++ b/include/asm-sparc/unistd.h	Mon Oct  7 11:25:25 2002
@@ -416,8 +416,6 @@
  * some others too.
  */
 #define __NR__exit __NR_exit
-static __inline__ _syscall0(int,pause)
-static __inline__ _syscall0(int,sync)
 static __inline__ _syscall0(pid_t,setsid)
 static __inline__ _syscall3(int,write,int,fd,__const__ char *,buf,off_t,count)
 static __inline__ _syscall3(int,read,int,fd,char *,buf,off_t,count)
@@ -428,12 +426,6 @@
 static __inline__ _syscall1(int,close,int,fd)
 static __inline__ _syscall1(int,_exit,int,exitcode)
 static __inline__ _syscall3(pid_t,waitpid,pid_t,pid,int *,wait_stat,int,options)
-static __inline__ _syscall1(int,delete_module,const char *,name)
-
-static __inline__ pid_t wait(int * wait_stat)
-{
-	return waitpid(-1,wait_stat,0);
-}
 
 #endif /* __KERNEL_SYSCALLS__ */
 
diff -Nru a/include/asm-sparc64/checksum.h b/include/asm-sparc64/checksum.h
--- a/include/asm-sparc64/checksum.h	Mon Oct  7 11:25:30 2002
+++ b/include/asm-sparc64/checksum.h	Mon Oct  7 11:25:30 2002
@@ -40,7 +40,7 @@
  */
 extern unsigned int csum_partial_copy_sparc64(const char *src, char *dst, int len, unsigned int sum);
 			
-extern __inline__ unsigned int 
+static __inline__ unsigned int 
 csum_partial_copy_nocheck (const char *src, char *dst, int len, 
 			   unsigned int sum)
 {
@@ -52,7 +52,7 @@
 	return ret;
 }
 
-extern __inline__ unsigned int 
+static __inline__ unsigned int 
 csum_partial_copy_from_user(const char *src, char *dst, int len, 
 			    unsigned int sum, int *err)
 {
@@ -66,7 +66,7 @@
  */
 #define HAVE_CSUM_COPY_USER
 extern unsigned int csum_partial_copy_user_sparc64(const char *src, char *dst, int len, unsigned int sum);
-extern __inline__ unsigned int 
+static __inline__ unsigned int 
 csum_and_copy_to_user(const char *src, char *dst, int len, 
 		      unsigned int sum, int *err)
 {
@@ -78,7 +78,7 @@
 /* ihl is always 5 or greater, almost always is 5, and iph is word aligned
  * the majority of the time.
  */
-extern __inline__ unsigned short ip_fast_csum(__const__ unsigned char *iph,
+static __inline__ unsigned short ip_fast_csum(__const__ unsigned char *iph,
 					      unsigned int ihl)
 {
 	unsigned short sum;
@@ -119,7 +119,7 @@
 }
 
 /* Fold a partial checksum without adding pseudo headers. */
-extern __inline__ unsigned short csum_fold(unsigned int sum)
+static __inline__ unsigned short csum_fold(unsigned int sum)
 {
 	unsigned int tmp;
 
@@ -134,7 +134,7 @@
 	return (sum & 0xffff);
 }
 
-extern __inline__ unsigned long csum_tcpudp_nofold(unsigned long saddr,
+static __inline__ unsigned long csum_tcpudp_nofold(unsigned long saddr,
 						   unsigned long daddr,
 						   unsigned int len,
 						   unsigned short proto,
@@ -201,7 +201,7 @@
 }
 
 /* this routine is used for miscellaneous IP-like checksums, mainly in icmp.c */
-extern __inline__ unsigned short ip_compute_csum(unsigned char * buff, int len)
+static __inline__ unsigned short ip_compute_csum(unsigned char * buff, int len)
 {
 	return csum_fold(csum_partial(buff, len, 0));
 }
diff -Nru a/include/asm-sparc64/delay.h b/include/asm-sparc64/delay.h
--- a/include/asm-sparc64/delay.h	Mon Oct  7 11:25:36 2002
+++ b/include/asm-sparc64/delay.h	Mon Oct  7 11:25:36 2002
@@ -18,7 +18,7 @@
 extern unsigned long loops_per_jiffy;
 #endif 
 
-extern __inline__ void __delay(unsigned long loops)
+static __inline__ void __delay(unsigned long loops)
 {
 	__asm__ __volatile__(
 "	b,pt	%%xcc, 1f\n"
@@ -32,7 +32,7 @@
 	: "cc");
 }
 
-extern __inline__ void __udelay(unsigned long usecs, unsigned long lps)
+static __inline__ void __udelay(unsigned long usecs, unsigned long lps)
 {
 	usecs *= 0x00000000000010c6UL;		/* 2**32 / 1000000 */
 
diff -Nru a/include/asm-sparc64/fpumacro.h b/include/asm-sparc64/fpumacro.h
--- a/include/asm-sparc64/fpumacro.h	Mon Oct  7 11:25:36 2002
+++ b/include/asm-sparc64/fpumacro.h	Mon Oct  7 11:25:36 2002
@@ -16,7 +16,7 @@
 
 #define FPUSTATE (struct fpustate *)(current_thread_info()->fpregs)
 
-extern __inline__ unsigned long fprs_read(void)
+static __inline__ unsigned long fprs_read(void)
 {
 	unsigned long retval;
 
@@ -25,7 +25,7 @@
 	return retval;
 }
 
-extern __inline__ void fprs_write(unsigned long val)
+static __inline__ void fprs_write(unsigned long val)
 {
 	__asm__ __volatile__("wr %0, 0x0, %%fprs" : : "r" (val));
 }
diff -Nru a/include/asm-sparc64/irq.h b/include/asm-sparc64/irq.h
--- a/include/asm-sparc64/irq.h	Mon Oct  7 11:25:35 2002
+++ b/include/asm-sparc64/irq.h	Mon Oct  7 11:25:35 2002
@@ -133,21 +133,21 @@
 			    unsigned long flags, __const__ char *devname,
 			    void *dev_id);
 
-extern __inline__ void set_softint(unsigned long bits)
+static __inline__ void set_softint(unsigned long bits)
 {
 	__asm__ __volatile__("wr	%0, 0x0, %%set_softint"
 			     : /* No outputs */
 			     : "r" (bits));
 }
 
-extern __inline__ void clear_softint(unsigned long bits)
+static __inline__ void clear_softint(unsigned long bits)
 {
 	__asm__ __volatile__("wr	%0, 0x0, %%clear_softint"
 			     : /* No outputs */
 			     : "r" (bits));
 }
 
-extern __inline__ unsigned long get_softint(void)
+static __inline__ unsigned long get_softint(void)
 {
 	unsigned long retval;
 
diff -Nru a/include/asm-sparc64/page.h b/include/asm-sparc64/page.h
--- a/include/asm-sparc64/page.h	Mon Oct  7 11:25:33 2002
+++ b/include/asm-sparc64/page.h	Mon Oct  7 11:25:33 2002
@@ -161,7 +161,7 @@
 extern struct sparc_phys_banks sp_banks[SPARC_PHYS_BANKS];
 
 /* Pure 2^n version of get_order */
-extern __inline__ int get_order(unsigned long size)
+static __inline__ int get_order(unsigned long size)
 {
 	int order;
 
diff -Nru a/include/asm-sparc64/pci.h b/include/asm-sparc64/pci.h
--- a/include/asm-sparc64/pci.h	Mon Oct  7 11:25:26 2002
+++ b/include/asm-sparc64/pci.h	Mon Oct  7 11:25:26 2002
@@ -17,12 +17,12 @@
 
 #define PCI_IRQ_NONE		0xffffffff
 
-extern inline void pcibios_set_master(struct pci_dev *dev)
+static inline void pcibios_set_master(struct pci_dev *dev)
 {
 	/* No special bus mastering setup handling */
 }
 
-extern inline void pcibios_penalize_isa_irq(int irq)
+static inline void pcibios_penalize_isa_irq(int irq)
 {
 	/* We don't do dynamic PCI IRQ allocation */
 }
diff -Nru a/include/asm-sparc64/pgtable.h b/include/asm-sparc64/pgtable.h
--- a/include/asm-sparc64/pgtable.h	Mon Oct  7 11:25:25 2002
+++ b/include/asm-sparc64/pgtable.h	Mon Oct  7 11:25:25 2002
@@ -212,7 +212,7 @@
 #define page_pte_prot(page, prot)	mk_pte(page, prot)
 #define page_pte(page)			page_pte_prot(page, __pgprot(0))
 
-extern inline pte_t pte_modify(pte_t orig_pte, pgprot_t new_prot)
+static inline pte_t pte_modify(pte_t orig_pte, pgprot_t new_prot)
 {
 	pte_t __pte;
 
@@ -291,7 +291,7 @@
 extern void update_mmu_cache(struct vm_area_struct *, unsigned long, pte_t);
 
 /* Make a non-present pseudo-TTE. */
-extern inline pte_t mk_pte_io(unsigned long page, pgprot_t prot, int space)
+static inline pte_t mk_pte_io(unsigned long page, pgprot_t prot, int space)
 {
 	pte_t pte;
 	pte_val(pte) = ((page) | pgprot_val(prot) | _PAGE_E) & ~(unsigned long)_PAGE_CACHE;
@@ -313,7 +313,7 @@
 
 extern unsigned long prom_virt_to_phys(unsigned long, int *);
 
-extern __inline__ unsigned long
+static __inline__ unsigned long
 sun4u_get_pte (unsigned long addr)
 {
 	pgd_t *pgdp;
@@ -330,13 +330,13 @@
 	return pte_val(*ptep) & _PAGE_PADDR;
 }
 
-extern __inline__ unsigned long
+static __inline__ unsigned long
 __get_phys (unsigned long addr)
 {
 	return sun4u_get_pte (addr);
 }
 
-extern __inline__ int
+static __inline__ int
 __get_iospace (unsigned long addr)
 {
 	return ((sun4u_get_pte (addr) & 0xf0000000) >> 28);
diff -Nru a/include/asm-sparc64/psrcompat.h b/include/asm-sparc64/psrcompat.h
--- a/include/asm-sparc64/psrcompat.h	Mon Oct  7 11:25:25 2002
+++ b/include/asm-sparc64/psrcompat.h	Mon Oct  7 11:25:25 2002
@@ -24,7 +24,7 @@
 #define PSR_V8PLUS  0xff000000         /* fake impl/ver, meaning a 64bit CPU is present */
 #define PSR_XCC	    0x000f0000         /* if PSR_V8PLUS, this is %xcc */
 
-extern inline unsigned int tstate_to_psr(unsigned long tstate)
+static inline unsigned int tstate_to_psr(unsigned long tstate)
 {
 	return ((tstate & TSTATE_CWP)			|
 		PSR_S					|
@@ -33,7 +33,7 @@
 		PSR_V8PLUS);
 }
 
-extern inline unsigned long psr_to_tstate_icc(unsigned int psr)
+static inline unsigned long psr_to_tstate_icc(unsigned int psr)
 {
 	unsigned long tstate = ((unsigned long)(psr & PSR_ICC)) << 12;
 	if ((psr & (PSR_VERS|PSR_IMPL)) == PSR_V8PLUS)
diff -Nru a/include/asm-sparc64/pstate.h b/include/asm-sparc64/pstate.h
--- a/include/asm-sparc64/pstate.h	Mon Oct  7 11:25:39 2002
+++ b/include/asm-sparc64/pstate.h	Mon Oct  7 11:25:39 2002
@@ -85,32 +85,4 @@
 #define VERS_MAXTL	0x000000000000ff00	/* Maximum Trap Level.		*/
 #define VERS_MAXWIN	0x000000000000001f	/* Maximum Reg Window Index.	*/
 
-#if defined(__KERNEL__) && !defined(__ASSEMBLY__)
-#define set_pstate(bits)					\
-	__asm__ __volatile__(					\
-		"rdpr      %%pstate, %%g1\n\t"			\
-		"or        %%g1, %0, %%g1\n\t"			\
-		"wrpr      %%g1, 0x0, %%pstate\n\t"		\
-		: /* no outputs */				\
-		: "i" (bits)					\
-		: "g1")
-
-#define clear_pstate(bits)					\
-	__asm__ __volatile__(					\
-		"rdpr      %%pstate, %%g1\n\t"			\
-		"andn        %%g1, %0, %%g1\n\t"		\
-		"wrpr      %%g1, 0x0, %%pstate\n\t"		\
-		: /* no outputs */				\
-		: "i" (bits)					\
-		: "g1")
-
-#define change_pstate(bits)					\
-	__asm__ __volatile__(					\
-		"rdpr      %%pstate, %%g1\n\t"			\
-		"wrpr      %%g1, %0, %%pstate\n\t"		\
-		: /* no outputs */				\
-		: "i" (bits)					\
-		: "g1")
-#endif
-
 #endif /* !(_SPARC64_PSTATE_H) */
diff -Nru a/include/asm-sparc64/sbus.h b/include/asm-sparc64/sbus.h
--- a/include/asm-sparc64/sbus.h	Mon Oct  7 11:25:26 2002
+++ b/include/asm-sparc64/sbus.h	Mon Oct  7 11:25:26 2002
@@ -27,12 +27,12 @@
  * numbers + offsets, and vice versa.
  */
 
-extern __inline__ unsigned long sbus_devaddr(int slotnum, unsigned long offset)
+static __inline__ unsigned long sbus_devaddr(int slotnum, unsigned long offset)
 {
   return (unsigned long) (SUN_SBUS_BVADDR+((slotnum)<<28)+(offset));
 }
 
-extern __inline__ int sbus_dev_slot(unsigned long dev_addr)
+static __inline__ int sbus_dev_slot(unsigned long dev_addr)
 {
   return (int) (((dev_addr)-SUN_SBUS_BVADDR)>>28);
 }
diff -Nru a/include/asm-sparc64/siginfo.h b/include/asm-sparc64/siginfo.h
--- a/include/asm-sparc64/siginfo.h	Mon Oct  7 11:25:36 2002
+++ b/include/asm-sparc64/siginfo.h	Mon Oct  7 11:25:36 2002
@@ -154,7 +154,7 @@
 
 #include <linux/string.h>
 
-extern inline void copy_siginfo(siginfo_t *to, siginfo_t *from)
+static inline void copy_siginfo(siginfo_t *to, siginfo_t *from)
 {
 	if (from->si_code < 0)
 		*to = *from;
diff -Nru a/include/asm-sparc64/smp.h b/include/asm-sparc64/smp.h
--- a/include/asm-sparc64/smp.h	Mon Oct  7 11:25:29 2002
+++ b/include/asm-sparc64/smp.h	Mon Oct  7 11:25:29 2002
@@ -89,7 +89,7 @@
  *	General functions that each host system must provide.
  */
 
-extern __inline__ int hard_smp_processor_id(void)
+static __inline__ int hard_smp_processor_id(void)
 {
 	if (tlb_type == cheetah || tlb_type == cheetah_plus) {
 		unsigned long safari_config;
@@ -130,7 +130,7 @@
 /* This is a nop as well because we capture all other cpus
  * anyways when making the PROM active.
  */
-extern __inline__ void smp_send_stop(void) { }
+static __inline__ void smp_send_stop(void) { }
 
 #endif /* !(__ASSEMBLY__) */
 
diff -Nru a/include/asm-sparc64/spinlock.h b/include/asm-sparc64/spinlock.h
--- a/include/asm-sparc64/spinlock.h	Mon Oct  7 11:25:26 2002
+++ b/include/asm-sparc64/spinlock.h	Mon Oct  7 11:25:26 2002
@@ -40,7 +40,7 @@
 do {	membar("#LoadLoad");	\
 } while(*((volatile unsigned char *)lock))
 
-extern __inline__ void _raw_spin_lock(spinlock_t *lock)
+static __inline__ void _raw_spin_lock(spinlock_t *lock)
 {
 	__asm__ __volatile__(
 "1:	ldstub		[%0], %%g7\n"
@@ -57,7 +57,7 @@
 	: "g7", "memory");
 }
 
-extern __inline__ int _raw_spin_trylock(spinlock_t *lock)
+static __inline__ int _raw_spin_trylock(spinlock_t *lock)
 {
 	unsigned int result;
 	__asm__ __volatile__("ldstub [%1], %0\n\t"
@@ -68,7 +68,7 @@
 	return (result == 0);
 }
 
-extern __inline__ void _raw_spin_unlock(spinlock_t *lock)
+static __inline__ void _raw_spin_unlock(spinlock_t *lock)
 {
 	__asm__ __volatile__("membar	#StoreStore | #LoadStore\n\t"
 			     "stb	%%g0, [%0]"
diff -Nru a/include/asm-sparc64/spitfire.h b/include/asm-sparc64/spitfire.h
--- a/include/asm-sparc64/spitfire.h	Mon Oct  7 11:25:28 2002
+++ b/include/asm-sparc64/spitfire.h	Mon Oct  7 11:25:28 2002
@@ -56,7 +56,7 @@
 	 SPITFIRE_HIGHEST_LOCKED_TLBENT : \
 	 CHEETAH_HIGHEST_LOCKED_TLBENT)
 
-extern __inline__ unsigned long spitfire_get_isfsr(void)
+static __inline__ unsigned long spitfire_get_isfsr(void)
 {
 	unsigned long ret;
 
@@ -66,7 +66,7 @@
 	return ret;
 }
 
-extern __inline__ unsigned long spitfire_get_dsfsr(void)
+static __inline__ unsigned long spitfire_get_dsfsr(void)
 {
 	unsigned long ret;
 
@@ -76,7 +76,7 @@
 	return ret;
 }
 
-extern __inline__ unsigned long spitfire_get_sfar(void)
+static __inline__ unsigned long spitfire_get_sfar(void)
 {
 	unsigned long ret;
 
@@ -86,7 +86,7 @@
 	return ret;
 }
 
-extern __inline__ void spitfire_put_isfsr(unsigned long sfsr)
+static __inline__ void spitfire_put_isfsr(unsigned long sfsr)
 {
 	__asm__ __volatile__("stxa	%0, [%1] %2\n\t"
 			     "membar	#Sync"
@@ -94,7 +94,7 @@
 			     : "r" (sfsr), "r" (TLB_SFSR), "i" (ASI_IMMU));
 }
 
-extern __inline__ void spitfire_put_dsfsr(unsigned long sfsr)
+static __inline__ void spitfire_put_dsfsr(unsigned long sfsr)
 {
 	__asm__ __volatile__("stxa	%0, [%1] %2\n\t"
 			     "membar	#Sync"
@@ -102,7 +102,7 @@
 			     : "r" (sfsr), "r" (TLB_SFSR), "i" (ASI_DMMU));
 }
 
-extern __inline__ unsigned long spitfire_get_primary_context(void)
+static __inline__ unsigned long spitfire_get_primary_context(void)
 {
 	unsigned long ctx;
 
@@ -112,7 +112,7 @@
 	return ctx;
 }
 
-extern __inline__ void spitfire_set_primary_context(unsigned long ctx)
+static __inline__ void spitfire_set_primary_context(unsigned long ctx)
 {
 	__asm__ __volatile__("stxa	%0, [%1] %2\n\t"
 			     "membar	#Sync"
@@ -122,7 +122,7 @@
 	__asm__ __volatile__ ("membar #Sync" : : : "memory");
 }
 
-extern __inline__ unsigned long spitfire_get_secondary_context(void)
+static __inline__ unsigned long spitfire_get_secondary_context(void)
 {
 	unsigned long ctx;
 
@@ -132,7 +132,7 @@
 	return ctx;
 }
 
-extern __inline__ void spitfire_set_secondary_context(unsigned long ctx)
+static __inline__ void spitfire_set_secondary_context(unsigned long ctx)
 {
 	__asm__ __volatile__("stxa	%0, [%1] %2\n\t"
 			     "membar	#Sync"
@@ -145,7 +145,7 @@
 /* The data cache is write through, so this just invalidates the
  * specified line.
  */
-extern __inline__ void spitfire_put_dcache_tag(unsigned long addr, unsigned long tag)
+static __inline__ void spitfire_put_dcache_tag(unsigned long addr, unsigned long tag)
 {
 	__asm__ __volatile__("stxa	%0, [%1] %2\n\t"
 			     "membar	#Sync"
@@ -160,7 +160,7 @@
  * a flush instruction (to any address) is sufficient to handle
  * this issue after the line is invalidated.
  */
-extern __inline__ void spitfire_put_icache_tag(unsigned long addr, unsigned long tag)
+static __inline__ void spitfire_put_icache_tag(unsigned long addr, unsigned long tag)
 {
 	__asm__ __volatile__("stxa	%0, [%1] %2\n\t"
 			     "membar	#Sync"
@@ -168,7 +168,7 @@
 			     : "r" (tag), "r" (addr), "i" (ASI_IC_TAG));
 }
 
-extern __inline__ unsigned long spitfire_get_dtlb_data(int entry)
+static __inline__ unsigned long spitfire_get_dtlb_data(int entry)
 {
 	unsigned long data;
 
@@ -182,7 +182,7 @@
 	return data;
 }
 
-extern __inline__ unsigned long spitfire_get_dtlb_tag(int entry)
+static __inline__ unsigned long spitfire_get_dtlb_tag(int entry)
 {
 	unsigned long tag;
 
@@ -192,7 +192,7 @@
 	return tag;
 }
 
-extern __inline__ void spitfire_put_dtlb_data(int entry, unsigned long data)
+static __inline__ void spitfire_put_dtlb_data(int entry, unsigned long data)
 {
 	__asm__ __volatile__("stxa	%0, [%1] %2\n\t"
 			     "membar	#Sync"
@@ -201,7 +201,7 @@
 			       "i" (ASI_DTLB_DATA_ACCESS));
 }
 
-extern __inline__ unsigned long spitfire_get_itlb_data(int entry)
+static __inline__ unsigned long spitfire_get_itlb_data(int entry)
 {
 	unsigned long data;
 
@@ -215,7 +215,7 @@
 	return data;
 }
 
-extern __inline__ unsigned long spitfire_get_itlb_tag(int entry)
+static __inline__ unsigned long spitfire_get_itlb_tag(int entry)
 {
 	unsigned long tag;
 
@@ -225,7 +225,7 @@
 	return tag;
 }
 
-extern __inline__ void spitfire_put_itlb_data(int entry, unsigned long data)
+static __inline__ void spitfire_put_itlb_data(int entry, unsigned long data)
 {
 	__asm__ __volatile__("stxa	%0, [%1] %2\n\t"
 			     "membar	#Sync"
@@ -237,7 +237,7 @@
 /* Spitfire hardware assisted TLB flushes. */
 
 /* Context level flushes. */
-extern __inline__ void spitfire_flush_dtlb_primary_context(void)
+static __inline__ void spitfire_flush_dtlb_primary_context(void)
 {
 	__asm__ __volatile__("stxa	%%g0, [%0] %1\n\t"
 			     "membar	#Sync"
@@ -245,7 +245,7 @@
 			     : "r" (0x40), "i" (ASI_DMMU_DEMAP));
 }
 
-extern __inline__ void spitfire_flush_itlb_primary_context(void)
+static __inline__ void spitfire_flush_itlb_primary_context(void)
 {
 	__asm__ __volatile__("stxa	%%g0, [%0] %1\n\t"
 			     "membar	#Sync"
@@ -253,7 +253,7 @@
 			     : "r" (0x40), "i" (ASI_IMMU_DEMAP));
 }
 
-extern __inline__ void spitfire_flush_dtlb_secondary_context(void)
+static __inline__ void spitfire_flush_dtlb_secondary_context(void)
 {
 	__asm__ __volatile__("stxa	%%g0, [%0] %1\n\t"
 			     "membar	#Sync"
@@ -261,7 +261,7 @@
 			     : "r" (0x50), "i" (ASI_DMMU_DEMAP));
 }
 
-extern __inline__ void spitfire_flush_itlb_secondary_context(void)
+static __inline__ void spitfire_flush_itlb_secondary_context(void)
 {
 	__asm__ __volatile__("stxa	%%g0, [%0] %1\n\t"
 			     "membar	#Sync"
@@ -269,7 +269,7 @@
 			     : "r" (0x50), "i" (ASI_IMMU_DEMAP));
 }
 
-extern __inline__ void spitfire_flush_dtlb_nucleus_context(void)
+static __inline__ void spitfire_flush_dtlb_nucleus_context(void)
 {
 	__asm__ __volatile__("stxa	%%g0, [%0] %1\n\t"
 			     "membar	#Sync"
@@ -277,7 +277,7 @@
 			     : "r" (0x60), "i" (ASI_DMMU_DEMAP));
 }
 
-extern __inline__ void spitfire_flush_itlb_nucleus_context(void)
+static __inline__ void spitfire_flush_itlb_nucleus_context(void)
 {
 	__asm__ __volatile__("stxa	%%g0, [%0] %1\n\t"
 			     "membar	#Sync"
@@ -286,7 +286,7 @@
 }
 
 /* Page level flushes. */
-extern __inline__ void spitfire_flush_dtlb_primary_page(unsigned long page)
+static __inline__ void spitfire_flush_dtlb_primary_page(unsigned long page)
 {
 	__asm__ __volatile__("stxa	%%g0, [%0] %1\n\t"
 			     "membar	#Sync"
@@ -294,7 +294,7 @@
 			     : "r" (page), "i" (ASI_DMMU_DEMAP));
 }
 
-extern __inline__ void spitfire_flush_itlb_primary_page(unsigned long page)
+static __inline__ void spitfire_flush_itlb_primary_page(unsigned long page)
 {
 	__asm__ __volatile__("stxa	%%g0, [%0] %1\n\t"
 			     "membar	#Sync"
@@ -302,7 +302,7 @@
 			     : "r" (page), "i" (ASI_IMMU_DEMAP));
 }
 
-extern __inline__ void spitfire_flush_dtlb_secondary_page(unsigned long page)
+static __inline__ void spitfire_flush_dtlb_secondary_page(unsigned long page)
 {
 	__asm__ __volatile__("stxa	%%g0, [%0] %1\n\t"
 			     "membar	#Sync"
@@ -310,7 +310,7 @@
 			     : "r" (page | 0x10), "i" (ASI_DMMU_DEMAP));
 }
 
-extern __inline__ void spitfire_flush_itlb_secondary_page(unsigned long page)
+static __inline__ void spitfire_flush_itlb_secondary_page(unsigned long page)
 {
 	__asm__ __volatile__("stxa	%%g0, [%0] %1\n\t"
 			     "membar	#Sync"
@@ -318,7 +318,7 @@
 			     : "r" (page | 0x10), "i" (ASI_IMMU_DEMAP));
 }
 
-extern __inline__ void spitfire_flush_dtlb_nucleus_page(unsigned long page)
+static __inline__ void spitfire_flush_dtlb_nucleus_page(unsigned long page)
 {
 	__asm__ __volatile__("stxa	%%g0, [%0] %1\n\t"
 			     "membar	#Sync"
@@ -326,7 +326,7 @@
 			     : "r" (page | 0x20), "i" (ASI_DMMU_DEMAP));
 }
 
-extern __inline__ void spitfire_flush_itlb_nucleus_page(unsigned long page)
+static __inline__ void spitfire_flush_itlb_nucleus_page(unsigned long page)
 {
 	__asm__ __volatile__("stxa	%%g0, [%0] %1\n\t"
 			     "membar	#Sync"
@@ -335,7 +335,7 @@
 }
 
 /* Cheetah has "all non-locked" tlb flushes. */
-extern __inline__ void cheetah_flush_dtlb_all(void)
+static __inline__ void cheetah_flush_dtlb_all(void)
 {
 	__asm__ __volatile__("stxa	%%g0, [%0] %1\n\t"
 			     "membar	#Sync"
@@ -343,7 +343,7 @@
 			     : "r" (0x80), "i" (ASI_DMMU_DEMAP));
 }
 
-extern __inline__ void cheetah_flush_itlb_all(void)
+static __inline__ void cheetah_flush_itlb_all(void)
 {
 	__asm__ __volatile__("stxa	%%g0, [%0] %1\n\t"
 			     "membar	#Sync"
@@ -365,7 +365,7 @@
  * ASI_{D,I}TLB_DATA_ACCESS loads, doing the load twice fixes
  * the problem for me. -DaveM
  */
-extern __inline__ unsigned long cheetah_get_ldtlb_data(int entry)
+static __inline__ unsigned long cheetah_get_ldtlb_data(int entry)
 {
 	unsigned long data;
 
@@ -378,7 +378,7 @@
 	return data;
 }
 
-extern __inline__ unsigned long cheetah_get_litlb_data(int entry)
+static __inline__ unsigned long cheetah_get_litlb_data(int entry)
 {
 	unsigned long data;
 
@@ -391,7 +391,7 @@
 	return data;
 }
 
-extern __inline__ unsigned long cheetah_get_ldtlb_tag(int entry)
+static __inline__ unsigned long cheetah_get_ldtlb_tag(int entry)
 {
 	unsigned long tag;
 
@@ -403,7 +403,7 @@
 	return tag;
 }
 
-extern __inline__ unsigned long cheetah_get_litlb_tag(int entry)
+static __inline__ unsigned long cheetah_get_litlb_tag(int entry)
 {
 	unsigned long tag;
 
@@ -415,7 +415,7 @@
 	return tag;
 }
 
-extern __inline__ void cheetah_put_ldtlb_data(int entry, unsigned long data)
+static __inline__ void cheetah_put_ldtlb_data(int entry, unsigned long data)
 {
 	__asm__ __volatile__("stxa	%0, [%1] %2\n\t"
 			     "membar	#Sync"
@@ -425,7 +425,7 @@
 			       "i" (ASI_DTLB_DATA_ACCESS));
 }
 
-extern __inline__ void cheetah_put_litlb_data(int entry, unsigned long data)
+static __inline__ void cheetah_put_litlb_data(int entry, unsigned long data)
 {
 	__asm__ __volatile__("stxa	%0, [%1] %2\n\t"
 			     "membar	#Sync"
@@ -435,7 +435,7 @@
 			       "i" (ASI_ITLB_DATA_ACCESS));
 }
 
-extern __inline__ unsigned long cheetah_get_dtlb_data(int entry, int tlb)
+static __inline__ unsigned long cheetah_get_dtlb_data(int entry, int tlb)
 {
 	unsigned long data;
 
@@ -447,7 +447,7 @@
 	return data;
 }
 
-extern __inline__ unsigned long cheetah_get_dtlb_tag(int entry, int tlb)
+static __inline__ unsigned long cheetah_get_dtlb_tag(int entry, int tlb)
 {
 	unsigned long tag;
 
@@ -457,7 +457,7 @@
 	return tag;
 }
 
-extern __inline__ void cheetah_put_dtlb_data(int entry, unsigned long data, int tlb)
+static __inline__ void cheetah_put_dtlb_data(int entry, unsigned long data, int tlb)
 {
 	__asm__ __volatile__("stxa	%0, [%1] %2\n\t"
 			     "membar	#Sync"
@@ -467,7 +467,7 @@
 			       "i" (ASI_DTLB_DATA_ACCESS));
 }
 
-extern __inline__ unsigned long cheetah_get_itlb_data(int entry)
+static __inline__ unsigned long cheetah_get_itlb_data(int entry)
 {
 	unsigned long data;
 
@@ -480,7 +480,7 @@
 	return data;
 }
 
-extern __inline__ unsigned long cheetah_get_itlb_tag(int entry)
+static __inline__ unsigned long cheetah_get_itlb_tag(int entry)
 {
 	unsigned long tag;
 
@@ -490,7 +490,7 @@
 	return tag;
 }
 
-extern __inline__ void cheetah_put_itlb_data(int entry, unsigned long data)
+static __inline__ void cheetah_put_itlb_data(int entry, unsigned long data)
 {
 	__asm__ __volatile__("stxa	%0, [%1] %2\n\t"
 			     "membar	#Sync"
diff -Nru a/include/asm-sparc64/system.h b/include/asm-sparc64/system.h
--- a/include/asm-sparc64/system.h	Mon Oct  7 11:25:25 2002
+++ b/include/asm-sparc64/system.h	Mon Oct  7 11:25:25 2002
@@ -217,7 +217,7 @@
 	}								\
 } while(0)
 
-extern __inline__ unsigned long xchg32(__volatile__ unsigned int *m, unsigned int val)
+static __inline__ unsigned long xchg32(__volatile__ unsigned int *m, unsigned int val)
 {
 	__asm__ __volatile__(
 "	mov		%0, %%g5\n"
@@ -233,7 +233,7 @@
 	return val;
 }
 
-extern __inline__ unsigned long xchg64(__volatile__ unsigned long *m, unsigned long val)
+static __inline__ unsigned long xchg64(__volatile__ unsigned long *m, unsigned long val)
 {
 	__asm__ __volatile__(
 "	mov		%0, %%g5\n"
@@ -277,7 +277,7 @@
 
 #define __HAVE_ARCH_CMPXCHG 1
 
-extern __inline__ unsigned long
+static __inline__ unsigned long
 __cmpxchg_u32(volatile int *m, int old, int new)
 {
 	__asm__ __volatile__("cas [%2], %3, %0\n\t"
@@ -289,7 +289,7 @@
 	return new;
 }
 
-extern __inline__ unsigned long
+static __inline__ unsigned long
 __cmpxchg_u64(volatile long *m, unsigned long old, unsigned long new)
 {
 	__asm__ __volatile__("casx [%2], %3, %0\n\t"
diff -Nru a/include/asm-sparc64/uaccess.h b/include/asm-sparc64/uaccess.h
--- a/include/asm-sparc64/uaccess.h	Mon Oct  7 11:25:33 2002
+++ b/include/asm-sparc64/uaccess.h	Mon Oct  7 11:25:33 2002
@@ -52,7 +52,7 @@
 #define __access_ok(addr,size) 1
 #define access_ok(type,addr,size) 1
 
-extern inline int verify_area(int type, const void * addr, unsigned long size)
+static inline int verify_area(int type, const void * addr, unsigned long size)
 {
 	return 0;
 }
@@ -270,7 +270,7 @@
 	__copy_in_user((void *)(to), \
 	(void *) (from), (__kernel_size_t)(n))
 
-extern __inline__ __kernel_size_t __clear_user(void *addr, __kernel_size_t size)
+static __inline__ __kernel_size_t __clear_user(void *addr, __kernel_size_t size)
 {
 	extern __kernel_size_t __bzero_noasi(void *addr, __kernel_size_t size);
 	
diff -Nru a/include/asm-sparc64/unistd.h b/include/asm-sparc64/unistd.h
--- a/include/asm-sparc64/unistd.h	Mon Oct  7 11:25:26 2002
+++ b/include/asm-sparc64/unistd.h	Mon Oct  7 11:25:26 2002
@@ -406,8 +406,6 @@
  * some others too.
  */
 #define __NR__exit __NR_exit
-static __inline__ _syscall0(int,pause)
-static __inline__ _syscall0(int,sync)
 static __inline__ _syscall0(pid_t,setsid)
 static __inline__ _syscall3(int,write,int,fd,__const__ char *,buf,off_t,count)
 static __inline__ _syscall3(int,read,int,fd,char *,buf,off_t,count)
@@ -418,12 +416,6 @@
 static __inline__ _syscall1(int,close,int,fd)
 static __inline__ _syscall1(int,_exit,int,exitcode)
 static __inline__ _syscall3(pid_t,waitpid,pid_t,pid,int *,wait_stat,int,options)
-static __inline__ _syscall1(int,delete_module,const char *,name)
-
-static __inline__ pid_t wait(int * wait_stat)
-{
-	return waitpid(-1,wait_stat,0);
-}
 
 #endif /* __KERNEL_SYSCALLS__ */
 
diff -Nru a/include/asm-sparc64/upa.h b/include/asm-sparc64/upa.h
--- a/include/asm-sparc64/upa.h	Mon Oct  7 11:25:39 2002
+++ b/include/asm-sparc64/upa.h	Mon Oct  7 11:25:39 2002
@@ -25,7 +25,7 @@
 
 /* UPA I/O space accessors */
 #if defined(__KERNEL__) && !defined(__ASSEMBLY__)
-extern __inline__ unsigned char _upa_readb(unsigned long addr)
+static __inline__ unsigned char _upa_readb(unsigned long addr)
 {
 	unsigned char ret;
 
@@ -36,7 +36,7 @@
 	return ret;
 }
 
-extern __inline__ unsigned short _upa_readw(unsigned long addr)
+static __inline__ unsigned short _upa_readw(unsigned long addr)
 {
 	unsigned short ret;
 
@@ -47,7 +47,7 @@
 	return ret;
 }
 
-extern __inline__ unsigned int _upa_readl(unsigned long addr)
+static __inline__ unsigned int _upa_readl(unsigned long addr)
 {
 	unsigned int ret;
 
@@ -58,7 +58,7 @@
 	return ret;
 }
 
-extern __inline__ unsigned long _upa_readq(unsigned long addr)
+static __inline__ unsigned long _upa_readq(unsigned long addr)
 {
 	unsigned long ret;
 
@@ -69,28 +69,28 @@
 	return ret;
 }
 
-extern __inline__ void _upa_writeb(unsigned char b, unsigned long addr)
+static __inline__ void _upa_writeb(unsigned char b, unsigned long addr)
 {
 	__asm__ __volatile__("stba\t%0, [%1] %2\t/* upa_writeb */"
 			     : /* no outputs */
 			     : "r" (b), "r" (addr), "i" (ASI_PHYS_BYPASS_EC_E));
 }
 
-extern __inline__ void _upa_writew(unsigned short w, unsigned long addr)
+static __inline__ void _upa_writew(unsigned short w, unsigned long addr)
 {
 	__asm__ __volatile__("stha\t%0, [%1] %2\t/* upa_writew */"
 			     : /* no outputs */
 			     : "r" (w), "r" (addr), "i" (ASI_PHYS_BYPASS_EC_E));
 }
 
-extern __inline__ void _upa_writel(unsigned int l, unsigned long addr)
+static __inline__ void _upa_writel(unsigned int l, unsigned long addr)
 {
 	__asm__ __volatile__("stwa\t%0, [%1] %2\t/* upa_writel */"
 			     : /* no outputs */
 			     : "r" (l), "r" (addr), "i" (ASI_PHYS_BYPASS_EC_E));
 }
 
-extern __inline__ void _upa_writeq(unsigned long q, unsigned long addr)
+static __inline__ void _upa_writeq(unsigned long q, unsigned long addr)
 {
 	__asm__ __volatile__("stxa\t%0, [%1] %2\t/* upa_writeq */"
 			     : /* no outputs */
diff -Nru a/include/asm-sparc64/visasm.h b/include/asm-sparc64/visasm.h
--- a/include/asm-sparc64/visasm.h	Mon Oct  7 11:25:26 2002
+++ b/include/asm-sparc64/visasm.h	Mon Oct  7 11:25:26 2002
@@ -42,7 +42,7 @@
 	wr		%o5, 0, %fprs;
 
 #ifndef __ASSEMBLY__	
-extern __inline__ void save_and_clear_fpu(void) {
+static __inline__ void save_and_clear_fpu(void) {
 	__asm__ __volatile__ (
 "		rd %%fprs, %%o5\n"
 "		andcc %%o5, %0, %%g0\n"
diff -Nru a/include/asm-um/unistd.h b/include/asm-um/unistd.h
--- a/include/asm-um/unistd.h	Mon Oct  7 11:25:37 2002
+++ b/include/asm-um/unistd.h	Mon Oct  7 11:25:37 2002
@@ -61,11 +61,6 @@
 	KERNEL_CALL(pid_t, sys_wait4, pid, status, options, NULL)
 }
 
-static inline pid_t wait(int *status)
-{
-	KERNEL_CALL(pid_t, sys_wait4, -1, status, 0, NULL)
-}
-
 static inline pid_t setsid(void)
 {
 	KERNEL_CALL(pid_t, sys_setsid)
diff -Nru a/include/asm-x86_64/unistd.h b/include/asm-x86_64/unistd.h
--- a/include/asm-x86_64/unistd.h	Mon Oct  7 11:25:36 2002
+++ b/include/asm-x86_64/unistd.h	Mon Oct  7 11:25:36 2002
@@ -573,18 +573,6 @@
  */
 #define __NR__exit __NR_exit
 
-extern long sys_pause(void);
-static inline long pause(void)
-{
-	return sys_pause();
-}
-
-extern long sys_sync(void);
-static inline long sync(void)
-{
-	return sys_sync();
-}
-
 extern pid_t sys_setsid(void);
 static inline pid_t setsid(void)
 {
@@ -636,23 +624,12 @@
 	sys_exit(error_code);
 }
 
-extern long sys_delete_module(const char *);
-static inline long delete_module(const char *name_user)
-{
-	return sys_delete_module(name_user);
-}
-
 struct rusage; 
 asmlinkage long sys_wait4(pid_t pid,unsigned int * stat_addr, 
 			int options, struct rusage * ru);
 static inline pid_t waitpid(int pid, int * wait_stat, int flags)
 {
 	return sys_wait4(pid, wait_stat, flags, NULL);
-}
-
-static inline pid_t wait(int * wait_stat)
-{
-	return waitpid(-1,wait_stat,0);
 }
 
 #endif /* __KERNEL_SYSCALLS__ */
diff -Nru a/include/linux/amifd.h b/include/linux/amifd.h
--- a/include/linux/amifd.h	Mon Oct  7 11:25:39 2002
+++ b/include/linux/amifd.h	Mon Oct  7 11:25:39 2002
@@ -55,7 +55,7 @@
     int busy;			/* true when drive is active */
     int dirty;			/* true when trackbuf is not on disk */
     int status;			/* current error code for unit */
-    struct gendisk disk;
+    struct gendisk *gendisk;
 };
 #endif
 
diff -Nru a/include/linux/atalk.h b/include/linux/atalk.h
--- a/include/linux/atalk.h	Mon Oct  7 11:25:36 2002
+++ b/include/linux/atalk.h	Mon Oct  7 11:25:36 2002
@@ -198,5 +198,19 @@
 
 #define at_sk(__sk) ((struct atalk_sock *)(__sk)->protinfo)
 
+extern struct sock *atalk_sockets;
+extern spinlock_t atalk_sockets_lock;
+
+extern struct atalk_route *atalk_routes;
+extern rwlock_t atalk_routes_lock;
+
+extern struct atalk_iface *atalk_interfaces;
+extern spinlock_t atalk_interfaces_lock;
+
+extern struct atalk_route atrtr_default;
+
+extern int atalk_proc_init(void);
+extern void atalk_proc_exit(void);
+
 #endif /* __KERNEL__ */
 #endif /* __LINUX_ATALK_H__ */
diff -Nru a/include/linux/blkdev.h b/include/linux/blkdev.h
--- a/include/linux/blkdev.h	Mon Oct  7 11:25:27 2002
+++ b/include/linux/blkdev.h	Mon Oct  7 11:25:27 2002
@@ -4,7 +4,6 @@
 #include <linux/major.h>
 #include <linux/sched.h>
 #include <linux/genhd.h>
-#include <linux/tqueue.h>
 #include <linux/list.h>
 #include <linux/pagemap.h>
 #include <linux/backing-dev.h>
@@ -400,5 +399,9 @@
 {
 	page_cache_release(p.v);
 }
+
+extern atomic_t nr_iowait_tasks;
+void io_schedule(void);
+void io_schedule_timeout(long timeout);
 
 #endif
diff -Nru a/include/linux/buffer_head.h b/include/linux/buffer_head.h
--- a/include/linux/buffer_head.h	Mon Oct  7 11:25:25 2002
+++ b/include/linux/buffer_head.h	Mon Oct  7 11:25:25 2002
@@ -140,7 +140,6 @@
 void buffer_insert_list(spinlock_t *lock,
 			struct buffer_head *, struct list_head *);
 void mark_buffer_dirty_inode(struct buffer_head *bh, struct inode *inode);
-int write_mapping_buffers(struct address_space *mapping);
 int inode_has_buffers(struct inode *);
 void invalidate_inode_buffers(struct inode *);
 int fsync_buffers_list(spinlock_t *lock, struct list_head *);
@@ -168,6 +167,9 @@
 void FASTCALL(unlock_buffer(struct buffer_head *bh));
 void ll_rw_block(int, int, struct buffer_head * bh[]);
 int submit_bh(int, struct buffer_head *);
+void write_boundary_block(struct block_device *bdev,
+			sector_t bblock, unsigned blocksize);
+
 extern int buffer_heads_over_limit;
 
 /*
diff -Nru a/include/linux/compatmac.h b/include/linux/compatmac.h
--- a/include/linux/compatmac.h	Mon Oct  7 11:25:25 2002
+++ b/include/linux/compatmac.h	Mon Oct  7 11:25:25 2002
@@ -102,7 +102,7 @@
 
 #define my_iounmap(x, b)             (((long)x<0x100000)?0:vfree ((void*)x))
 
-#define tty_flip_buffer_push(tty)    queue_task(&tty->flip.tqueue, &tq_timer)
+#define tty_flip_buffer_push(tty)    schedule_delayed_work(&tty->flip.work, 1)
 #define signal_pending(current)      (current->signal & ~current->blocked)
 #define schedule_timeout(to)         do {current->timeout = jiffies + (to);schedule ();} while (0)
 #define time_after(t1,t2)            (((long)t1-t2) > 0)
diff -Nru a/include/linux/cyclades.h b/include/linux/cyclades.h
--- a/include/linux/cyclades.h	Mon Oct  7 11:25:40 2002
+++ b/include/linux/cyclades.h	Mon Oct  7 11:25:40 2002
@@ -605,7 +605,7 @@
 	struct cyclades_monitor	mon;
 	struct cyclades_idle_stats	idle_stats;
 	struct cyclades_icount	icount;
-	struct tq_struct	tqueue;
+	struct work_struct	tqueue;
 	wait_queue_head_t       open_wait;
 	wait_queue_head_t       close_wait;
 	wait_queue_head_t       shutdown_wait;
diff -Nru a/include/linux/elevator.h b/include/linux/elevator.h
--- a/include/linux/elevator.h	Mon Oct  7 11:25:25 2002
+++ b/include/linux/elevator.h	Mon Oct  7 11:25:25 2002
@@ -73,8 +73,8 @@
 #define BLKELVGET   _IOR(0x12,106,sizeof(blkelv_ioctl_arg_t))
 #define BLKELVSET   _IOW(0x12,107,sizeof(blkelv_ioctl_arg_t))
 
-extern int elevator_init(request_queue_t *, elevator_t *, elevator_t);
-extern void elevator_exit(request_queue_t *, elevator_t *);
+extern int elevator_init(request_queue_t *, elevator_t *);
+extern void elevator_exit(request_queue_t *);
 extern inline int bio_rq_in_between(struct bio *, struct request *, struct list_head *);
 extern inline int elv_rq_merge_ok(struct request *, struct bio *);
 extern inline int elv_try_merge(struct request *, struct bio *);
diff -Nru a/include/linux/ext3_fs.h b/include/linux/ext3_fs.h
--- a/include/linux/ext3_fs.h	Mon Oct  7 11:25:25 2002
+++ b/include/linux/ext3_fs.h	Mon Oct  7 11:25:25 2002
@@ -17,6 +17,7 @@
 #define _LINUX_EXT3_FS_H
 
 #include <linux/types.h>
+#include <linux/ext3_fs_sb.h>
 
 /*
  * The second extended filesystem constants/structures
diff -Nru a/include/linux/fs.h b/include/linux/fs.h
--- a/include/linux/fs.h	Mon Oct  7 11:25:28 2002
+++ b/include/linux/fs.h	Mon Oct  7 11:25:28 2002
@@ -308,7 +308,8 @@
 	int (*bmap)(struct address_space *, long);
 	int (*invalidatepage) (struct page *, unsigned long);
 	int (*releasepage) (struct page *, int);
-	int (*direct_IO)(int, struct inode *, const struct iovec *iov, loff_t offset, unsigned long nr_segs);
+	int (*direct_IO)(int, struct file *, const struct iovec *iov,
+			loff_t offset, unsigned long nr_segs);
 };
 
 struct backing_dev_info;
@@ -627,9 +628,6 @@
 #define MNT_FORCE	0x00000001	/* Attempt to forcibily umount */
 #define MNT_DETACH	0x00000002	/* Just detach from the tree */
 
-#include <linux/ext3_fs_sb.h>
-#include <linux/hpfs_fs_sb.h>
-
 extern struct list_head super_blocks;
 extern spinlock_t sb_lock;
 
@@ -670,8 +668,6 @@
 	char s_id[32];				/* Informational name */
 
 	union {
-		struct ext3_sb_info	ext3_sb;
-		struct hpfs_sb_info	hpfs_sb;
 		void			*generic_sbp;
 	} u;
 	/*
@@ -1140,7 +1136,7 @@
 extern int __check_disk_change(dev_t);
 extern int invalidate_inodes(struct super_block *);
 extern int invalidate_device(kdev_t, int);
-extern void invalidate_inode_pages(struct inode *);
+extern void invalidate_inode_pages(struct address_space *mapping);
 extern void invalidate_inode_pages2(struct address_space *mapping);
 extern void write_inode_now(struct inode *, int);
 extern int filemap_fdatawrite(struct address_space *);
@@ -1247,7 +1243,7 @@
 				unsigned long nr_segs, loff_t *ppos);
 extern ssize_t generic_file_sendfile(struct file *, struct file *, loff_t *, size_t);
 extern void do_generic_file_read(struct file *, loff_t *, read_descriptor_t *, read_actor_t);
-extern ssize_t generic_file_direct_IO(int rw, struct inode *inode, 
+extern ssize_t generic_file_direct_IO(int rw, struct file *file,
 	const struct iovec *iov, loff_t offset, unsigned long nr_segs);
 extern int generic_direct_IO(int rw, struct inode *inode, const struct iovec 
 	*iov, loff_t offset, unsigned long nr_segs, get_blocks_t *get_blocks);
diff -Nru a/include/linux/hayesesp.h b/include/linux/hayesesp.h
--- a/include/linux/hayesesp.h	Mon Oct  7 11:25:36 2002
+++ b/include/linux/hayesesp.h	Mon Oct  7 11:25:36 2002
@@ -102,8 +102,8 @@
 	int			xmit_head;
 	int			xmit_tail;
 	int			xmit_cnt;
-	struct tq_struct	tqueue;
-	struct tq_struct	tqueue_hangup;
+	struct work_struct	tqueue;
+	struct work_struct	tqueue_hangup;
 	struct termios		normal_termios;
 	struct termios		callout_termios;
 	wait_queue_head_t	open_wait;
diff -Nru a/include/linux/hpfs_fs_sb.h b/include/linux/hpfs_fs_sb.h
--- a/include/linux/hpfs_fs_sb.h	Mon Oct  7 11:25:37 2002
+++ b/include/linux/hpfs_fs_sb.h	Mon Oct  7 11:25:37 2002
@@ -36,31 +36,4 @@
 	int sb_timeshift;
 };
 
-#define s_hpfs_root u.hpfs_sb.sb_root
-#define s_hpfs_fs_size u.hpfs_sb.sb_fs_size
-#define s_hpfs_bitmaps u.hpfs_sb.sb_bitmaps
-#define s_hpfs_dirband_start u.hpfs_sb.sb_dirband_start
-#define s_hpfs_dirband_size u.hpfs_sb.sb_dirband_size
-#define s_hpfs_dmap u.hpfs_sb.sb_dmap
-#define s_hpfs_uid u.hpfs_sb.sb_uid
-#define s_hpfs_gid u.hpfs_sb.sb_gid
-#define s_hpfs_mode u.hpfs_sb.sb_mode
-#define s_hpfs_n_free u.hpfs_sb.sb_n_free
-#define s_hpfs_n_free_dnodes u.hpfs_sb.sb_n_free_dnodes
-#define s_hpfs_lowercase u.hpfs_sb.sb_lowercase
-#define s_hpfs_conv u.hpfs_sb.sb_conv
-#define s_hpfs_eas u.hpfs_sb.sb_eas
-#define s_hpfs_err u.hpfs_sb.sb_err
-#define s_hpfs_chk u.hpfs_sb.sb_chk
-#define s_hpfs_was_error u.hpfs_sb.sb_was_error
-#define s_hpfs_chkdsk u.hpfs_sb.sb_chkdsk
-/*#define s_hpfs_rd_fnode u.hpfs_sb.sb_rd_fnode*/
-#define s_hpfs_rd_inode u.hpfs_sb.sb_rd_inode
-#define s_hpfs_cp_table u.hpfs_sb.sb_cp_table
-#define s_hpfs_bmp_dir u.hpfs_sb.sb_bmp_dir
-#define s_hpfs_c_bitmap u.hpfs_sb.sb_c_bitmap
-#define s_hpfs_iget_q u.hpfs_sb.sb_iget_q
-/*#define s_hpfs_mounting u.hpfs_sb.sb_mounting*/
-#define s_hpfs_timeshift u.hpfs_sb.sb_timeshift
-
 #endif
diff -Nru a/include/linux/ide.h b/include/linux/ide.h
--- a/include/linux/ide.h	Mon Oct  7 11:25:32 2002
+++ b/include/linux/ide.h	Mon Oct  7 11:25:32 2002
@@ -17,6 +17,7 @@
 #include <linux/interrupt.h>
 #include <linux/bitops.h>
 #include <linux/bio.h>
+#include <linux/device.h>
 #include <linux/pci.h>
 #include <asm/byteorder.h>
 #include <asm/system.h>
@@ -298,27 +299,17 @@
 
 typedef struct ide_io_ops_s {
 	/* insert io operations here! */
-	void (*OUTB)(u8 addr, u32 port);
-	void (*OUTW)(u16 addr, u32 port);
-	void (*OUTL)(u32 addr, u32 port);
-	void (*OUTBP)(u8 addr, u32 port);
-	void (*OUTWP)(u16 addr, u32 port);
-	void (*OUTLP)(u32 addr, u32 port);
-	void (*OUTSW)(u32 port, void *addr, u32 count);
-	void (*OUTSWP)(u32 port, void *addr, u32 count);
-	void (*OUTSL)(u32 port, void *addr, u32 count);
-	void (*OUTSLP)(u32 port, void *addr, u32 count);
-
-	u8  (*INB)(u32 port);
-	u16 (*INW)(u32 port);
-	u32 (*INL)(u32 port);
-	u8  (*INBP)(u32 port);
-	u16 (*INWP)(u32 port);
-	u32 (*INLP)(u32 port);
-	void (*INSW)(u32 port, void *addr, u32 count);
-	void (*INSWP)(u32 port, void *addr, u32 count);
-	void (*INSL)(u32 port, void *addr, u32 count);
-	void (*INSLP)(u32 port, void *addr, u32 count);
+	void (*OUTB)(u8 addr, ide_ioreg_t port);
+	void (*OUTW)(u16 addr, ide_ioreg_t port);
+	void (*OUTL)(u32 addr, ide_ioreg_t port);
+	void (*OUTSW)(ide_ioreg_t port, void *addr, u32 count);
+	void (*OUTSL)(ide_ioreg_t port, void *addr, u32 count);
+
+	u8  (*INB)(ide_ioreg_t port);
+	u16 (*INW)(ide_ioreg_t port);
+	u32 (*INL)(ide_ioreg_t port);
+	void (*INSW)(ide_ioreg_t port, void *addr, u32 count);
+	void (*INSL)(ide_ioreg_t port, void *addr, u32 count);
 } ide_io_ops_t;
 
 /*
@@ -374,41 +365,6 @@
 #endif /* IDE_ARCH_LOCK */
 
 /*
- * If the arch-dependant ide.h did not declare/define any OUT_BYTE
- * or IN_BYTE functions, we make some defaults here.
- */
-
-#ifndef HAVE_ARCH_OUT_BYTE
-# ifdef REALLY_FAST_IO
-#  define OUT_BYTE(b,p)		outb((b),(p))
-#  define OUT_WORD(w,p)		outw((w),(p))
-#  define OUT_LONG(l,p)		outl((l),(p))
-# else
-#  define OUT_BYTE(b,p)		outb_p((b),(p))
-#  define OUT_WORD(w,p)		outw_p((w),(p))
-#  define OUT_LONG(l,p)		outl_p((l),(p))
-# endif
-# define OUT_BYTE_P(b,p)	outb_p((b),(p))
-# define OUT_WORD_P(w,p)	outw_p((w),(p))
-# define OUT_LONG_P(l,p)	outl_p((l),(p))
-#endif
-
-#ifndef HAVE_ARCH_IN_BYTE
-# ifdef REALLY_FAST_IO
-#  define IN_BYTE(p)		(u8) inb(p)
-#  define IN_WORD(p)		(u16) inw(p)
-#  define IN_LONG(p)		(u32) inl(p)
-# else
-#  define IN_BYTE(p)		(u8) inb_p(p)
-#  define IN_WORD(p)		(u16) inw_p(p)
-#  define IN_LONG(p)		(u32) inl_p(p)
-# endif
-# define IN_BYTE_P(p)		(u8) inb_p(p)
-# define IN_WORD_P(p)		(u16) inw_p(p)
-# define IN_LONG_P(p)		(u32) inl_p(p)
-#endif
-
-/*
  * Now for the data we need to maintain per-drive:  ide_drive_t
  */
 
@@ -789,6 +745,7 @@
 	unsigned autotune	: 2;	/* 1=autotune, 2=noautotune, 0=default */
 	unsigned remap_0_to_1	: 2;	/* 0=remap if ezdrive, 1=remap, 2=noremap */
 	unsigned ata_flash	: 1;	/* 1=present, 0=default */
+	unsigned blocked        : 1;	/* 1=powermanagment told us not to do anything, so sleep nicely */
 	unsigned addressing;		/*      : 3;
 					 *  0=28-bit
 					 *  1=48-bit
@@ -1006,27 +963,17 @@
 #if 0
 	ide_io_ops_t	*iops;
 #else
-	void (*OUTB)(u8 addr, u32 port);
-	void (*OUTW)(u16 addr, u32 port);
-	void (*OUTL)(u32 addr, u32 port);
-	void (*OUTBP)(u8 addr, u32 port);
-	void (*OUTWP)(u16 addr, u32 port);
-	void (*OUTLP)(u32 addr, u32 port);
-	void (*OUTSW)(u32 port, void *addr, u32 count);
-	void (*OUTSWP)(u32 port, void *addr, u32 count);
-	void (*OUTSL)(u32 port, void *addr, u32 count);
-	void (*OUTSLP)(u32 port, void *addr, u32 count);
-
-	u8  (*INB)(u32 port);
-	u16 (*INW)(u32 port);
-	u32 (*INL)(u32 port);
-	u8  (*INBP)(u32 port);
-	u16 (*INWP)(u32 port);
-	u32 (*INLP)(u32 port);
-	void (*INSW)(u32 port, void *addr, u32 count);
-	void (*INSWP)(u32 port, void *addr, u32 count);
-	void (*INSL)(u32 port, void *addr, u32 count);
-	void (*INSLP)(u32 port, void *addr, u32 count);
+	void (*OUTB)(u8 addr, ide_ioreg_t port);
+	void (*OUTW)(u16 addr, ide_ioreg_t port);
+	void (*OUTL)(u32 addr, ide_ioreg_t port);
+	void (*OUTSW)(ide_ioreg_t port, void *addr, u32 count);
+	void (*OUTSL)(ide_ioreg_t port, void *addr, u32 count);
+
+	u8  (*INB)(ide_ioreg_t port);
+	u16 (*INW)(ide_ioreg_t port);
+	u32 (*INL)(ide_ioreg_t port);
+	void (*INSW)(ide_ioreg_t port, void *addr, u32 count);
+	void (*INSL)(ide_ioreg_t port, void *addr, u32 count);
 #endif
 
 	/* dma physical region descriptor table (cpu view) */
diff -Nru a/include/linux/if_eql.h b/include/linux/if_eql.h
--- a/include/linux/if_eql.h	Mon Oct  7 11:25:36 2002
+++ b/include/linux/if_eql.h	Mon Oct  7 11:25:36 2002
@@ -19,8 +19,6 @@
 #ifndef _LINUX_IF_EQL_H
 #define _LINUX_IF_EQL_H
 
-#include <linux/timer.h>
-
 #define EQL_DEFAULT_SLAVE_PRIORITY 28800
 #define EQL_DEFAULT_MAX_SLAVES     4
 #define EQL_DEFAULT_MTU            576
@@ -35,46 +33,51 @@
 #define EQL_GETMASTRCFG (SIOCDEVPRIVATE + 4)
 #define EQL_SETMASTRCFG (SIOCDEVPRIVATE + 5)
 
+#ifdef __KERNEL__
+
+#include <linux/timer.h>
+#include <linux/spinlock.h>
+
 typedef struct slave {
-  struct net_device *dev;
-  long priority;
-  long priority_bps;
-  long priority_Bps;
-  long bytes_queued;
-  struct slave *next;
+	struct list_head	list;
+	struct net_device	*dev;
+	long			priority;
+	long			priority_bps;
+	long			priority_Bps;
+	long			bytes_queued;
 } slave_t;
 
 typedef struct slave_queue {
-  slave_t *head;
-  slave_t *best_slave;
-  int num_slaves;
-  struct net_device *master_dev;
-  char lock;
+	spinlock_t		lock;
+	struct list_head	all_slaves;
+	int			num_slaves;
+	struct net_device	*master_dev;
 } slave_queue_t;
 
 typedef struct equalizer {
-  slave_queue_t *queue;
-  int min_slaves;
-  int max_slaves;
-  struct net_device_stats *stats;
-  struct timer_list timer;
-  char timer_on;
+	slave_queue_t		queue;
+	int			min_slaves;
+	int			max_slaves;
+	struct net_device_stats	stats;
+	struct timer_list	timer;
 } equalizer_t;  
 
+#endif /* __KERNEL__ */
+
 typedef struct master_config {
-  char master_name[16];
-  int max_slaves;
-  int min_slaves;
+	char	master_name[16];
+	int	max_slaves;
+	int	min_slaves;
 } master_config_t;
 
 typedef struct slave_config {
-  char slave_name[16];
-  long priority;
+	char	slave_name[16];
+	long	priority;
 } slave_config_t;
 
 typedef struct slaving_request {
-  char slave_name[16];
-  long priority;
+	char	slave_name[16];
+	long	priority;
 } slaving_request_t;
 
 
diff -Nru a/include/linux/if_wanpipe_common.h b/include/linux/if_wanpipe_common.h
--- a/include/linux/if_wanpipe_common.h	Mon Oct  7 11:25:36 2002
+++ b/include/linux/if_wanpipe_common.h	Mon Oct  7 11:25:36 2002
@@ -39,7 +39,7 @@
 	int   (*func) (struct sk_buff *, netdevice_t *, 
                        struct sock *);
 
-	struct tq_struct wanpipe_task;    /* Immediate BH handler task */
+	struct work_struct wanpipe_work;    /* deferred keventd work */
 	unsigned char rw_bind;			  /* Sock bind state */
 	unsigned char usedby;
 	unsigned char state;
diff -Nru a/include/linux/isapnp.h b/include/linux/isapnp.h
--- a/include/linux/isapnp.h	Mon Oct  7 11:25:33 2002
+++ b/include/linux/isapnp.h	Mon Oct  7 11:25:33 2002
@@ -132,11 +132,11 @@
 #define ISAPNP_CARD_DEVS	8
 
 #define ISAPNP_CARD_ID(_va, _vb, _vc, _device) \
-		card_vendor: ISAPNP_VENDOR(_va, _vb, _vc), card_device: ISAPNP_DEVICE(_device)
+		.card_vendor = ISAPNP_VENDOR(_va, _vb, _vc), .card_device = ISAPNP_DEVICE(_device)
 #define ISAPNP_CARD_END \
-		card_vendor: 0, card_device: 0
+		.card_vendor = 0, .card_device = 0
 #define ISAPNP_DEVICE_ID(_va, _vb, _vc, _function) \
-		{ vendor: ISAPNP_VENDOR(_va, _vb, _vc), function: ISAPNP_FUNCTION(_function) }
+		{ .vendor = ISAPNP_VENDOR(_va, _vb, _vc), .function = ISAPNP_FUNCTION(_function) }
 
 /* export used IDs outside module */
 #define ISAPNP_CARD_TABLE(name) \
@@ -151,10 +151,10 @@
 };
 
 #define ISAPNP_DEVICE_SINGLE(_cva, _cvb, _cvc, _cdevice, _dva, _dvb, _dvc, _dfunction) \
-		card_vendor: ISAPNP_VENDOR(_cva, _cvb, _cvc), card_device: ISAPNP_DEVICE(_cdevice), \
-		vendor: ISAPNP_VENDOR(_dva, _dvb, _dvc), function: ISAPNP_FUNCTION(_dfunction)
+		.card_vendor = ISAPNP_VENDOR(_cva, _cvb, _cvc), .card_device =  ISAPNP_DEVICE(_cdevice), \
+		.vendor = ISAPNP_VENDOR(_dva, _dvb, _dvc), .function = ISAPNP_FUNCTION(_dfunction)
 #define ISAPNP_DEVICE_SINGLE_END \
-		card_vendor: 0, card_device: 0
+		.card_vendor = 0, .card_device = 0
 
 struct isapnp_device_id {
 	unsigned short card_vendor, card_device;
diff -Nru a/include/linux/isdn.h b/include/linux/isdn.h
--- a/include/linux/isdn.h	Mon Oct  7 11:25:39 2002
+++ b/include/linux/isdn.h	Mon Oct  7 11:25:40 2002
@@ -16,6 +16,9 @@
 
 #include <linux/ioctl.h>
 
+// FIXME!!!
+#include <../drivers/isdn/i4l/isdn_fsm.h>
+
 #ifdef CONFIG_COBALT_MICRO_SERVER
 /* Save memory */
 #define ISDN_MAX_DRIVERS    2
@@ -282,6 +285,8 @@
 struct isdn_net_local_s;
 
 struct isdn_netif_ops {
+	int			(*hard_start_xmit) (struct sk_buff *skb,
+						    struct net_device *dev);
 	int			(*hard_header) (struct sk_buff *skb,
 						struct net_device *dev,
 						unsigned short type,
@@ -294,26 +299,24 @@
 	unsigned short		flags;	/* interface flags (a la BSD)	*/
 	unsigned short		type;	/* interface hardware type	*/
 	unsigned char		addr_len;/* hardware address length	*/
-	void                    (*receive)(struct isdn_net_dev_s *p,
-					   struct isdn_net_local_s *olp,
-					   struct sk_buff *skb);
-	void                    (*connected)(struct isdn_net_local_s *lp);
-	void                    (*disconnected)(struct isdn_net_local_s *lp);
-	int                     (*bind)(struct isdn_net_local_s *lp);
-	void                    (*unbind)(struct isdn_net_local_s *lp);
-	int                     (*init)(struct isdn_net_local_s *lp);
-	void                    (*cleanup)(struct isdn_net_local_s *lp);
-	int                     (*open)(struct isdn_net_local_s *lp);
-	void                    (*close)(struct isdn_net_local_s *lp);
+	void                    (*receive)(struct isdn_net_local_s *,
+					   struct isdn_net_dev_s *,
+					   struct sk_buff *);
+	void                    (*connected)(struct isdn_net_dev_s *);
+	void                    (*disconnected)(struct isdn_net_dev_s *);
+	int                     (*bind)(struct isdn_net_dev_s *);
+	void                    (*unbind)(struct isdn_net_dev_s *);
+	int                     (*init)(struct isdn_net_local_s *);
+	void                    (*cleanup)(struct isdn_net_local_s *);
+	int                     (*open)(struct isdn_net_local_s *);
+	void                    (*close)(struct isdn_net_local_s *);
 };
 
 /* Local interface-data */
 typedef struct isdn_net_local_s {
   ulong                  magic;
-  spinlock_t             lock;
   struct net_device_stats stats;       /* Ethernet Statistics              */
   int                    flags;        /* Connection-flags                 */
-  int                    dialretry;    /* Counter for Dialout-retries      */
   int                    dialmax;      /* Max. Number of Dial-retries      */
   int	        	 dialtimeout;  /* How long shall we try on dialing */
   int			 dialwait;     /* wait after failed attempt        */
@@ -329,26 +332,17 @@
   u_char                 l2_proto;     /* Layer-2-protocol                 */
   u_char                 l3_proto;     /* Layer-3-protocol                 */
 
-  int                    sqfull;       /* Flag: netdev-queue overloaded    */
-  ulong                  sqfull_stamp; /* Start-Time of overload           */
   ulong                  slavedelay;   /* Dynamic bundling delaytime       */
   int                    triggercps;   /* BogoCPS needed for trigger slave */
   struct list_head       phone[2];     /* List of remote-phonenumbers      */
 				       /* phone[0] = Incoming Numbers      */
 				       /* phone[1] = Outgoing Numbers      */
-  struct net_device      *master;      /* Ptr to Master device for slaves  */
-  struct net_device      *slave;       /* Ptr to Slave device for masters  */
-  struct isdn_net_local_s *next;       /* Ptr to next link in bundle       */
-  struct isdn_net_local_s *last;       /* Ptr to last link in bundle       */
-  struct isdn_net_dev_s  *netdev;      /* Ptr to netdev                    */
-  struct sk_buff_head    super_tx_queue; /* List of supervisory frames to  */
-	                               /* be transmitted asap              */
-  atomic_t frame_cnt;                  /* number of frames currently       */
-                        	       /* queued in HL driver              */    
-                                       /* Ptr to orig. hard_header_cache   */
-  spinlock_t             xmit_lock;    /* used to protect the xmit path of */
-                                       /* a particular channel (including  */
-                                       /* the frame_cnt                    */
+
+  struct list_head       slaves;       /* list of all bundled channels     */
+  struct list_head       online;       /* circular list of all bundled
+					  channels, which are currently
+					  online                           */
+  spinlock_t             online_lock;  /* lock to protect queue            */
 
 #ifdef CONFIG_ISDN_X25
   struct concap_device_ops *dops;      /* callbacks used by encapsulator   */
@@ -361,33 +355,34 @@
   ulong cisco_last_slarp_in;		/* jiffie of last keepalive packet we received */
   char cisco_line_state;		/* state of line according to keepalive packets */
   char cisco_debserint;			/* debugging flag of cisco hdlc with slarp */
-  struct timer_list cisco_timer;
-  struct tq_struct tqueue;
-  struct isdn_netif_ops   *ops;
+
+  struct timer_list       cisco_timer;
+
+  struct isdn_netif_ops  *ops;
+
+  struct net_device       dev;          /* interface to upper levels        */
 } isdn_net_local;
 
 /* the interface itself */
 typedef struct isdn_net_dev_s {
-  isdn_net_local         local;
-
   int                    isdn_slot;    /* Index to isdn device/channel     */
   int                    pre_device;   /* Preselected isdn-device          */
   int                    pre_channel;  /* Preselected isdn-channel         */
   int                    exclusive;    /* -1 if non excl./idx to excl chan */
 
   struct timer_list      dial_timer;   /* dial events timer                */
+  struct fsm_inst        fi;           /* call control state machine       */
   int                    dial_event;   /* event in case of timer expiry    */
-  int                    dialstate;    /* State for dialing                */
   int                    dial;         /* # of phone number just dialed    */
   int                    outgoing;     /* Flag: outgoing call              */
-  unsigned long		 dialstarted;	/* first dialing-attempt           */
-  unsigned long		 dialwait_timer;/* earliest next dialing-attempt   */
+  int                    dialretry;    /* Counter for Dialout-retries      */
 
   int                    cps;          /* current speed of this interface  */
   int                    transcount;   /* byte-counter for cps-calculation */
   int                    last_jiffies; /* when transcount was reset        */
+  int                    sqfull;       /* Flag: netdev-queue overloaded    */
+  ulong                  sqfull_stamp; /* Start-Time of overload           */
 
-  struct timer_list      hup_timer;    /* auto hangup timer                */
   int                    huptimer;     /* Timeout-counter for auto-hangup  */
   int                    charge;       /* Counter for charging units       */
   int                    charge_state; /* ChargeInfo state machine         */
@@ -397,13 +392,22 @@
   int                    pppbind;      /* ippp device for bindings         */
   int			 ppp_slot;     /* PPPD device slot number          */
 
-  isdn_net_local         *queue;       /* circular list of all bundled
-					  channels, which are currently
-					  online                           */
-  spinlock_t queue_lock;               /* lock to protect queue            */
+  spinlock_t             xmit_lock;    /* used to protect the xmit path of */
+                                       /* a particular channel (including  */
+                                       /* the frame_cnt                    */
+  struct sk_buff_head    super_tx_queue; /* List of supervisory frames to  */
+	                               /* be transmitted asap              */
+  atomic_t               frame_cnt;    /* number of frames currently       */
+                        	       /* queued in HL driver              */
+  struct tasklet_struct  tlet;
+
+  isdn_net_local        *mlp;          /* Ptr to master device for all devs*/
+
+  struct list_head       slaves;       /* Members of local->slaves         */
+  struct list_head       online;       /* Members of local->online         */
+
   char                   name[10];     /* Name of device                   */
-  struct list_head global_list;        /* global list of all isdn_net_devs */
-  struct net_device dev;               /* interface to upper levels        */
+  struct list_head       global_list;  /* global list of all isdn_net_devs */
 #ifdef CONFIG_ISDN_PPP
   ippp_bundle * pb;		/* pointer to the common bundle structure
    			         * with the per-bundle data */
diff -Nru a/include/linux/isdn_ppp.h b/include/linux/isdn_ppp.h
--- a/include/linux/isdn_ppp.h	Mon Oct  7 11:25:27 2002
+++ b/include/linux/isdn_ppp.h	Mon Oct  7 11:25:27 2002
@@ -159,14 +159,7 @@
   isdn_mppp_stats stats;
 } ippp_bundle;
 
-#define NUM_RCV_BUFFS     64
-
-struct ippp_buf_queue {
-  struct ippp_buf_queue *next;
-  struct ippp_buf_queue *last;
-  char *buf;                 /* NULL here indicates end of queue */
-  int len;
-};
+#define IPPP_MAX_RQ_LEN     8
 
 /* The data structure for one CCP reset transaction */
 enum ippp_ccp_reset_states {
@@ -201,9 +194,7 @@
 struct ippp_struct {
   struct ippp_struct *next_link;
   int state;
-  struct ippp_buf_queue rq[NUM_RCV_BUFFS]; /* packet queue for isdn_ppp_read() */
-  struct ippp_buf_queue *first;  /* pointer to (current) first packet */
-  struct ippp_buf_queue *last;   /* pointer to (current) last used packet in queue */
+  struct sk_buff_head rq;
   wait_queue_head_t wq;
   struct task_struct *tk;
   unsigned int mpppcfg;
diff -Nru a/include/linux/isicom.h b/include/linux/isicom.h
--- a/include/linux/isicom.h	Mon Oct  7 11:25:32 2002
+++ b/include/linux/isicom.h	Mon Oct  7 11:25:32 2002
@@ -154,8 +154,8 @@
 	struct tty_struct 	* tty;
 	wait_queue_head_t	close_wait;
 	wait_queue_head_t	open_wait;
-	struct tq_struct	hangup_tq;
-	struct tq_struct	bh_tqueue;
+	struct work_struct	hangup_tq;
+	struct work_struct	bh_tqueue;
 	unsigned char		* xmit_buf;
 	int			xmit_head;
 	int			xmit_tail;
diff -Nru a/include/linux/istallion.h b/include/linux/istallion.h
--- a/include/linux/istallion.h	Mon Oct  7 11:25:27 2002
+++ b/include/linux/istallion.h	Mon Oct  7 11:25:27 2002
@@ -79,7 +79,7 @@
 	wait_queue_head_t	close_wait;
 	wait_queue_head_t	raw_wait;
 #endif
-	struct tq_struct	tqhangup;
+	struct work_struct	tqhangup;
 	struct termios		normaltermios;
 	struct termios		callouttermios;
 	asysigs_t		asig;
diff -Nru a/include/linux/jffs2_fs_sb.h b/include/linux/jffs2_fs_sb.h
--- a/include/linux/jffs2_fs_sb.h	Mon Oct  7 11:25:25 2002
+++ b/include/linux/jffs2_fs_sb.h	Mon Oct  7 11:25:25 2002
@@ -5,7 +5,7 @@
 
 #include <linux/types.h>
 #include <linux/spinlock.h>
-#include <linux/tqueue.h>
+#include <linux/workqueue.h>
 #include <linux/completion.h>
 #include <asm/semaphore.h>
 #include <linux/list.h>
@@ -82,7 +82,7 @@
 	uint32_t wbuf_ofs;
 	uint32_t wbuf_len;
 	uint32_t wbuf_pagesize;
-	struct tq_struct wbuf_task;		/* task for timed wbuf flush */
+	struct work_struct wbuf_task;		/* task for timed wbuf flush */
 	struct timer_list wbuf_timer;		/* timer for flushing wbuf */
 
 	/* OS-private pointer for getting back to master superblock info */
diff -Nru a/include/linux/kbd_kern.h b/include/linux/kbd_kern.h
--- a/include/linux/kbd_kern.h	Mon Oct  7 11:25:36 2002
+++ b/include/linux/kbd_kern.h	Mon Oct  7 11:25:36 2002
@@ -150,7 +150,7 @@
 
 static inline void con_schedule_flip(struct tty_struct *t)
 {
-	schedule_task(&t->flip.tqueue);
+	schedule_work(&t->flip.work);
 }
 
 #endif
diff -Nru a/include/linux/kernel_stat.h b/include/linux/kernel_stat.h
--- a/include/linux/kernel_stat.h	Mon Oct  7 11:25:33 2002
+++ b/include/linux/kernel_stat.h	Mon Oct  7 11:25:33 2002
@@ -18,19 +18,14 @@
 struct kernel_stat {
 	unsigned int per_cpu_user[NR_CPUS],
 	             per_cpu_nice[NR_CPUS],
-	             per_cpu_system[NR_CPUS];
+	             per_cpu_system[NR_CPUS],
+	             per_cpu_idle[NR_CPUS],
+	             per_cpu_iowait[NR_CPUS];
 	unsigned int dk_drive[DK_MAX_MAJOR][DK_MAX_DISK];
 	unsigned int dk_drive_rio[DK_MAX_MAJOR][DK_MAX_DISK];
 	unsigned int dk_drive_wio[DK_MAX_MAJOR][DK_MAX_DISK];
 	unsigned int dk_drive_rblk[DK_MAX_MAJOR][DK_MAX_DISK];
 	unsigned int dk_drive_wblk[DK_MAX_MAJOR][DK_MAX_DISK];
-	unsigned int pgpgin, pgpgout;
-	unsigned int pswpin, pswpout;
-	unsigned int pgalloc, pgfree;
-	unsigned int pgactivate, pgdeactivate;
-	unsigned int pgfault, pgmajfault;
-	unsigned int pgscan, pgsteal;
-	unsigned int pageoutrun, allocstall;
 #if !defined(CONFIG_ARCH_S390)
 	unsigned int irqs[NR_CPUS][NR_IRQS];
 #endif
diff -Nru a/include/linux/list.h b/include/linux/list.h
--- a/include/linux/list.h	Mon Oct  7 11:25:25 2002
+++ b/include/linux/list.h	Mon Oct  7 11:25:25 2002
@@ -86,13 +86,12 @@
 /**
  * list_del - deletes entry from list.
  * @entry: the element to delete from the list.
- * Note: list_empty on entry does not return true after this, the entry is in an undefined state.
+ * Note: list_empty on entry does not return true after this, the entry is
+ * in an undefined state.
  */
 static inline void list_del(struct list_head *entry)
 {
 	__list_del(entry->prev, entry->next);
-	entry->next = (void *) 0;
-	entry->prev = (void *) 0;
 }
 
 /**
diff -Nru a/include/linux/mm.h b/include/linux/mm.h
--- a/include/linux/mm.h	Mon Oct  7 11:25:25 2002
+++ b/include/linux/mm.h	Mon Oct  7 11:25:25 2002
@@ -176,7 +176,7 @@
 	 * Architectures with slow multiplication can define
 	 * WANT_PAGE_VIRTUAL in asm/page.h
 	 */
-#if defined(CONFIG_HIGHMEM) || defined(WANT_PAGE_VIRTUAL)
+#if defined(WANT_PAGE_VIRTUAL)
 	void *virtual;			/* Kernel virtual address (NULL if
 					   not kmapped, ie. highmem) */
 #endif /* CONFIG_HIGMEM || WANT_PAGE_VIRTUAL */
@@ -289,38 +289,34 @@
 	page->flags |= zone_num << ZONE_SHIFT;
 }
 
-/*
- * In order to avoid #ifdefs within C code itself, we define
- * set_page_address to a noop for non-highmem machines, where
- * the field isn't useful.
- * The same is true for page_address() in arch-dependent code.
- */
-#if defined(CONFIG_HIGHMEM) || defined(WANT_PAGE_VIRTUAL)
+#define lowmem_page_address(page)					\
+	__va( ( ((page) - page_zone(page)->zone_mem_map)		\
+			+ page_zone(page)->zone_start_pfn) << PAGE_SHIFT)
+
+#if defined(CONFIG_HIGHMEM) && !defined(WANT_PAGE_VIRTUAL)
+#define HASHED_PAGE_VIRTUAL
+#endif
 
+#if defined(WANT_PAGE_VIRTUAL)
+#define page_address(page) ((page)->virtual)
 #define set_page_address(page, address)			\
 	do {						\
 		(page)->virtual = (address);		\
 	} while(0)
+#define page_address_init()  do { } while(0)
+#endif
 
-#else /* CONFIG_HIGHMEM || WANT_PAGE_VIRTUAL */
-#define set_page_address(page, address)  do { } while(0)
-#endif /* CONFIG_HIGHMEM || WANT_PAGE_VIRTUAL */
+#if defined(HASHED_PAGE_VIRTUAL)
+void *page_address(struct page *page);
+void set_page_address(struct page *page, void *virtual);
+void page_address_init(void);
+#endif
 
-/*
- * Permanent address of a page. Obviously must never be
- * called on a highmem page.
- */
-#if defined(CONFIG_HIGHMEM) || defined(WANT_PAGE_VIRTUAL)
-
-#define page_address(page) ((page)->virtual)
-
-#else /* CONFIG_HIGHMEM || WANT_PAGE_VIRTUAL */
-
-#define page_address(page)						\
-	__va( ( ((page) - page_zone(page)->zone_mem_map)		\
-			+ page_zone(page)->zone_start_pfn) << PAGE_SHIFT)
-
-#endif /* CONFIG_HIGHMEM || WANT_PAGE_VIRTUAL */
+#if !defined(HASHED_PAGE_VIRTUAL) && !defined(WANT_PAGE_VIRTUAL)
+#define page_address(page) lowmem_page_address(page)
+#define set_page_address(page, address)  do { } while(0)
+#define page_address_init()  do { } while(0)
+#endif
 
 /*
  * Return true if this page is mapped into pagetables.  Subtle: test pte.direct
diff -Nru a/include/linux/mmzone.h b/include/linux/mmzone.h
--- a/include/linux/mmzone.h	Mon Oct  7 11:25:25 2002
+++ b/include/linux/mmzone.h	Mon Oct  7 11:25:25 2002
@@ -62,7 +62,6 @@
 	spinlock_t		lock;
 	unsigned long		free_pages;
 	unsigned long		pages_min, pages_low, pages_high;
-	int			need_balance;
 
 	ZONE_PADDING(_pad1_)
 
@@ -120,7 +119,8 @@
 	 * rarely used fields:
 	 */
 	char			*name;
-	unsigned long		size;
+	unsigned long		spanned_pages;	/* total size, including holes */
+	unsigned long		present_pages;	/* amount of memory (excluding holes) */
 } ____cacheline_maxaligned_in_smp;
 
 #define ZONE_DMA		0
diff -Nru a/include/linux/nbd.h b/include/linux/nbd.h
--- a/include/linux/nbd.h	Mon Oct  7 11:25:39 2002
+++ b/include/linux/nbd.h	Mon Oct  7 11:25:39 2002
@@ -79,7 +79,7 @@
 	spinlock_t queue_lock;
 	struct list_head queue_head;	/* Requests are added here...			*/
 	struct semaphore tx_lock;
-	struct gendisk disk;
+	struct gendisk *disk;
 };
 #endif
 
diff -Nru a/include/linux/ncp_fs_sb.h b/include/linux/ncp_fs_sb.h
--- a/include/linux/ncp_fs_sb.h	Mon Oct  7 11:25:25 2002
+++ b/include/linux/ncp_fs_sb.h	Mon Oct  7 11:25:25 2002
@@ -13,7 +13,7 @@
 
 #ifdef __KERNEL__
 
-#include <linux/tqueue.h>
+#include <linux/workqueue.h>
 
 #define NCP_DEFAULT_OPTIONS 0		/* 2 for packet signatures */
 
@@ -91,7 +91,7 @@
 	void (*error_report)(struct sock* sk);
 	void (*write_space)(struct sock* sk);	/* STREAM mode only */
 	struct {
-		struct tq_struct tq;		/* STREAM/DGRAM: data/error ready */
+		struct work_struct tq;		/* STREAM/DGRAM: data/error ready */
 		struct ncp_request_reply* creq;	/* STREAM/DGRAM: awaiting reply from this request */
 		struct semaphore creq_sem;	/* DGRAM only: lock accesses to rcv.creq */
 
@@ -110,11 +110,11 @@
 	} rcv;
 	struct {
 		struct list_head requests;	/* STREAM only: queued requests */
-		struct tq_struct tq;		/* STREAM only: transmitter ready */
+		struct work_struct tq;		/* STREAM only: transmitter ready */
 		struct ncp_request_reply* creq;	/* STREAM only: currently transmitted entry */
 	} tx;
 	struct timer_list timeout_tm;		/* DGRAM only: timeout timer */
-	struct tq_struct timeout_tq;		/* DGRAM only: associated queue, we run timers from process context */
+	struct work_struct timeout_tq;		/* DGRAM only: associated queue, we run timers from process context */
 	int timeout_last;			/* DGRAM only: current timeout length */
 	int timeout_retries;			/* DGRAM only: retries left */
 	struct {
diff -Nru a/include/linux/netdevice.h b/include/linux/netdevice.h
--- a/include/linux/netdevice.h	Mon Oct  7 11:25:39 2002
+++ b/include/linux/netdevice.h	Mon Oct  7 11:25:39 2002
@@ -587,7 +587,6 @@
 		dev_kfree_skb(skb);
 }
 
-extern void		net_call_rx_atomic(void (*fn)(void));
 #define HAVE_NETIF_RX 1
 extern int		netif_rx(struct sk_buff *skb);
 #define HAVE_NETIF_RECEIVE_SKB 1
diff -Nru a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h
--- a/include/linux/nfs_fs.h	Mon Oct  7 11:25:31 2002
+++ b/include/linux/nfs_fs.h	Mon Oct  7 11:25:31 2002
@@ -14,6 +14,7 @@
 #include <linux/mm.h>
 #include <linux/pagemap.h>
 #include <linux/wait.h>
+#include <linux/uio.h>
 
 #include <linux/nfs_fs_sb.h>
 
@@ -24,6 +25,7 @@
 #include <linux/nfs.h>
 #include <linux/nfs2.h>
 #include <linux/nfs3.h>
+#include <linux/nfs_page.h>
 #include <linux/nfs_xdr.h>
 
 /*
@@ -242,9 +244,9 @@
 }
 
 static inline
-unsigned long page_index(struct page *page)
+loff_t req_offset(struct nfs_page *req)
 {
-	return page->index;
+	return ((loff_t)req->wb_index) << PAGE_CACHE_SHIFT;
 }
 
 /*
@@ -284,6 +286,12 @@
 }
 
 /*
+ * linux/fs/nfs/direct.c
+ */
+extern int nfs_direct_IO(int, struct file *, const struct iovec *, loff_t,
+			unsigned long);
+
+/*
  * linux/fs/nfs/dir.c
  */
 extern struct inode_operations nfs_dir_inode_operations;
@@ -353,7 +361,8 @@
 static inline int
 nfs_wb_page(struct inode *inode, struct page* page)
 {
-	int error = nfs_sync_file(inode, 0, page_index(page), 1, FLUSH_WAIT | FLUSH_STABLE);
+	int error = nfs_sync_file(inode, 0, page->index, 1,
+						FLUSH_WAIT | FLUSH_STABLE);
 	return (error < 0) ? error : 0;
 }
 
diff -Nru a/include/linux/nfs_page.h b/include/linux/nfs_page.h
--- a/include/linux/nfs_page.h	Mon Oct  7 11:25:25 2002
+++ b/include/linux/nfs_page.h	Mon Oct  7 11:25:25 2002
@@ -33,7 +33,8 @@
 	struct page		*wb_page;	/* page to read in/write out */
 	wait_queue_head_t	wb_wait;	/* wait queue */
 	unsigned long		wb_timeout;	/* when to read/write/commit */
-	unsigned int		wb_offset,	/* Offset of read/write */
+	unsigned long		wb_index;	/* Offset within mapping */
+	unsigned int		wb_offset,	/* Offset within page */
 				wb_bytes,	/* Length of request */
 				wb_count;	/* reference count */
 	unsigned long		wb_flags;
@@ -47,6 +48,7 @@
 					    unsigned int, unsigned int);
 extern	void nfs_clear_request(struct nfs_page *req);
 extern	void nfs_release_request(struct nfs_page *req);
+extern	void nfs_release_list(struct list_head *list);
 
 
 extern	void nfs_list_add_request(struct nfs_page *, struct list_head *);
@@ -58,6 +60,7 @@
 extern	int nfs_coalesce_requests(struct list_head *, struct list_head *,
 				  unsigned int);
 extern  int nfs_wait_on_request(struct nfs_page *);
+extern	int nfs_wait_for_reads(struct list_head *);
 
 extern	spinlock_t nfs_wreq_lock;
 
diff -Nru a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h
--- a/include/linux/nfs_xdr.h	Mon Oct  7 11:25:37 2002
+++ b/include/linux/nfs_xdr.h	Mon Oct  7 11:25:37 2002
@@ -1,6 +1,8 @@
 #ifndef _LINUX_NFS_XDR_H
 #define _LINUX_NFS_XDR_H
 
+#include <linux/sunrpc/xprt.h>
+
 struct nfs_fattr {
 	unsigned short		valid;		/* which fields are valid */
 	__u64			pre_size;	/* pre_op_attr.size	  */
@@ -57,10 +59,14 @@
 	__u32			namelen;/* max name length */
 };
 
-/* Arguments to the read call.
- * Note that NFS_READ_MAXIOV must be <= (MAX_IOVEC-2) from sunrpc/xprt.h
+/*
+ * Arguments to the read call.
  */
-#define NFS_READ_MAXIOV 8
+
+#define NFS_READ_MAXIOV		(9U)
+#if (NFS_READ_MAXIOV > (MAX_IOVEC -2))
+#error "NFS_READ_MAXIOV is too large"
+#endif
 
 struct nfs_readargs {
 	struct nfs_fh *		fh;
@@ -76,10 +82,14 @@
 	int                     eof;
 };
 
-/* Arguments to the write call.
- * Note that NFS_WRITE_MAXIOV must be <= (MAX_IOVEC-2) from sunrpc/xprt.h
+/*
+ * Arguments to the write call.
  */
-#define NFS_WRITE_MAXIOV        8
+#define NFS_WRITE_MAXIOV	(9U)
+#if (NFS_WRITE_MAXIOV > (MAX_IOVEC -2))
+#error "NFS_WRITE_MAXIOV is too large"
+#endif
+
 struct nfs_writeargs {
 	struct nfs_fh *		fh;
 	__u64			offset;
diff -Nru a/include/linux/page-flags.h b/include/linux/page-flags.h
--- a/include/linux/page-flags.h	Mon Oct  7 11:25:36 2002
+++ b/include/linux/page-flags.h	Mon Oct  7 11:25:36 2002
@@ -70,7 +70,8 @@
 #define PG_direct		16	/* ->pte_chain points directly at pte */
 
 /*
- * Global page accounting.  One instance per CPU.
+ * Global page accounting.  One instance per CPU.  Only unsigned longs are
+ * allowed.
  */
 extern struct page_state {
 	unsigned long nr_dirty;
@@ -80,9 +81,32 @@
 	unsigned long nr_reverse_maps;
 	unsigned long nr_mapped;
 	unsigned long nr_slab;
+#define GET_PAGE_STATE_LAST nr_slab
+
+	/*
+	 * The below are zeroed by get_page_state().  Use get_full_page_state()
+	 * to add up all these.
+	 */
+	unsigned long pgpgin;
+	unsigned long pgpgout;
+	unsigned long pswpin;
+	unsigned long pswpout;
+	unsigned long pgalloc;
+	unsigned long pgfree;
+	unsigned long pgactivate;
+	unsigned long pgdeactivate;
+	unsigned long pgfault;
+	unsigned long pgmajfault;
+	unsigned long pgscan;
+	unsigned long pgrefill;
+	unsigned long pgsteal;
+	unsigned long kswapd_steal;
+	unsigned long pageoutrun;
+	unsigned long allocstall;
 } ____cacheline_aligned_in_smp page_states[NR_CPUS];
 
 extern void get_page_state(struct page_state *ret);
+extern void get_full_page_state(struct page_state *ret);
 
 #define mod_page_state(member, delta)					\
 	do {								\
diff -Nru a/include/linux/pagemap.h b/include/linux/pagemap.h
--- a/include/linux/pagemap.h	Mon Oct  7 11:25:25 2002
+++ b/include/linux/pagemap.h	Mon Oct  7 11:25:25 2002
@@ -41,6 +41,9 @@
 				unsigned long index);
 extern struct page * find_or_create_page(struct address_space *mapping,
 				unsigned long index, unsigned int gfp_mask);
+extern unsigned int find_get_pages(struct address_space *mapping,
+				pgoff_t start, unsigned int nr_pages,
+				struct page **pages);
 
 /*
  * Returns locked page at given index in given cache, creating it if needed.
diff -Nru a/include/linux/pagevec.h b/include/linux/pagevec.h
--- a/include/linux/pagevec.h	Mon Oct  7 11:25:39 2002
+++ b/include/linux/pagevec.h	Mon Oct  7 11:25:39 2002
@@ -8,6 +8,7 @@
 #define PAGEVEC_SIZE	16
 
 struct page;
+struct address_space;
 
 struct pagevec {
 	unsigned nr;
@@ -21,6 +22,8 @@
 void lru_add_drain(void);
 void pagevec_deactivate_inactive(struct pagevec *pvec);
 void pagevec_strip(struct pagevec *pvec);
+unsigned int pagevec_lookup(struct pagevec *pvec, struct address_space *mapping,
+		pgoff_t start, unsigned int nr_pages);
 
 static inline void pagevec_init(struct pagevec *pvec)
 {
diff -Nru a/include/linux/pci.h b/include/linux/pci.h
--- a/include/linux/pci.h	Mon Oct  7 11:25:35 2002
+++ b/include/linux/pci.h	Mon Oct  7 11:25:35 2002
@@ -322,15 +322,6 @@
 
 #define PCI_ANY_ID (~0)
 
-#define pci_present pcibios_present
-
-
-#define pci_for_each_dev_reverse(dev) \
-	for(dev = pci_dev_g(pci_devices.prev); dev != pci_dev_g(&pci_devices); dev = pci_dev_g(dev->global_list.prev))
-
-#define pci_for_each_bus(bus) \
-for(bus = pci_bus_b(pci_root_buses.next); bus != pci_bus_b(&pci_root_buses); bus = pci_bus_b(bus->node.next))
-
 /*
  * The pci_dev structure is used to describe both PCI and ISAPnP devices.
  */
@@ -503,8 +494,17 @@
 /* these external functions are only available when PCI support is enabled */
 #ifdef CONFIG_PCI
 
+static inline int pci_present(void)
+{
+	return !list_empty(&pci_devices);
+}
+
 #define pci_for_each_dev(dev) \
 	for(dev = pci_dev_g(pci_devices.next); dev != pci_dev_g(&pci_devices); dev = pci_dev_g(dev->global_list.next))
+#define pci_for_each_dev_reverse(dev) \
+	for(dev = pci_dev_g(pci_devices.prev); dev != pci_dev_g(&pci_devices); dev = pci_dev_g(dev->global_list.prev))
+#define pci_for_each_bus(bus) \
+	for(bus = pci_bus_b(pci_root_buses.next); bus != pci_bus_b(&pci_root_buses); bus = pci_bus_b(bus->node.next))
 
 void pcibios_fixup_bus(struct pci_bus *);
 int pcibios_enable_device(struct pci_dev *, int mask);
@@ -520,7 +520,6 @@
 
 /* Backward compatibility, don't use in new code! */
 
-int pcibios_present(void);
 int pcibios_read_config_byte (unsigned char bus, unsigned char dev_fn,
 			      unsigned char where, unsigned char *val);
 int pcibios_read_config_word (unsigned char bus, unsigned char dev_fn,
@@ -533,10 +532,6 @@
 			       unsigned char where, unsigned short val);
 int pcibios_write_config_dword (unsigned char bus, unsigned char dev_fn,
 				unsigned char where, unsigned int val);
-int pcibios_find_class (unsigned int class_code, unsigned short index, unsigned char *bus, unsigned char *dev_fn);
-int pcibios_find_device (unsigned short vendor, unsigned short dev_id,
-			 unsigned short index, unsigned char *bus,
-			 unsigned char *dev_fn);
 
 /* Generic PCI functions used internally */
 
@@ -642,7 +637,7 @@
 
 /* kmem_cache style wrapper around pci_alloc_consistent() */
 struct pci_pool *pci_pool_create (const char *name, struct pci_dev *dev,
-		size_t size, size_t align, size_t allocation, int flags);
+		size_t size, size_t align, size_t allocation);
 void pci_pool_destroy (struct pci_pool *pool);
 
 void *pci_pool_alloc (struct pci_pool *pool, int flags, dma_addr_t *handle);
@@ -660,9 +655,7 @@
  */
 
 #ifndef CONFIG_PCI
-static inline int pcibios_present(void) { return 0; }
-static inline int pcibios_find_class (unsigned int class_code, unsigned short index, unsigned char *bus, unsigned char *dev_fn) 
-{ 	return PCIBIOS_DEVICE_NOT_FOUND; }
+static inline int pci_present(void) { return 0; }
 
 #define _PCI_NOP(o,s,t) \
 	static inline int pcibios_##o##_config_##s (u8 bus, u8 dfn, u8 where, t val) \
diff -Nru a/include/linux/pci_ids.h b/include/linux/pci_ids.h
--- a/include/linux/pci_ids.h	Mon Oct  7 11:25:32 2002
+++ b/include/linux/pci_ids.h	Mon Oct  7 11:25:32 2002
@@ -288,6 +288,12 @@
 #define PCI_DEVICE_ID_NS_87560_USB	0x0012
 #define PCI_DEVICE_ID_NS_83815		0x0020
 #define PCI_DEVICE_ID_NS_83820		0x0022
+#define PCI_DEVICE_ID_NS_SCx200_BRIDGE	0x0500
+#define PCI_DEVICE_ID_NS_SCx200_SMI	0x0501
+#define PCI_DEVICE_ID_NS_SCx200_IDE	0x0502
+#define PCI_DEVICE_ID_NS_SCx200_AUDIO	0x0503
+#define PCI_DEVICE_ID_NS_SCx200_VIDEO	0x0504
+#define PCI_DEVICE_ID_NS_SCx200_XBUS	0x0505
 #define PCI_DEVICE_ID_NS_87410		0xd001
 
 #define PCI_VENDOR_ID_TSENG		0x100c
diff -Nru a/include/linux/pipe_fs_i.h b/include/linux/pipe_fs_i.h
--- a/include/linux/pipe_fs_i.h	Mon Oct  7 11:25:36 2002
+++ b/include/linux/pipe_fs_i.h	Mon Oct  7 11:25:36 2002
@@ -9,7 +9,6 @@
 	unsigned int start;
 	unsigned int readers;
 	unsigned int writers;
-	unsigned int waiting_readers;
 	unsigned int waiting_writers;
 	unsigned int r_counter;
 	unsigned int w_counter;
@@ -28,7 +27,6 @@
 #define PIPE_LEN(inode)		((inode).i_pipe->len)
 #define PIPE_READERS(inode)	((inode).i_pipe->readers)
 #define PIPE_WRITERS(inode)	((inode).i_pipe->writers)
-#define PIPE_WAITING_READERS(inode)	((inode).i_pipe->waiting_readers)
 #define PIPE_WAITING_WRITERS(inode)	((inode).i_pipe->waiting_writers)
 #define PIPE_RCOUNTER(inode)	((inode).i_pipe->r_counter)
 #define PIPE_WCOUNTER(inode)	((inode).i_pipe->w_counter)
diff -Nru a/include/linux/radix-tree.h b/include/linux/radix-tree.h
--- a/include/linux/radix-tree.h	Mon Oct  7 11:25:28 2002
+++ b/include/linux/radix-tree.h	Mon Oct  7 11:25:28 2002
@@ -45,5 +45,8 @@
 extern int radix_tree_insert(struct radix_tree_root *, unsigned long, void *);
 extern void *radix_tree_lookup(struct radix_tree_root *, unsigned long);
 extern int radix_tree_delete(struct radix_tree_root *, unsigned long);
+extern unsigned int
+radix_tree_gang_lookup(struct radix_tree_root *root, void **results,
+			unsigned long first_index, unsigned int max_items);
 
 #endif /* _LINUX_RADIX_TREE_H */
diff -Nru a/include/linux/reiserfs_fs.h b/include/linux/reiserfs_fs.h
--- a/include/linux/reiserfs_fs.h	Mon Oct  7 11:25:38 2002
+++ b/include/linux/reiserfs_fs.h	Mon Oct  7 11:25:38 2002
@@ -16,7 +16,7 @@
 #ifdef __KERNEL__
 #include <linux/slab.h>
 #include <linux/interrupt.h>
-#include <linux/tqueue.h>
+#include <linux/workqueue.h>
 #include <asm/unaligned.h>
 #include <linux/bitops.h>
 #include <linux/proc_fs.h>
diff -Nru a/include/linux/reiserfs_fs_sb.h b/include/linux/reiserfs_fs_sb.h
--- a/include/linux/reiserfs_fs_sb.h	Mon Oct  7 11:25:35 2002
+++ b/include/linux/reiserfs_fs_sb.h	Mon Oct  7 11:25:35 2002
@@ -5,7 +5,7 @@
 #define _LINUX_REISER_FS_SB
 
 #ifdef __KERNEL__
-#include <linux/tqueue.h>
+#include <linux/workqueue.h>
 #endif
 
 
diff -Nru a/include/linux/sched.h b/include/linux/sched.h
--- a/include/linux/sched.h	Mon Oct  7 11:25:25 2002
+++ b/include/linux/sched.h	Mon Oct  7 11:25:25 2002
@@ -172,9 +172,6 @@
 extern signed long FASTCALL(schedule_timeout(signed long timeout));
 asmlinkage void schedule(void);
 
-extern int start_context_thread(void);
-extern int current_is_keventd(void);
-
 struct namespace;
 
 /* Maximum number of active map areas.. This is a random (large) number */
@@ -381,7 +378,7 @@
 /* signal handlers */
 	struct signal_struct *sig;
 
-	sigset_t blocked, real_blocked, shared_unblocked;
+	sigset_t blocked, real_blocked;
 	struct sigpending pending;
 
 	unsigned long sas_ss_sp;
@@ -433,6 +430,7 @@
 #define PF_FROZEN	0x00040000	/* frozen for system suspend */
 #define PF_SYNC		0x00080000	/* performing fsync(), etc */
 #define PF_FSTRANS	0x00100000	/* inside a filesystem transaction */
+#define PF_KSWAPD	0x00200000	/* I am kswapd */
 
 /*
  * Ptrace flags
@@ -532,7 +530,7 @@
 extern void flush_signals(struct task_struct *);
 extern void flush_signal_handlers(struct task_struct *);
 extern void sig_exit(int, int, struct siginfo *);
-extern int dequeue_signal(struct sigpending *pending, sigset_t *mask, siginfo_t *info);
+extern int dequeue_signal(sigset_t *mask, siginfo_t *info);
 extern void block_all_signals(int (*notifier)(void *priv), void *priv,
 			      sigset_t *mask);
 extern void unblock_all_signals(void);
diff -Nru a/include/linux/sctp.h b/include/linux/sctp.h
--- a/include/linux/sctp.h	Mon Oct  7 11:25:36 2002
+++ b/include/linux/sctp.h	Mon Oct  7 11:25:36 2002
@@ -155,15 +155,15 @@
 typedef enum {
 
 	/* RFC 2960 Section 3.3.5 */
-	SCTP_PARAM_HEATBEAT_INFO		= __constant_htons(1),  
+	SCTP_PARAM_HEARTBEAT_INFO		= __constant_htons(1),
 	/* RFC 2960 Section 3.3.2.1 */
-	SCTP_PARAM_IPV4_ADDRESS			= __constant_htons(5), 
-	SCTP_PARAM_IPV6_ADDRESS			= __constant_htons(6), 
-	SCTP_PARAM_STATE_COOKIE			= __constant_htons(7),  
-	SCTP_PARAM_UNRECOGNIZED_PARAMETERS	= __constant_htons(8),  
-	SCTP_PARAM_COOKIE_PRESERVATIVE		= __constant_htons(9),  
-	SCTP_PARAM_HOST_NAME_ADDRESS		= __constant_htons(11), 
-	SCTP_PARAM_SUPPORTED_ADDRESS_TYPES	= __constant_htons(12), 
+	SCTP_PARAM_IPV4_ADDRESS			= __constant_htons(5),
+	SCTP_PARAM_IPV6_ADDRESS			= __constant_htons(6),
+	SCTP_PARAM_STATE_COOKIE			= __constant_htons(7),
+	SCTP_PARAM_UNRECOGNIZED_PARAMETERS	= __constant_htons(8),
+	SCTP_PARAM_COOKIE_PRESERVATIVE		= __constant_htons(9),
+	SCTP_PARAM_HOST_NAME_ADDRESS		= __constant_htons(11),
+	SCTP_PARAM_SUPPORTED_ADDRESS_TYPES	= __constant_htons(12),
 	SCTP_PARAM_ECN_CAPABLE			= __constant_htons(0x8000),
 
 	/* Add-IP Extension. Section 3.2 */
@@ -190,6 +190,7 @@
 	SCTP_PARAM_ACTION_SKIP_ERR    = __constant_htons(0xc000),
 } sctp_param_action_t; 
 
+enum { SCTP_PARAM_ACTION_MASK = __constant_htons(0xc000), };
 
 /* RFC 2960 Section 3.3.1 Payload Data (DATA) (0) */
 
diff -Nru a/include/linux/scx200.h b/include/linux/scx200.h
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/include/linux/scx200.h	Mon Oct  7 11:26:06 2002
@@ -0,0 +1,56 @@
+/* linux/include/linux/scx200.h
+
+   Copyright (c) 2001,2002 Christer Weinigel <wingel@nano-system.com>
+
+   Defines for the National Semiconductor SCx200 Processors
+*/
+
+/* Interesting stuff for the National Semiconductor SCx200 CPU */
+
+/* F0 PCI Header/Bridge Configuration Registers */
+#define SCx200_DOCCS_BASE 0x78	/* DOCCS Base Address Register */
+#define SCx200_DOCCS_CTRL 0x7c	/* DOCCS Control Register */
+
+/* GPIO Register Block */
+#define SCx200_GPIO_SIZE 0x2c	/* Size of GPIO register block */
+
+/* General Configuration Block */
+#define SCx200_CB_BASE 0x9000	/* Base fixed at 0x9000 according to errata */
+
+/* Watchdog Timer */
+#define SCx200_WDT_OFFSET 0x00	/* offset within configuration block */
+#define SCx200_WDT_SIZE 0x05	/* size */
+
+#define SCx200_WDT_WDTO 0x00	/* Time-Out Register */
+#define SCx200_WDT_WDCNFG 0x02	/* Configuration Register */
+#define SCx200_WDT_WDSTS 0x04	/* Status Register */
+#define SCx200_WDT_WDSTS_WDOVF (1<<0) /* Overflow bit */
+
+/* High Resolution Timer */
+#define SCx200_TIMER_OFFSET 0x08
+#define SCx200_TIMER_SIZE 0x05
+
+/* Clock Generators */
+#define SCx200_CLOCKGEN_OFFSET 0x10
+#define SCx200_CLOCKGEN_SIZE 0x10
+
+/* Pin Multiplexing and Miscellaneous Configuration Registers */
+#define SCx200_MISC_OFFSET 0x30
+#define SCx200_MISC_SIZE 0x10
+
+#define SCx200_PMR 0x30		/* Pin Multiplexing Register */
+#define SCx200_MCR 0x34		/* Miscellaneous Configuration Register */
+#define SCx200_INTSEL 0x38	/* Interrupt Selection Register */
+#define SCx200_IID 0x3c		/* IA On a Chip Identification Number Reg */
+#define SCx200_REV 0x3d		/* Revision Register */
+#define SCx200_CBA 0x3e		/* Configuration Base Address Register */
+
+/* Verify that the configuration block really is there */
+#define scx200_cb_probe(base) (inw((base) + SCx200_CBA) == (base))
+
+/*
+    Local variables:
+        compile-command: "make -C ../.. bzImage modules"
+        c-basic-offset: 8
+    End:
+*/
diff -Nru a/include/linux/scx200_gpio.h b/include/linux/scx200_gpio.h
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/include/linux/scx200_gpio.h	Mon Oct  7 11:25:40 2002
@@ -0,0 +1,98 @@
+#include <linux/spinlock.h>
+
+u32 scx200_gpio_configure(int index, u32 set, u32 clear);
+void scx200_gpio_dump(unsigned index);
+
+extern unsigned scx200_gpio_base;
+extern spinlock_t scx200_gpio_lock;
+extern long scx200_gpio_shadow[2];
+
+#define scx200_gpio_present() (scx200_gpio_base!=0)
+
+/* Definitions to make sure I do the same thing in all functions */
+#define __SCx200_GPIO_BANK unsigned bank = index>>5
+#define __SCx200_GPIO_IOADDR unsigned short ioaddr = scx200_gpio_base+0x10*bank
+#define __SCx200_GPIO_SHADOW long *shadow = scx200_gpio_shadow+bank
+#define __SCx200_GPIO_INDEX index &= 31
+
+#define __SCx200_GPIO_OUT __asm__ __volatile__("outsl":"=mS" (shadow):"d" (ioaddr), "0" (shadow))
+
+/* returns the value of the GPIO pin */
+
+static inline int scx200_gpio_get(int index) {
+	__SCx200_GPIO_BANK;
+	__SCx200_GPIO_IOADDR + 0x04;
+	__SCx200_GPIO_INDEX;
+		
+	return (inl(ioaddr) & (1<<index)) ? 1 : 0;
+}
+
+/* return the value driven on the GPIO signal (the value that will be
+   driven if the GPIO is configured as an output, it might not be the
+   state of the GPIO right now if the GPIO is configured as an input) */
+
+static inline int scx200_gpio_current(int index) {
+        __SCx200_GPIO_BANK;
+	__SCx200_GPIO_INDEX;
+		
+	return (scx200_gpio_shadow[bank] & (1<<index)) ? 1 : 0;
+}
+
+/* drive the GPIO signal high */
+
+static inline void scx200_gpio_set_high(int index) {
+	__SCx200_GPIO_BANK;
+	__SCx200_GPIO_IOADDR;
+	__SCx200_GPIO_SHADOW;
+	__SCx200_GPIO_INDEX;
+	set_bit(index, shadow);
+	__SCx200_GPIO_OUT;
+}
+
+/* drive the GPIO signal low */
+
+static inline void scx200_gpio_set_low(int index) {
+	__SCx200_GPIO_BANK;
+	__SCx200_GPIO_IOADDR;
+	__SCx200_GPIO_SHADOW;
+	__SCx200_GPIO_INDEX;
+	clear_bit(index, shadow);
+	__SCx200_GPIO_OUT;
+}
+
+/* drive the GPIO signal to state */
+
+static inline void scx200_gpio_set(int index, int state) {
+	__SCx200_GPIO_BANK;
+	__SCx200_GPIO_IOADDR;
+	__SCx200_GPIO_SHADOW;
+	__SCx200_GPIO_INDEX;
+	if (state)
+		set_bit(index, shadow);
+	else
+		clear_bit(index, shadow);
+	__SCx200_GPIO_OUT;
+}
+
+/* toggle the GPIO signal */
+static inline void scx200_gpio_change(int index) {
+	__SCx200_GPIO_BANK;
+	__SCx200_GPIO_IOADDR;
+	__SCx200_GPIO_SHADOW;
+	__SCx200_GPIO_INDEX;
+	change_bit(index, shadow);
+	__SCx200_GPIO_OUT;
+}
+
+#undef __SCx200_GPIO_BANK
+#undef __SCx200_GPIO_IOADDR
+#undef __SCx200_GPIO_SHADOW
+#undef __SCx200_GPIO_INDEX
+#undef __SCx200_GPIO_OUT
+
+/*
+    Local variables:
+        compile-command: "make -C ../.. bzImage modules"
+        c-basic-offset: 8
+    End:
+*/
diff -Nru a/include/linux/serial167.h b/include/linux/serial167.h
--- a/include/linux/serial167.h	Mon Oct  7 11:25:29 2002
+++ b/include/linux/serial167.h	Mon Oct  7 11:25:29 2002
@@ -51,7 +51,7 @@
 	int			xmit_cnt;
         int                     default_threshold;
         int                     default_timeout;
-	struct tq_struct	tqueue;
+	struct work_struct	tqueue;
 	struct termios		normal_termios;
 	struct termios		callout_termios;
 	wait_queue_head_t	open_wait;
diff -Nru a/include/linux/serialP.h b/include/linux/serialP.h
--- a/include/linux/serialP.h	Mon Oct  7 11:25:25 2002
+++ b/include/linux/serialP.h	Mon Oct  7 11:25:25 2002
@@ -21,7 +21,7 @@
 
 #include <linux/config.h>
 #include <linux/termios.h>
-#include <linux/tqueue.h>
+#include <linux/workqueue.h>
 #include <linux/circ_buf.h>
 #include <linux/wait.h>
 #if (LINUX_VERSION_CODE < 0x020300)
@@ -86,7 +86,7 @@
 	u8			*iomem_base;
 	u16			iomem_reg_shift;
 	int			io_type;
-	struct tq_struct	tqueue;
+	struct work_struct			work;
 #ifdef DECLARE_WAITQUEUE
 	wait_queue_head_t	open_wait;
 	wait_queue_head_t	close_wait;
diff -Nru a/include/linux/serial_core.h b/include/linux/serial_core.h
--- a/include/linux/serial_core.h	Mon Oct  7 11:25:26 2002
+++ b/include/linux/serial_core.h	Mon Oct  7 11:25:26 2002
@@ -168,6 +168,8 @@
 #define UPF_BUGGY_UART		(1 << 14)
 #define UPF_AUTOPROBE		(1 << 15)
 #define UPF_BOOT_AUTOCONF	(1 << 28)
+#define UPF_RESOURCES		(1 << 30)
+#define UPF_IOREMAP		(1 << 31)
 
 #define UPF_FLAGS		(0x7fff)
 #define UPF_USR_MASK		(UPF_SPD_MASK|UPF_LOW_LATENCY)
diff -Nru a/include/linux/shmem_fs.h b/include/linux/shmem_fs.h
--- a/include/linux/shmem_fs.h	Mon Oct  7 11:25:37 2002
+++ b/include/linux/shmem_fs.h	Mon Oct  7 11:25:37 2002
@@ -11,11 +11,11 @@
 
 struct shmem_inode_info {
 	spinlock_t		lock;
-	struct semaphore 	sem;
 	unsigned long		next_index;
 	swp_entry_t		i_direct[SHMEM_NR_DIRECT]; /* for the first blocks */
-	void		      **i_indirect; /* indirect blocks */
-	unsigned long		swapped;
+	struct page	       *i_indirect; /* indirect blocks */
+	unsigned long		alloced;    /* data pages allocated to file */
+	unsigned long		swapped;    /* subtotal assigned to swap */
 	unsigned long		flags;
 	struct list_head	list;
 	struct inode		vfs_inode;
diff -Nru a/include/linux/stallion.h b/include/linux/stallion.h
--- a/include/linux/stallion.h	Mon Oct  7 11:25:38 2002
+++ b/include/linux/stallion.h	Mon Oct  7 11:25:38 2002
@@ -104,7 +104,7 @@
 #endif
 	struct termios		normaltermios;
 	struct termios		callouttermios;
-	struct tq_struct	tqueue;
+	struct work_struct	tqueue;
 	comstats_t		stats;
 	stlrq_t			tx;
 } stlport_t;
diff -Nru a/include/linux/sunrpc/debug.h b/include/linux/sunrpc/debug.h
--- a/include/linux/sunrpc/debug.h	Mon Oct  7 11:25:32 2002
+++ b/include/linux/sunrpc/debug.h	Mon Oct  7 11:25:32 2002
@@ -12,7 +12,7 @@
 #include <linux/config.h>
 
 #include <linux/timer.h>
-#include <linux/tqueue.h>
+#include <linux/workqueue.h>
 
 /*
  * Enable RPC debugging/profiling.
diff -Nru a/include/linux/sunrpc/sched.h b/include/linux/sunrpc/sched.h
--- a/include/linux/sunrpc/sched.h	Mon Oct  7 11:25:36 2002
+++ b/include/linux/sunrpc/sched.h	Mon Oct  7 11:25:36 2002
@@ -10,7 +10,6 @@
 #define _LINUX_SUNRPC_SCHED_H_
 
 #include <linux/timer.h>
-#include <linux/tqueue.h>
 #include <linux/sunrpc/types.h>
 #include <linux/wait.h>
 
diff -Nru a/include/linux/sunrpc/types.h b/include/linux/sunrpc/types.h
--- a/include/linux/sunrpc/types.h	Mon Oct  7 11:25:28 2002
+++ b/include/linux/sunrpc/types.h	Mon Oct  7 11:25:28 2002
@@ -10,7 +10,7 @@
 #define _LINUX_SUNRPC_TYPES_H_
 
 #include <linux/timer.h>
-#include <linux/tqueue.h>
+#include <linux/workqueue.h>
 #include <linux/sunrpc/debug.h>
 #include <linux/list.h>
 
diff -Nru a/include/linux/suspend.h b/include/linux/suspend.h
--- a/include/linux/suspend.h	Mon Oct  7 11:25:28 2002
+++ b/include/linux/suspend.h	Mon Oct  7 11:25:28 2002
@@ -43,7 +43,7 @@
 
 #define SUSPEND_PD_PAGES(x)     (((x)*sizeof(struct pbe))/PAGE_SIZE+1)
    
-extern struct tq_struct suspend_tq;
+extern struct work_struct suspend_tq;
 
 /* mm/vmscan.c */
 extern int shrink_mem(void);
diff -Nru a/include/linux/swap.h b/include/linux/swap.h
--- a/include/linux/swap.h	Mon Oct  7 11:25:25 2002
+++ b/include/linux/swap.h	Mon Oct  7 11:25:25 2002
@@ -218,7 +218,7 @@
 #define swap_device_lock(p)	spin_lock(&p->sdev_lock)
 #define swap_device_unlock(p)	spin_unlock(&p->sdev_lock)
 
-extern void shmem_unuse(swp_entry_t entry, struct page *page);
+extern int shmem_unuse(swp_entry_t entry, struct page *page);
 
 #endif /* __KERNEL__*/
 
diff -Nru a/include/linux/timer.h b/include/linux/timer.h
--- a/include/linux/timer.h	Mon Oct  7 11:25:26 2002
+++ b/include/linux/timer.h	Mon Oct  7 11:25:26 2002
@@ -2,70 +2,59 @@
 #define _LINUX_TIMER_H
 
 #include <linux/config.h>
-#include <linux/smp.h>
-#include <linux/stddef.h>
 #include <linux/list.h>
-#include <linux/spinlock.h>
-#include <linux/cache.h>
 
 struct tvec_t_base_s;
 
-/*
- * Timers may be dynamically created and destroyed, and should be initialized
- * by a call to init_timer() upon creation.
- *
- * The "data" field enables use of a common timeout function for several
- * timeouts. You can use this field to distinguish between the different
- * invocations.
- */
-typedef struct timer_list {
-	struct list_head list;
+struct timer_list {
+	struct list_head entry;
 	unsigned long expires;
-	unsigned long data;
+
 	void (*function)(unsigned long);
+	unsigned long data;
+
 	struct tvec_t_base_s *base;
-} timer_t;
+};
 
-extern void add_timer(timer_t * timer);
-extern int del_timer(timer_t * timer);
-  
-#ifdef CONFIG_SMP
-extern int del_timer_sync(timer_t * timer);
-extern void sync_timers(void);
-#define timer_enter(base, t) do { base->running_timer = t; mb(); } while (0)
-#define timer_exit(base) do { base->running_timer = NULL; } while (0)
-#define timer_is_running(base,t) (base->running_timer == t)
-#define timer_synchronize(base,t) while (timer_is_running(base,t)) barrier()
-#else
-#define del_timer_sync(t)	del_timer(t)
-#define sync_timers()		do { } while (0)
-#define timer_enter(base,t)          do { } while (0)
-#define timer_exit(base)            do { } while (0)
-#endif
-  
-/*
- * mod_timer is a more efficient way to update the expire field of an
- * active timer (if the timer is inactive it will be activated)
- * mod_timer(a,b) is equivalent to del_timer(a); a->expires = b; add_timer(a).
- * If the timer is known to be not pending (ie, in the handler), mod_timer
- * is less efficient than a->expires = b; add_timer(a).
+/***
+ * init_timer - initialize a timer.
+ * @timer: the timer to be initialized
+ *
+ * init_timer() must be done to a timer prior calling *any* of the
+ * other timer functions.
  */
-int mod_timer(timer_t *timer, unsigned long expires);
-
-extern void it_real_fn(unsigned long);
-
-extern void init_timers(void);
-extern void run_local_timers(void);
-
-static inline void init_timer(timer_t * timer)
+static inline void init_timer(struct timer_list * timer)
 {
-	timer->list.next = timer->list.prev = NULL;
 	timer->base = NULL;
 }
 
-static inline int timer_pending(const timer_t * timer)
+/***
+ * timer_pending - is a timer pending?
+ * @timer: the timer in question
+ *
+ * timer_pending will tell whether a given timer is currently pending,
+ * or not. Callers must ensure serialization wrt. other operations done
+ * to this timer, eg. interrupt contexts, or other CPUs on SMP.
+ *
+ * return value: 1 if the timer is pending, 0 if not.
+ */
+static inline int timer_pending(const struct timer_list * timer)
 {
-	return timer->list.next != NULL;
+	return timer->base != NULL;
 }
+
+extern void add_timer(struct timer_list * timer);
+extern int del_timer(struct timer_list * timer);
+extern int mod_timer(struct timer_list *timer, unsigned long expires);
+  
+#if CONFIG_SMP
+  extern int del_timer_sync(struct timer_list * timer);
+#else
+# define del_timer_sync(t) del_timer(t)
+#endif
+
+extern void init_timers(void);
+extern void run_local_timers(void);
+extern void it_real_fn(unsigned long);
 
 #endif
diff -Nru a/include/linux/tqueue.h b/include/linux/tqueue.h
--- a/include/linux/tqueue.h	Mon Oct  7 11:25:25 2002
+++ /dev/null	Wed Dec 31 16:00:00 1969
@@ -1,55 +0,0 @@
-/*
- * tqueue.h --- task queue handling for Linux.
- *
- * Modified version of previous incarnations of task-queues,
- * written by:
- *
- * (C) 1994 Kai Petzke, wpp@marie.physik.tu-berlin.de
- * Modified for use in the Linux kernel by Theodore Ts'o,
- * tytso@mit.edu.
- */
-
-#ifndef _LINUX_TQUEUE_H
-#define _LINUX_TQUEUE_H
-
-#include <linux/spinlock.h>
-#include <linux/list.h>
-#include <linux/bitops.h>
-#include <asm/system.h>
-
-struct tq_struct {
-	struct list_head list;		/* linked list of active tq's */
-	unsigned long sync;		/* must be initialized to zero */
-	void (*routine)(void *);	/* function to call */
-	void *data;			/* argument to function */
-};
-
-/*
- * Emit code to initialise a tq_struct's routine and data pointers
- */
-#define PREPARE_TQUEUE(_tq, _routine, _data)			\
-	do {							\
-		(_tq)->routine = _routine;			\
-		(_tq)->data = _data;				\
-	} while (0)
-
-/*
- * Emit code to initialise all of a tq_struct
- */
-#define INIT_TQUEUE(_tq, _routine, _data)			\
-	do {							\
-		INIT_LIST_HEAD(&(_tq)->list);			\
-		(_tq)->sync = 0;				\
-		PREPARE_TQUEUE((_tq), (_routine), (_data));	\
-	} while (0)
-
-#define DECLARE_TASK_QUEUE(q)	LIST_HEAD(q)
-
-/* Schedule a tq to run in process context */
-extern int schedule_task(struct tq_struct *task);
-
-/* finish all currently pending tasks - do not call from irq context */
-extern void flush_scheduled_tasks(void);
-
-#endif
-
diff -Nru a/include/linux/tty.h b/include/linux/tty.h
--- a/include/linux/tty.h	Mon Oct  7 11:25:35 2002
+++ b/include/linux/tty.h	Mon Oct  7 11:25:35 2002
@@ -20,7 +20,7 @@
 #include <linux/fs.h>
 #include <linux/major.h>
 #include <linux/termios.h>
-#include <linux/tqueue.h>
+#include <linux/workqueue.h>
 #include <linux/tty_driver.h>
 #include <linux/tty_ldisc.h>
 
@@ -138,7 +138,7 @@
 #define TTY_FLIPBUF_SIZE 512
 
 struct tty_flip_buffer {
-	struct tq_struct tqueue;
+	struct work_struct		work;
 	struct semaphore pty_sem;
 	char		*char_buf_ptr;
 	unsigned char	*flag_buf_ptr;
@@ -279,7 +279,7 @@
 	int alt_speed;		/* For magic substitution of 38400 bps */
 	wait_queue_head_t write_wait;
 	wait_queue_head_t read_wait;
-	struct tq_struct tq_hangup;
+	struct work_struct hangup_work;
 	void *disc_data;
 	void *driver_data;
 	struct list_head tty_files;
@@ -309,7 +309,7 @@
 	struct semaphore atomic_write;
 	spinlock_t read_lock;
 	/* If the tty has a pending do_SAK, queue it here - akpm */
-	struct tq_struct SAK_tq;
+	struct work_struct SAK_work;
 };
 
 /* tty magic number */
diff -Nru a/include/linux/tty_flip.h b/include/linux/tty_flip.h
--- a/include/linux/tty_flip.h	Mon Oct  7 11:25:35 2002
+++ b/include/linux/tty_flip.h	Mon Oct  7 11:25:35 2002
@@ -19,7 +19,7 @@
 
 _INLINE_ void tty_schedule_flip(struct tty_struct *tty)
 {
-	schedule_task(&tty->flip.tqueue);
+	schedule_delayed_work(&tty->flip.work, 1);
 }
 
 #undef _INLINE_
diff -Nru a/include/linux/types.h b/include/linux/types.h
--- a/include/linux/types.h	Mon Oct  7 11:25:29 2002
+++ b/include/linux/types.h	Mon Oct  7 11:25:29 2002
@@ -4,7 +4,7 @@
 #ifdef	__KERNEL__
 #include <linux/config.h>
 
-#define bitmap_member(name,bits) \
+#define DECLARE_BITMAP(name,bits) \
 	unsigned long name[((bits)+BITS_PER_LONG-1)/BITS_PER_LONG]
 #endif
 
diff -Nru a/include/linux/wanpipe.h b/include/linux/wanpipe.h
--- a/include/linux/wanpipe.h	Mon Oct  7 11:25:36 2002
+++ b/include/linux/wanpipe.h	Mon Oct  7 11:25:36 2002
@@ -320,7 +320,7 @@
 
 #include <linux/sdladrv.h>	/* SDLA support module API definitions */
 #include <linux/sdlasfm.h>	/* SDLA firmware module definitions */
-#include <linux/tqueue.h>
+#include <linux/workqueue.h>
 #ifdef LINUX_2_4
   #include <linux/serial.h>
   #include <linux/serialP.h>
@@ -389,7 +389,7 @@
 	unsigned int tty_open;
 	unsigned char *tty_buf;
 	unsigned char *tty_rx;
-	struct tq_struct tty_task_queue;
+	struct work_struct tty_work;
 	
 	union
 	{
@@ -422,7 +422,7 @@
 			u8 oob_on_modem;	/* Option to send modem status to the api */
 			u16 num_of_ch;		/* Number of channels configured by the user */
 
-			struct tq_struct x25_poll_task;
+			struct work_struct x25_poll_work;
 			struct timer_list x25_timer;
 		} x;
 		struct
@@ -545,7 +545,7 @@
 extern sdla_t * wanpipe_find_card(char *);
 extern sdla_t * wanpipe_find_card_num (int);
 
-extern void wanpipe_queue_tq (struct tq_struct *);
+extern void wanpipe_queue_work (struct work_struct *);
 extern void wanpipe_mark_bh (void);
 extern void wakeup_sk_bh (netdevice_t *);
 extern int change_dev_flags (netdevice_t *, unsigned); 
diff -Nru a/include/linux/workqueue.h b/include/linux/workqueue.h
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/include/linux/workqueue.h	Mon Oct  7 11:25:40 2002
@@ -0,0 +1,65 @@
+/*
+ * workqueue.h --- work queue handling for Linux.
+ */
+
+#ifndef _LINUX_WORKQUEUE_H
+#define _LINUX_WORKQUEUE_H
+
+#include <linux/timer.h>
+#include <linux/linkage.h>
+
+struct workqueue_struct;
+
+struct work_struct {
+	unsigned long pending;
+	struct list_head entry;
+	void (*func)(void *);
+	void *data;
+	void *wq_data;
+	struct timer_list timer;
+};
+
+#define __WORK_INITIALIZER(n, f, d) {				\
+        .entry	= { &(n).entry, &(n).entry },			\
+	.func = (f),						\
+	.data = (d) }
+
+#define DECLARE_WORK(n, f, d)					\
+	struct work_struct n = __WORK_INITIALIZER(n, f, d)
+
+/*
+ * initialize a work-struct's func and data pointers:
+ */
+#define PREPARE_WORK(_work, _func, _data)			\
+	do {							\
+		(_work)->func = _func;				\
+		(_work)->data = _data;				\
+	} while (0)
+
+/*
+ * initialize all of a work-struct:
+ */
+#define INIT_WORK(_work, _func, _data)				\
+	do {							\
+		INIT_LIST_HEAD(&(_work)->entry);		\
+		(_work)->pending = 0;				\
+		PREPARE_WORK((_work), (_func), (_data));	\
+		init_timer(&(_work)->timer);			\
+	} while (0)
+
+extern struct workqueue_struct *create_workqueue(const char *name);
+extern void destroy_workqueue(struct workqueue_struct *wq);
+
+extern int FASTCALL(queue_work(struct workqueue_struct *wq, struct work_struct *work));
+extern int FASTCALL(queue_delayed_work(struct workqueue_struct *wq, struct work_struct *work, unsigned long delay));
+extern void FASTCALL(flush_workqueue(struct workqueue_struct *wq));
+
+extern int FASTCALL(schedule_work(struct work_struct *work));
+extern int FASTCALL(schedule_delayed_work(struct work_struct *work, unsigned long delay));
+extern void flush_scheduled_work(void);
+extern int current_is_keventd(void);
+
+extern void init_workqueues(void);
+
+#endif
+
diff -Nru a/include/linux/zorro.h b/include/linux/zorro.h
--- a/include/linux/zorro.h	Mon Oct  7 11:25:25 2002
+++ b/include/linux/zorro.h	Mon Oct  7 11:25:25 2002
@@ -199,7 +199,7 @@
      *  the corresponding bits.
      */
 
-extern bitmap_member(zorro_unused_z2ram, 128);
+extern DECLARE_BITMAP(zorro_unused_z2ram, 128);
 
 #define Z2RAM_START		(0x00200000)
 #define Z2RAM_END		(0x00a00000)
diff -Nru a/include/net/bluetooth/bluetooth.h b/include/net/bluetooth/bluetooth.h
--- a/include/net/bluetooth/bluetooth.h	Mon Oct  7 11:25:33 2002
+++ b/include/net/bluetooth/bluetooth.h	Mon Oct  7 11:25:33 2002
@@ -50,6 +50,7 @@
 #define BTPROTO_HCI     1
 #define BTPROTO_SCO   	2
 #define BTPROTO_RFCOMM	3
+#define BTPROTO_BNEP    4
 
 #define SOL_HCI     0
 #define SOL_L2CAP   6
@@ -198,15 +199,5 @@
 int hci_sock_cleanup(void);
 
 int bterr(__u16 code);
-
-#ifndef MODULE_LICENSE
-#define MODULE_LICENSE(x)
-#endif
-
-#ifndef list_for_each_safe
-#define list_for_each_safe(pos, n, head) \
-	for (pos = (head)->next, n = pos->next; pos != (head); \
-		pos = n, n = pos->next)
-#endif
 
 #endif /* __BLUETOOTH_H */
diff -Nru a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h
--- a/include/net/bluetooth/hci.h	Mon Oct  7 11:25:38 2002
+++ b/include/net/bluetooth/hci.h	Mon Oct  7 11:25:38 2002
@@ -113,10 +113,10 @@
 #define ACL_PTYPE_MASK	(~SCO_PTYPE_MASK)
 
 /* ACL flags */
-#define ACL_CONT		0x0001
-#define ACL_START		0x0002
-#define ACL_ACTIVE_BCAST	0x0010
-#define ACL_PICO_BCAST		0x0020
+#define ACL_CONT		0x01
+#define ACL_START		0x02
+#define ACL_ACTIVE_BCAST	0x04
+#define ACL_PICO_BCAST		0x08
 
 /* Baseband links */
 #define SCO_LINK	0x00
@@ -542,7 +542,7 @@
 	bdaddr_t bdaddr;
 	__u8     role;
 } __attribute__ ((packed)) evt_role_change;
-#define EVT_ROLE_CHANGE_SIZE 1
+#define EVT_ROLE_CHANGE_SIZE 8
 
 #define EVT_PIN_CODE_REQ        0x16
 typedef struct {
@@ -658,29 +658,21 @@
 #define HCI_DEV_NONE	0xffff
 
 struct hci_filter {
-	__u32 type_mask;
-	__u32 event_mask[2];
-	__u16 opcode;
+	unsigned long type_mask;
+	unsigned long event_mask[2];
+	__u16   opcode;
+};
+
+struct hci_ufilter {
+	__u32   type_mask;
+	__u32   event_mask[2];
+	__u16   opcode;
 };
 
 #define HCI_FLT_TYPE_BITS	31
 #define HCI_FLT_EVENT_BITS	63
 #define HCI_FLT_OGF_BITS	63
 #define HCI_FLT_OCF_BITS	127
-
-#if BITS_PER_LONG == 64
-static inline void hci_set_bit(int nr, void *addr)
-{
-	*((__u32 *) addr + (nr >> 5)) |= ((__u32) 1 << (nr & 31));
-}
-static inline int hci_test_bit(int nr, void *addr)
-{
-	return *((__u32 *) addr + (nr >> 5)) & ((__u32) 1 << (nr & 31));
-}
-#else
-#define hci_set_bit	set_bit
-#define hci_test_bit	test_bit
-#endif
 
 /* Ioctl requests structures */
 struct hci_dev_stats {
diff -Nru a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
--- a/include/net/bluetooth/hci_core.h	Mon Oct  7 11:25:35 2002
+++ b/include/net/bluetooth/hci_core.h	Mon Oct  7 11:25:35 2002
@@ -149,7 +149,7 @@
 
 extern struct hci_proto *hci_proto[];
 extern struct list_head hdev_list;
-extern spinlock_t hdev_list_lock;
+extern rwlock_t hdev_list_lock;
 
 /* ----- Inquiry cache ----- */
 #define INQUIRY_CACHE_AGE_MAX   (HZ*30)   // 30 seconds
@@ -339,8 +339,8 @@
 /* ----- HCI protocols ----- */
 struct hci_proto {
 	char 		*name;
-	__u32		id;
-	__u32		flags;
+	unsigned int	id;
+	unsigned long	flags;
 
 	void		*priv;
 
@@ -450,11 +450,10 @@
 #define HCI_SFLT_MAX_OGF 4
 
 struct hci_sec_filter {
-	__u32 type_mask;
-	__u32 event_mask[2];
-	__u32 ocf_mask[HCI_SFLT_MAX_OGF + 1][4];
+	unsigned long type_mask;
+	unsigned long event_mask[2];
+	unsigned long ocf_mask[HCI_SFLT_MAX_OGF + 1][4];
 };
-
 
 /* ----- HCI requests ----- */
 #define HCI_REQ_DONE	  0
diff -Nru a/include/net/bluetooth/rfcomm.h b/include/net/bluetooth/rfcomm.h
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/include/net/bluetooth/rfcomm.h	Mon Oct  7 11:25:40 2002
@@ -0,0 +1,346 @@
+/* 
+   RFCOMM implementation for Linux Bluetooth stack (BlueZ).
+   Copyright (C) 2002 Maxim Krasnyansky <maxk@qualcomm.com>
+   Copyright (C) 2002 Marcel Holtmann <marcel@holtmann.org>
+
+   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;
+
+   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+   IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
+   CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES 
+   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 
+   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 
+   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+   ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, 
+   COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS 
+   SOFTWARE IS DISCLAIMED.
+*/
+
+/* 
+   RPN support    -    Dirk Husemann <hud@zurich.ibm.com>
+*/
+
+/*
+ * $Id: rfcomm.h,v 1.29 2002/10/02 20:26:17 maxk Exp $
+ */
+
+#ifndef __RFCOMM_H
+#define __RFCOMM_H
+
+#define RFCOMM_PSM 3
+
+#define RFCOMM_CONN_TIMEOUT (HZ * 30)
+#define RFCOMM_DISC_TIMEOUT (HZ * 20)
+
+#define RFCOMM_DEFAULT_MTU	127
+#define RFCOMM_DEFAULT_CREDITS	7
+
+#define RFCOMM_MAX_L2CAP_MTU	1024
+#define RFCOMM_MAX_CREDITS	40
+
+#define RFCOMM_SKB_HEAD_RESERVE	8
+#define RFCOMM_SKB_TAIL_RESERVE	2
+#define RFCOMM_SKB_RESERVE  (RFCOMM_SKB_HEAD_RESERVE + RFCOMM_SKB_TAIL_RESERVE)
+
+#define RFCOMM_SABM	0x2f
+#define RFCOMM_DISC	0x43
+#define RFCOMM_UA	0x63
+#define RFCOMM_DM	0x0f
+#define RFCOMM_UIH	0xef
+
+#define RFCOMM_TEST	0x08
+#define RFCOMM_FCON	0x28
+#define RFCOMM_FCOFF	0x18
+#define RFCOMM_MSC	0x38
+#define RFCOMM_RPN	0x24
+#define RFCOMM_RLS	0x14
+#define RFCOMM_PN	0x20
+#define RFCOMM_NSC	0x04
+
+#define RFCOMM_V24_FC	0x02
+#define RFCOMM_V24_RTC	0x04
+#define RFCOMM_V24_RTR	0x08
+#define RFCOMM_V24_IC	0x40
+#define RFCOMM_V24_DV	0x80
+
+#define RFCOMM_RPN_BR_2400	0x0
+#define RFCOMM_RPN_BR_4800	0x1
+#define RFCOMM_RPN_BR_7200	0x2
+#define RFCOMM_RPN_BR_9600	0x3
+#define RFCOMM_RPN_BR_19200	0x4
+#define RFCOMM_RPN_BR_38400	0x5
+#define RFCOMM_RPN_BR_57600	0x6
+#define RFCOMM_RPN_BR_115200	0x7
+#define RFCOMM_RPN_BR_230400	0x8
+
+#define RFCOMM_RPN_DATA_5	0x0
+#define RFCOMM_RPN_DATA_6	0x1
+#define RFCOMM_RPN_DATA_7	0x2
+#define RFCOMM_RPN_DATA_8	0x3
+
+#define RFCOMM_RPN_STOP_1	0
+#define RFCOMM_RPN_STOP_15	1
+
+#define RFCOMM_RPN_PARITY_NONE	0x0
+#define RFCOMM_RPN_PARITY_ODD	0x4
+#define RFCOMM_RPN_PARITY_EVEN	0x5
+#define RFCOMM_RPN_PARITY_MARK	0x6
+#define RFCOMM_RPN_PARITY_SPACE	0x7
+
+#define RFCOMM_RPN_FLOW_NONE	0x00
+
+#define RFCOMM_RPN_XON_CHAR	0x11
+#define RFCOMM_RPN_XOFF_CHAR	0x13
+
+#define RFCOMM_RPN_PM_BITRATE		0x0001
+#define RFCOMM_RPN_PM_DATA		0x0002
+#define RFCOMM_RPN_PM_STOP		0x0004
+#define RFCOMM_RPN_PM_PARITY		0x0008
+#define RFCOMM_RPN_PM_PARITY_TYPE	0x0010
+#define RFCOMM_RPN_PM_XON		0x0020
+#define RFCOMM_RPN_PM_XOFF		0x0040
+#define RFCOMM_RPN_PM_FLOW		0x3F00
+
+#define RFCOMM_RPN_PM_ALL		0x3F7F
+
+struct rfcomm_hdr {
+	u8 addr;
+	u8 ctrl;
+	u8 len;    // Actual size can be 2 bytes
+} __attribute__ ((packed));
+
+struct rfcomm_cmd {
+	u8 addr;
+	u8 ctrl;
+	u8 len;
+	u8 fcs;
+} __attribute__ ((packed));
+
+struct rfcomm_mcc {
+	u8 type;
+	u8 len;
+} __attribute__ ((packed));
+
+struct rfcomm_pn {
+	u8  dlci;
+	u8  flow_ctrl;
+	u8  priority;
+	u8  ack_timer;
+	u16 mtu;
+	u8  max_retrans;
+	u8  credits;
+} __attribute__ ((packed));
+
+struct rfcomm_rpn {
+	u8  dlci;
+	u8  bit_rate;
+	u8  line_settings;
+	u8  flow_ctrl;
+	u8  xon_char;
+	u8  xoff_char;
+	u16 param_mask;
+} __attribute__ ((packed));
+
+struct rfcomm_msc {
+	u8  dlci;
+	u8  v24_sig;
+} __attribute__ ((packed));
+
+/* ---- Core structures, flags etc ---- */
+
+struct rfcomm_session {
+	struct list_head list;
+	struct socket   *sock;
+	unsigned long    state;
+	unsigned long    flags;
+	atomic_t         refcnt;
+	int              initiator;
+
+	/* Default DLC parameters */
+	uint   mtu;
+	uint   credits;
+
+	struct list_head dlcs;
+};
+
+struct rfcomm_dlc {
+	struct list_head      list;
+	struct rfcomm_session *session;
+	struct sk_buff_head   tx_queue;
+	struct timer_list     timer;
+
+	spinlock_t    lock;
+	unsigned long state;
+	unsigned long flags;
+	atomic_t      refcnt;
+	u8            dlci;
+	u8            addr;
+
+	uint          mtu;
+	u8            v24_sig;
+
+	uint          credits;
+	uint          rx_credits;
+	uint          tx_credits;
+
+	void          *owner;
+
+	void (*data_ready)(struct rfcomm_dlc *d, struct sk_buff *skb);
+	void (*state_change)(struct rfcomm_dlc *d, int err);
+	void (*modem_status)(struct rfcomm_dlc *d, int v24_sig);
+};
+
+/* DLC and session flags */
+#define RFCOMM_RX_THROTTLED 0
+#define RFCOMM_TX_THROTTLED 1
+#define RFCOMM_MSC_PENDING  2
+#define RFCOMM_TIMED_OUT    3
+
+/* Scheduling flags and events */
+#define RFCOMM_SCHED_STATE  0
+#define RFCOMM_SCHED_RX     1
+#define RFCOMM_SCHED_TX     2
+#define RFCOMM_SCHED_TIMEO  3
+#define RFCOMM_SCHED_WAKEUP 31
+
+extern struct task_struct *rfcomm_thread;
+extern unsigned long rfcomm_event;
+
+static inline void rfcomm_schedule(uint event)
+{
+	if (!rfcomm_thread)
+		return;
+
+	//set_bit(event, &rfcomm_event);
+	if (!test_and_set_bit(RFCOMM_SCHED_WAKEUP, &rfcomm_event))
+		wake_up_process(rfcomm_thread);
+}
+
+extern struct semaphore rfcomm_sem;
+#define rfcomm_lock()	down(&rfcomm_sem);
+#define rfcomm_unlock()	up(&rfcomm_sem);
+
+/* ---- RFCOMM DLCs (channels) ---- */
+struct rfcomm_dlc *rfcomm_dlc_alloc(int prio);
+void rfcomm_dlc_free(struct rfcomm_dlc *d);
+int  rfcomm_dlc_open(struct rfcomm_dlc *d, bdaddr_t *src, bdaddr_t *dst, u8 channel);
+int  rfcomm_dlc_close(struct rfcomm_dlc *d, int reason);
+int  rfcomm_dlc_send(struct rfcomm_dlc *d, struct sk_buff *skb);
+int  rfcomm_dlc_modem_status(struct rfcomm_dlc *d, int v24_sig);
+
+#define rfcomm_dlc_lock(d)     spin_lock(&d->lock)
+#define rfcomm_dlc_unlock(d)   spin_unlock(&d->lock)
+
+static inline void rfcomm_dlc_hold(struct rfcomm_dlc *d)
+{
+	atomic_inc(&d->refcnt);
+}
+
+static inline void rfcomm_dlc_put(struct rfcomm_dlc *d)
+{
+	if (atomic_dec_and_test(&d->refcnt))
+		rfcomm_dlc_free(d);
+}
+
+extern void FASTCALL(__rfcomm_dlc_throttle(struct rfcomm_dlc *d));
+extern void FASTCALL(__rfcomm_dlc_unthrottle(struct rfcomm_dlc *d));
+
+static inline void rfcomm_dlc_throttle(struct rfcomm_dlc *d)
+{
+	if (!test_and_set_bit(RFCOMM_RX_THROTTLED, &d->flags))
+		__rfcomm_dlc_throttle(d);
+}
+
+static inline void rfcomm_dlc_unthrottle(struct rfcomm_dlc *d)
+{
+	if (test_and_clear_bit(RFCOMM_RX_THROTTLED, &d->flags))
+		__rfcomm_dlc_unthrottle(d);
+}
+
+/* ---- RFCOMM sessions ---- */
+struct rfcomm_session *rfcomm_session_add(struct socket *sock, int state);
+struct rfcomm_session *rfcomm_session_get(bdaddr_t *src, bdaddr_t *dst);
+struct rfcomm_session *rfcomm_session_create(bdaddr_t *src, bdaddr_t *dst, int *err);
+void   rfcomm_session_del(struct rfcomm_session *s);
+void   rfcomm_session_close(struct rfcomm_session *s, int err);
+
+static inline void rfcomm_session_hold(struct rfcomm_session *s)
+{
+	atomic_inc(&s->refcnt);
+}
+
+static inline void rfcomm_session_put(struct rfcomm_session *s)
+{
+	if (atomic_dec_and_test(&s->refcnt))
+		rfcomm_session_del(s);
+}
+
+/* ---- RFCOMM chechsum ---- */
+extern u8 rfcomm_crc_table[];
+
+/* ---- RFCOMM sockets ---- */
+struct sockaddr_rc {
+	sa_family_t rc_family;
+	bdaddr_t    rc_bdaddr;
+	u8          rc_channel;
+};
+
+#define rfcomm_pi(sk)   ((struct rfcomm_pinfo *) &sk->protinfo)
+
+struct rfcomm_pinfo {
+	struct rfcomm_dlc   *dlc;
+	u8     channel;
+};
+
+int  rfcomm_init_sockets(void);
+void rfcomm_cleanup_sockets(void);
+
+int  rfcomm_connect_ind(struct rfcomm_session *s, u8 channel, struct rfcomm_dlc **d);
+
+/* ---- RFCOMM TTY ---- */
+#define RFCOMM_MAX_DEV  256
+
+#define RFCOMMCREATEDEV		_IOW('R', 200, int)
+#define RFCOMMRELEASEDEV	_IOW('R', 201, int)
+#define RFCOMMGETDEVLIST	_IOR('R', 210, int)
+#define RFCOMMGETDEVINFO	_IOR('R', 211, int)
+#define RFCOMMSTEALDLC		_IOW('R', 220, int)
+
+#define RFCOMM_REUSE_DLC      0
+#define RFCOMM_RELEASE_ONHUP  1
+#define RFCOMM_HANGUP_NOW     2
+#define RFCOMM_TTY_ATTACHED   3
+
+struct rfcomm_dev_req {
+	s16      dev_id;
+	u32      flags;
+	bdaddr_t src;
+	bdaddr_t dst;
+	u8       channel;
+	
+};
+
+struct rfcomm_dev_info {
+	s16      id;
+	u32      flags;
+	u16      state;
+	bdaddr_t src;
+	bdaddr_t dst;
+	u8       channel;
+};
+
+struct rfcomm_dev_list_req {
+	u16      dev_num;
+	struct   rfcomm_dev_info dev_info[0];
+};
+
+int  rfcomm_dev_ioctl(struct sock *sk, unsigned int cmd, unsigned long arg);
+int  rfcomm_init_ttys(void);
+void rfcomm_cleanup_ttys(void);
+
+#endif /* __RFCOMM_H */
diff -Nru a/include/net/ipx.h b/include/net/ipx.h
--- a/include/net/ipx.h	Mon Oct  7 11:25:25 2002
+++ b/include/net/ipx.h	Mon Oct  7 11:25:25 2002
@@ -1,3 +1,5 @@
+#ifndef _NET_INET_IPX_H_
+#define _NET_INET_IPX_H_
 /*
  *	The following information is in its entirety obtained from:
  *
@@ -7,9 +9,6 @@
  *	Which is available from ftp.novell.com
  */
 
-#ifndef _NET_INET_IPX_H_
-#define _NET_INET_IPX_H_
-
 #include <linux/netdevice.h>
 #include <net/datalink.h>
 #include <linux/ipx.h>
@@ -25,8 +24,7 @@
 
 #define IPX_MAX_PPROP_HOPS 8
 
-struct ipxhdr
-{
+struct ipxhdr {
 	__u16           ipx_checksum __attribute__ ((packed));
 #define IPX_NO_CHECKSUM	0xFFFF
 	__u16           ipx_pktsize __attribute__ ((packed));
@@ -110,4 +108,17 @@
 #define IPX_MIN_EPHEMERAL_SOCKET	0x4000
 #define IPX_MAX_EPHEMERAL_SOCKET	0x7fff
 
+extern struct ipx_route *ipx_routes;
+extern rwlock_t ipx_routes_lock;
+
+extern struct ipx_interface *ipx_interfaces;
+extern spinlock_t ipx_interfaces_lock;
+
+extern struct ipx_interface *ipx_primary_net;
+
+extern int ipx_proc_init(void);
+extern void ipx_proc_exit(void);
+
+extern const char *ipx_frame_name(unsigned short);
+extern const char *ipx_device_name(struct ipx_interface *intrfc);
 #endif /* def _NET_INET_IPX_H_ */
diff -Nru a/include/net/irda/ircomm_tty.h b/include/net/irda/ircomm_tty.h
--- a/include/net/irda/ircomm_tty.h	Mon Oct  7 11:25:32 2002
+++ b/include/net/irda/ircomm_tty.h	Mon Oct  7 11:25:32 2002
@@ -95,7 +95,7 @@
 	wait_queue_head_t open_wait;
 	wait_queue_head_t close_wait;
 	struct timer_list watchdog_timer;
-	struct tq_struct  tqueue;
+	struct work_struct  tqueue;
 
         unsigned short    close_delay;
         unsigned short    closing_wait; /* time to wait before closing */
diff -Nru a/include/net/llc_conn.h b/include/net/llc_conn.h
--- a/include/net/llc_conn.h	Mon Oct  7 11:25:37 2002
+++ b/include/net/llc_conn.h	Mon Oct  7 11:25:37 2002
@@ -79,7 +79,6 @@
 extern int llc_conn_state_process(struct sock *sk, struct sk_buff *skb);
 extern void llc_conn_send_pdu(struct sock *sk, struct sk_buff *skb);
 extern void llc_conn_rtn_pdu(struct sock *sk, struct sk_buff *skb);
-extern void llc_conn_free_ev(struct sk_buff *skb);
 extern void llc_conn_resend_i_pdu_as_cmd(struct sock *sk, u8 nr,
 					 u8 first_p_bit);
 extern void llc_conn_resend_i_pdu_as_rsp(struct sock *sk, u8 nr,
diff -Nru a/include/net/llc_mac.h b/include/net/llc_mac.h
--- a/include/net/llc_mac.h	Mon Oct  7 11:25:37 2002
+++ b/include/net/llc_mac.h	Mon Oct  7 11:25:37 2002
@@ -13,8 +13,6 @@
  */
 extern int llc_rcv(struct sk_buff *skb, struct net_device *dev,
 		   struct packet_type *pt);
-extern struct net_device *mac_dev_peer(struct net_device *current_dev,
-				       int type, u8 *mac);
 extern u16 lan_hdrs_init(struct sk_buff *skb, u8 *sa, u8 *da);
 extern int llc_conn_rcv(struct sock *sk, struct sk_buff *skb);
 
diff -Nru a/include/net/llc_main.h b/include/net/llc_main.h
--- a/include/net/llc_main.h	Mon Oct  7 11:25:32 2002
+++ b/include/net/llc_main.h	Mon Oct  7 11:25:32 2002
@@ -57,10 +57,11 @@
 extern void llc_sap_save(struct llc_sap *sap);
 extern void llc_free_sap(struct llc_sap *sap);
 extern struct llc_sap *llc_sap_find(u8 lsap);
-extern struct llc_station *llc_station_get(void);
 extern void llc_station_state_process(struct llc_station *station,
 				      struct sk_buff *skb);
 extern void llc_station_send_pdu(struct llc_station *station,
 				 struct sk_buff *skb);
 extern struct sk_buff *llc_alloc_frame(void);
+
+extern struct llc_station llc_main_station;
 #endif /* LLC_MAIN_H */
diff -Nru a/include/net/llc_proc.h b/include/net/llc_proc.h
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/include/net/llc_proc.h	Mon Oct  7 11:25:41 2002
@@ -0,0 +1,18 @@
+#ifndef LLC_PROC_H
+#define LLC_PROC_H
+/*
+ * Copyright (c) 1997 by Procom Technology, Inc.
+ * 		 2002 by Arnaldo Carvalho de Melo <acme@conectiva.com.br>
+ *
+ * This program can be redistributed or modified under the terms of the
+ * GNU General Public License as published by the Free Software Foundation.
+ * This program is distributed without any warranty or implied warranty
+ * of merchantability or fitness for a particular purpose.
+ *
+ * See the GNU General Public License for more details.
+ */
+
+extern int llc_proc_init(void);
+extern void llc_proc_exit(void);
+
+#endif /* LLC_PROC_H */
diff -Nru a/include/net/sctp/sctp.h b/include/net/sctp/sctp.h
--- a/include/net/sctp/sctp.h	Mon Oct  7 11:25:28 2002
+++ b/include/net/sctp/sctp.h	Mon Oct  7 11:25:28 2002
@@ -103,6 +103,14 @@
 #define SCTP_PROTOSW_FLAG INET_PROTOSW_PERMANENT
 #endif
 
+
+/* Certain internal static functions need to be exported when 
+ * compiled into the test frame.
+ */
+#ifndef SCTP_STATIC
+#define SCTP_STATIC static
+#endif
+
 /* 
  * Function declarations. 
  */
diff -Nru a/include/net/sctp/sm.h b/include/net/sctp/sm.h
--- a/include/net/sctp/sm.h	Mon Oct  7 11:25:34 2002
+++ b/include/net/sctp/sm.h	Mon Oct  7 11:25:34 2002
@@ -215,7 +215,8 @@
 			     int priority);
 sctp_chunk_t *sctp_make_init_ack(const sctp_association_t *,
 				 const sctp_chunk_t *,
-				 const int priority);
+				 const int priority,
+				 const int unkparam_len);
 sctp_chunk_t *sctp_make_cookie_echo(const sctp_association_t *,
 				    const sctp_chunk_t *);
 sctp_chunk_t *sctp_make_cookie_ack(const sctp_association_t *,
@@ -304,6 +305,14 @@
 void sctp_generate_heartbeat_event(unsigned long peer);
 
 sctp_sackhdr_t *sctp_sm_pull_sack(sctp_chunk_t *);
+sctp_packet_t *sctp_abort_pkt_new(const sctp_endpoint_t *ep,
+				  const sctp_association_t *asoc,
+				  sctp_chunk_t *chunk,
+				  const void *payload,
+				  size_t paylen);
+sctp_packet_t *sctp_ootb_pkt_new(const sctp_association_t *asoc,
+				 const sctp_chunk_t *chunk);
+void sctp_ootb_pkt_free(sctp_packet_t *packet);
 
 sctp_cookie_param_t *
 sctp_pack_cookie(const sctp_endpoint_t *, const sctp_association_t *,
diff -Nru a/include/net/sctp/structs.h b/include/net/sctp/structs.h
--- a/include/net/sctp/structs.h	Mon Oct  7 11:25:35 2002
+++ b/include/net/sctp/structs.h	Mon Oct  7 11:25:35 2002
@@ -57,7 +57,7 @@
 #include <asm/param.h>		/* We get MAXHOSTNAMELEN.     */
 #include <asm/atomic.h>		/* This gets us atomic counters.  */
 #include <linux/skbuff.h>	/* We need sk_buff_head. */
-#include <linux/tqueue.h>	/* We need tq_struct.    */
+#include <linux/workqueue.h>	/* We need tq_struct.    */
 #include <linux/sctp.h>         /* We need sctp* header structs.  */
 
 /*
@@ -761,7 +761,7 @@
 	/* This is the delayed task to finish delivering inbound
 	 * messages.
 	 */
-	struct tq_struct immediate;
+	struct work_struct immediate;
 
 	int malloced;        /* Is this structure kfree()able?  */
 };
@@ -1044,6 +1044,20 @@
 sctp_endpoint_t *sctp_endpoint_is_match(sctp_endpoint_t *,
 					const sockaddr_storage_t *);
 
+int sctp_verify_init(const sctp_association_t *asoc,
+		     sctp_cid_t cid,
+		     sctp_init_chunk_t *peer_init,
+		     sctp_chunk_t *chunk,
+		     sctp_chunk_t **err_chunk);
+int sctp_verify_param(const sctp_association_t *asoc,
+		      sctpParam_t param,
+		      sctp_cid_t cid,
+		      sctp_chunk_t *chunk,
+		      sctp_chunk_t **err_chunk);
+int sctp_process_unk_param(const sctp_association_t *asoc,
+			   sctpParam_t param,
+			   sctp_chunk_t *chunk,
+			   sctp_chunk_t **err_chunk);
 void sctp_process_init(sctp_association_t *asoc, sctp_cid_t cid,
 		       const sockaddr_storage_t *peer_addr,
 		       sctp_init_chunk_t  *peer_init, int priority);
diff -Nru a/include/net/x25.h b/include/net/x25.h
--- a/include/net/x25.h	Mon Oct  7 11:25:36 2002
+++ b/include/net/x25.h	Mon Oct  7 11:25:36 2002
@@ -220,9 +220,8 @@
 /* x25_route.c */
 extern struct x25_route *x25_get_route(struct x25_address *addr);
 extern struct net_device *x25_dev_get(char *);
-extern void x25_route_device_down(struct net_device *);
+extern void x25_route_device_down(struct net_device *dev);
 extern int  x25_route_ioctl(unsigned int, void *);
-extern int  x25_routes_get_info(char *, char **, off_t, int);
 extern void x25_route_free(void);
 
 static __inline__ void x25_route_hold(struct x25_route *rt)
@@ -263,4 +262,12 @@
 	unsigned flags;
 };
 #define X25_SKB_CB(s) ((struct x25_skb_cb *) ((s)->cb))
+
+extern struct sock *x25_list;
+extern rwlock_t x25_list_lock;
+extern struct list_head x25_route_list;
+extern rwlock_t x25_route_list_lock;
+
+extern int x25_proc_init(void);
+extern void x25_proc_exit(void);
 #endif
diff -Nru a/include/sound/ac97_codec.h b/include/sound/ac97_codec.h
--- a/include/sound/ac97_codec.h	Mon Oct  7 11:25:35 2002
+++ b/include/sound/ac97_codec.h	Mon Oct  7 11:25:35 2002
@@ -25,6 +25,7 @@
  *
  */
 
+#include <linux/bitops.h>
 #include "control.h"
 #include "info.h"
 
@@ -54,39 +55,87 @@
 #define AC97_POWERDOWN		0x26	/* Powerdown control / status */
 /* range 0x28-0x3a - AUDIO AC'97 2.0 extensions */
 #define AC97_EXTENDED_ID	0x28	/* Extended Audio ID */
-#define AC97_EXTENDED_STATUS	0x2a	/* Extended Audio Status */
+#define AC97_EXTENDED_STATUS	0x2a	/* Extended Audio Status and Control */
 #define AC97_PCM_FRONT_DAC_RATE 0x2c	/* PCM Front DAC Rate */
 #define AC97_PCM_SURR_DAC_RATE	0x2e	/* PCM Surround DAC Rate */
 #define AC97_PCM_LFE_DAC_RATE	0x30	/* PCM LFE DAC Rate */
-#define AC97_PCM_LR_ADC_RATE	0x32	/* PCM LR DAC Rate */
+#define AC97_PCM_LR_ADC_RATE	0x32	/* PCM LR ADC Rate */
 #define AC97_PCM_MIC_ADC_RATE	0x34	/* PCM MIC ADC Rate */
 #define AC97_CENTER_LFE_MASTER	0x36	/* Center + LFE Master Volume */
 #define AC97_SURROUND_MASTER	0x38	/* Surround (Rear) Master Volume */
 #define AC97_SPDIF		0x3a	/* S/PDIF control */
 /* range 0x3c-0x58 - MODEM */
+#define AC97_EXTENDED_MID	0x3c	/* Extended Modem ID */
+#define AC97_EXTENDED_MSTATUS	0x3e	/* Extended Modem Status and Control */
+#define AC97_LINE1_RATE		0x40	/* Line1 DAC/ADC Rate */
+#define AC97_LINE2_RATE		0x42	/* Line2 DAC/ADC Rate */
+#define AC97_HANDSET_RATE	0x44	/* Handset DAC/ADC Rate */
+#define AC97_LINE1_LEVEL	0x46	/* Line1 DAC/ADC Level */
+#define AC97_LINE2_LEVEL	0x48	/* Line2 DAC/ADC Level */
+#define AC97_HANDSET_LEVEL	0x4a	/* Handset DAC/ADC Level */
+#define AC97_GPIO_CFG		0x4c	/* GPIO Configuration */
+#define AC97_GPIO_POLARITY	0x4e	/* GPIO Pin Polarity/Type, 0=low, 1=high active */
+#define AC97_GPIO_STICKY	0x50	/* GPIO Pin Sticky, 0=not, 1=sticky */
+#define AC97_GPIO_WAKEUP	0x52	/* GPIO Pin Wakeup, 0=no int, 1=yes int */
+#define AC97_GPIO_STATUS	0x54	/* GPIO Pin Status, slot 12 */
+#define AC97_MISC_AFE		0x56	/* Miscellaneous Modem AFE Status and Control */
 /* range 0x5a-0x7b - Vendor Specific */
 #define AC97_VENDOR_ID1		0x7c	/* Vendor ID1 */
 #define AC97_VENDOR_ID2		0x7e	/* Vendor ID2 / revision */
 
+/* basic capabilities (reset register) */
+#define AC97_BC_DEDICATED_MIC	0x0001	/* Dedicated Mic PCM In Channel */
+#define AC97_BC_RESERVED1	0x0002	/* Reserved (was Modem Line Codec support) */
+#define AC97_BC_BASS_TREBLE	0x0004	/* Bass & Treble Control */
+#define AC97_BC_SIM_STEREO	0x0008	/* Simulated stereo */
+#define AC97_BC_HEADPHONE	0x0010	/* Headphone Out Support */
+#define AC97_BC_LOUDNESS	0x0020	/* Loudness (bass boost) Supporqt */
+#define AC97_BC_16BIT_DAC	0x0000	/* 16-bit DAC resolution */
+#define AC97_BC_18BIT_DAC	0x0040	/* 18-bit DAC resolution */
+#define AC97_BC_20BIT_DAC	0x0080	/* 20-bit DAC resolution */
+#define AC97_BC_DAC_MASK	0x00c0
+#define AC97_BC_16BIT_ADC	0x0000	/* 16-bit ADC resolution */
+#define AC97_BC_18BIT_ADC	0x0100	/* 18-bit ADC resolution */
+#define AC97_BC_20BIT_ADC	0x0200	/* 20-bit ADC resolution */
+#define AC97_BC_ADC_MASK	0x0300
+
+/* extended audio ID bit defines */
+#define AC97_EI_VRA		0x0001	/* Variable bit rate supported */
+#define AC97_EI_DRA		0x0002	/* Double rate supported */
+#define AC97_EI_SPDIF		0x0004	/* S/PDIF out supported */
+#define AC97_EI_VRM		0x0008	/* Variable bit rate supported for MIC */
+#define AC97_EI_DACS_SLOT_MASK	0x0030	/* DACs slot assignment */
+#define AC97_EI_DACS_SLOT_SHIFT	4
+#define AC97_EI_CDAC		0x0040	/* PCM Center DAC available */
+#define AC97_EI_SDAC		0x0080	/* PCM Surround DACs available */
+#define AC97_EI_LDAC		0x0100	/* PCM LFE DAC available */
+#define AC97_EI_AMAP		0x0200	/* indicates optional slot/DAC mapping based on codec ID */
+#define AC97_EI_REV_MASK	0x0c00	/* AC'97 revision mask */
+#define AC97_EI_REV_22		0x0400	/* AC'97 revision 2.2 */
+#define AC97_EI_REV_SHIFT	10
+#define AC97_EI_ADDR_MASK	0xc000	/* physical codec ID (address) */
+#define AC97_EI_ADDR_SHIFT	14
+
 /* extended audio status and control bit defines */
 #define AC97_EA_VRA		0x0001	/* Variable bit rate enable bit */
 #define AC97_EA_DRA		0x0002	/* Double-rate audio enable bit */
-#define AC97_EA_SPDIF		0x0004	/* S/PDIF Enable bit */
+#define AC97_EA_SPDIF		0x0004	/* S/PDIF out enable bit */
 #define AC97_EA_VRM		0x0008	/* Variable bit rate for MIC enable bit */
+#define AC97_EA_SPSA_SLOT_MASK	0x0030	/* Mask for slot assignment bits */
+#define AC97_EA_SPSA_SLOT_SHIFT 4
+#define AC97_EA_SPSA_3_4	0x0000	/* Slot assigned to 3 & 4 */
+#define AC97_EA_SPSA_7_8	0x0010	/* Slot assigned to 7 & 8 */
+#define AC97_EA_SPSA_6_9	0x0020	/* Slot assigned to 6 & 9 */
+#define AC97_EA_SPSA_10_11	0x0030	/* Slot assigned to 10 & 11 */
 #define AC97_EA_CDAC		0x0040	/* PCM Center DAC is ready (Read only) */
-#define AC97_EA_SDAC		0x0040	/* PCM Surround DACs are ready (Read only) */
-#define AC97_EA_LDAC		0x0080	/* PCM LFE DAC is ready (Read only) */
-#define AC97_EA_MDAC		0x0100	/* MIC ADC is ready (Read only) */
+#define AC97_EA_SDAC		0x0080	/* PCM Surround DACs are ready (Read only) */
+#define AC97_EA_LDAC		0x0100	/* PCM LFE DAC is ready (Read only) */
+#define AC97_EA_MDAC		0x0200	/* MIC ADC is ready (Read only) */
 #define AC97_EA_SPCV		0x0400	/* S/PDIF configuration valid (Read only) */
 #define AC97_EA_PRI		0x0800	/* Turns the PCM Center DAC off */
 #define AC97_EA_PRJ		0x1000	/* Turns the PCM Surround DACs off */
 #define AC97_EA_PRK		0x2000	/* Turns the PCM LFE DAC off */
 #define AC97_EA_PRL		0x4000	/* Turns the MIC ADC off */
-#define AC97_EA_SLOT_MASK	0xffcf	/* Mask for slot assignment bits */
-#define AC97_EA_SPSA_3_4	0x0000	/* Slot assigned to 3 & 4 */
-#define AC97_EA_SPSA_7_8	0x0010	/* Slot assigned to 7 & 8 */
-#define AC97_EA_SPSA_6_9	0x0020	/* Slot assigned to 6 & 9 */
-#define AC97_EA_SPSA_10_11	0x0030	/* Slot assigned to 10 & 11 */
 
 /* S/PDIF control bit defines */
 #define AC97_SC_PRO		0x0001	/* Professional status */
@@ -94,14 +143,25 @@
 #define AC97_SC_COPY		0x0004	/* Copyright status */
 #define AC97_SC_PRE		0x0008	/* Preemphasis status */
 #define AC97_SC_CC_MASK		0x07f0	/* Category Code mask */
+#define AC97_SC_CC_SHIFT	4
 #define AC97_SC_L		0x0800	/* Generation Level status */
-#define AC97_SC_SPSR_MASK	0xcfff	/* S/PDIF Sample Rate bits */
+#define AC97_SC_SPSR_MASK	0x3000	/* S/PDIF Sample Rate bits */
+#define AC97_SC_SPSR_SHIFT	12
 #define AC97_SC_SPSR_44K	0x0000	/* Use 44.1kHz Sample rate */
 #define AC97_SC_SPSR_48K	0x2000	/* Use 48kHz Sample rate */
 #define AC97_SC_SPSR_32K	0x3000	/* Use 32kHz Sample rate */
 #define AC97_SC_DRS		0x4000	/* Double Rate S/PDIF */
 #define AC97_SC_V		0x8000	/* Validity status */
 
+/* extended modem ID bit defines */
+#define AC97_MEI_LINE1		0x0001	/* Line1 present */
+#define AC97_MEI_LINE2		0x0002	/* Line2 present */
+#define AC97_MEI_HEADSET	0x0004	/* Headset present */
+#define AC97_MEI_CID1		0x0008	/* caller ID decode for Line1 is supported */
+#define AC97_MEI_CID2		0x0010	/* caller ID decode for Line2 is supported */
+#define AC97_MEI_ADDR_MASK	0xc000	/* physical codec ID (address) */
+#define AC97_MEI_ADDR_SHIFT	14
+
 /* specific - SigmaTel */
 #define AC97_SIGMATEL_ANALOG	0x6c	/* Analog Special */
 #define AC97_SIGMATEL_DAC2INVERT 0x6e
@@ -136,8 +196,10 @@
 #define AC97_CXR_SPDIF_AC3	0x2
 
 /* ac97->scaps */
-#define AC97_SCAP_SURROUND_DAC	(1<<0)	/* surround L&R DACs are present */
-#define AC97_SCAP_CENTER_LFE_DAC (1<<1)	/* center and LFE DACs are present */
+#define AC97_SCAP_AUDIO		(1<<0)	/* audio AC'97 codec */
+#define AC97_SCAP_MODEM		(1<<1)	/* modem AC'97 codec */
+#define AC97_SCAP_SURROUND_DAC	(1<<2)	/* surround L&R DACs are present */
+#define AC97_SCAP_CENTER_LFE_DAC (1<<3)	/* center and LFE DACs are present */
 
 /* ac97->flags */
 #define AC97_HAS_PC_BEEP	(1<<0)	/* force PC Speaker usage */
@@ -145,8 +207,16 @@
 #define AC97_CS_SPDIF		(1<<2)	/* Cirrus Logic uses funky SPDIF */
 #define AC97_CX_SPDIF		(1<<3)	/* Conexant's spdif interface */
 
-/*
+/* rates indexes */
+#define AC97_RATES_FRONT_DAC	0
+#define AC97_RATES_SURR_DAC	1
+#define AC97_RATES_LFE_DAC	2
+#define AC97_RATES_ADC		3
+#define AC97_RATES_MIC_ADC	4
+#define AC97_RATES_SPDIF	5
 
+/*
+ *
  */
 
 typedef struct _snd_ac97 ac97_t;
@@ -169,18 +239,15 @@
 	unsigned int id;	/* identification of codec */
 	unsigned short caps;	/* capabilities (register 0) */
 	unsigned short ext_id;	/* extended feature identification (register 28) */
+	unsigned short ext_mid;	/* extended modem ID (register 3C) */
 	unsigned int scaps;	/* driver capabilities */
 	unsigned int flags;	/* specific code */
 	unsigned int clock;	/* AC'97 clock (usually 48000Hz) */
-	unsigned int rates_front_dac;
-	unsigned int rates_surr_dac;
-	unsigned int rates_lfe_dac;
-	unsigned int rates_adc;
-	unsigned int rates_mic_adc;
+	unsigned int rates[6];	/* see AC97_RATES_* defines */
 	unsigned int spdif_status;
 	unsigned short regs[0x80]; /* register cache */
 	unsigned int limited_regs; /* allow limited registers only */
-	bitmap_member(reg_accessed,0x80); /* bit flags */
+	DECLARE_BITMAP(reg_accessed, 0x80); /* bit flags */
 	union {			/* vendor specific code */
 		struct {
 			unsigned short unchained[3];	// 0 = C34, 1 = C79, 2 = C69
@@ -192,6 +259,17 @@
 	} spec;
 };
 
+/* conditions */
+static inline int ac97_is_rev22(ac97_t * ac97)
+{
+	return (ac97->ext_id & AC97_EI_REV_MASK) == AC97_EI_REV_22;
+}
+static inline int ac97_can_amap(ac97_t * ac97)
+{
+	return (ac97->ext_id & AC97_EI_AMAP) != 0;
+}
+
+/* functions */
 int snd_ac97_mixer(snd_card_t * card, ac97_t * _ac97, ac97_t ** rac97);
 
 void snd_ac97_write(ac97_t *ac97, unsigned short reg, unsigned short value);
diff -Nru a/include/sound/ad1848.h b/include/sound/ad1848.h
--- a/include/sound/ad1848.h	Mon Oct  7 11:25:29 2002
+++ b/include/sound/ad1848.h	Mon Oct  7 11:25:29 2002
@@ -166,20 +166,20 @@
 void snd_ad1848_interrupt(int irq, void *dev_id, struct pt_regs *regs);
 
 #define AD1848_SINGLE(xname, xindex, reg, shift, mask, invert) \
-{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \
-  info: snd_ad1848_info_single, \
-  get: snd_ad1848_get_single, put: snd_ad1848_put_single, \
-  private_value: reg | (shift << 8) | (mask << 16) | (invert << 24) }
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
+  .info = snd_ad1848_info_single, \
+  .get = snd_ad1848_get_single, .put = snd_ad1848_put_single, \
+  .private_value = reg | (shift << 8) | (mask << 16) | (invert << 24) }
 
 int snd_ad1848_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo);
 int snd_ad1848_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol);
 int snd_ad1848_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol);
 
 #define AD1848_DOUBLE(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \
-{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \
-  info: snd_ad1848_info_double, \
-  get: snd_ad1848_get_double, put: snd_ad1848_put_double, \
-  private_value: left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) }
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
+  .info = snd_ad1848_info_double, \
+  .get = snd_ad1848_get_double, .put = snd_ad1848_put_double, \
+  .private_value = left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) }
 
 int snd_ad1848_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo);
 int snd_ad1848_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol);
diff -Nru a/include/sound/asound.h b/include/sound/asound.h
--- a/include/sound/asound.h	Mon Oct  7 11:25:36 2002
+++ b/include/sound/asound.h	Mon Oct  7 11:25:36 2002
@@ -103,9 +103,10 @@
 	SNDRV_HWDEP_IFACE_EMU10K1,	/* FX8010 processor in EMU10K1 chip */
 	SNDRV_HWDEP_IFACE_YSS225,	/* Yamaha FX processor */
 	SNDRV_HWDEP_IFACE_ICS2115,	/* Wavetable synth */
+	SNDRV_HWDEP_IFACE_SSCAPE,	/* Ensoniq SoundScape ISA card (MC68EC000) */
 
 	/* Don't forget to change the following: */
-	SNDRV_HWDEP_IFACE_LAST = SNDRV_HWDEP_IFACE_ICS2115,
+	SNDRV_HWDEP_IFACE_LAST = SNDRV_HWDEP_IFACE_SSCAPE,
 };
 
 struct sndrv_hwdep_info {
diff -Nru a/include/sound/cs4231.h b/include/sound/cs4231.h
--- a/include/sound/cs4231.h	Mon Oct  7 11:25:35 2002
+++ b/include/sound/cs4231.h	Mon Oct  7 11:25:35 2002
@@ -54,22 +54,28 @@
 #define CS4231_PLY_UPR_CNT	0x0e	/* playback upper base count */
 #define CS4231_PLY_LWR_CNT	0x0f	/* playback lower base count */
 #define CS4231_ALT_FEATURE_1	0x10	/* alternate #1 feature enable */
+#define AD1845_AF1_MIC_LEFT	0x10	/* alternate #1 feature + MIC left */
 #define CS4231_ALT_FEATURE_2	0x11	/* alternate #2 feature enable */
+#define AD1845_AF2_MIC_RIGHT	0x11	/* alternate #2 feature + MIC right */
 #define CS4231_LEFT_LINE_IN	0x12	/* left line input control */
 #define CS4231_RIGHT_LINE_IN	0x13	/* right line input control */
 #define CS4231_TIMER_LOW	0x14	/* timer low byte */
 #define CS4231_TIMER_HIGH	0x15	/* timer high byte */
 #define CS4231_LEFT_MIC_INPUT	0x16	/* left MIC input control register (InterWave only) */
+#define AD1845_UPR_FREQ_SEL	0x16	/* upper byte of frequency select */
 #define CS4231_RIGHT_MIC_INPUT	0x17	/* right MIC input control register (InterWave only) */
+#define AD1845_LWR_FREQ_SEL	0x17	/* lower byte of frequency select */
 #define CS4236_EXT_REG		0x17	/* extended register access */
 #define CS4231_IRQ_STATUS	0x18	/* irq status register */
 #define CS4231_LINE_LEFT_OUTPUT	0x19	/* left line output control register (InterWave only) */
 #define CS4231_VERSION		0x19	/* CS4231(A) - version values */
 #define CS4231_MONO_CTRL	0x1a	/* mono input/output control */
 #define CS4231_LINE_RIGHT_OUTPUT 0x1b	/* right line output control register (InterWave only) */
+#define AD1845_PWR_DOWN		0x1b	/* power down control */
 #define CS4235_LEFT_MASTER	0x1b	/* left master output control */
 #define CS4231_REC_FORMAT	0x1c	/* clock and data format - record - bits 7-0 MCE */
 #define CS4231_PLY_VAR_FREQ	0x1d	/* playback variable frequency */
+#define AD1845_CLOCK		0x1d	/* crystal clock select and total power down */
 #define CS4235_RIGHT_MASTER	0x1d	/* right master output control */
 #define CS4231_REC_UPR_CNT	0x1e	/* record upper count */
 #define CS4231_REC_LWR_CNT	0x1f	/* record lower count */
@@ -191,6 +197,7 @@
 #define CS4231_HW_CS4231_MASK   0x0100	/* CS4231 serie */
 #define CS4231_HW_CS4231        0x0100	/* CS4231 chip */
 #define CS4231_HW_CS4231A       0x0101	/* CS4231A chip */
+#define CS4231_HW_AD1845	0x0102	/* AD1845 chip */
 #define CS4231_HW_CS4232_MASK   0x0200	/* CS4232 serie (has control ports) */
 #define CS4231_HW_CS4232        0x0200	/* CS4232 */
 #define CS4231_HW_CS4232A       0x0201	/* CS4232A */
@@ -298,20 +305,20 @@
  */
 
 #define CS4231_SINGLE(xname, xindex, reg, shift, mask, invert) \
-{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \
-  info: snd_cs4231_info_single, \
-  get: snd_cs4231_get_single, put: snd_cs4231_put_single, \
-  private_value: reg | (shift << 8) | (mask << 16) | (invert << 24) }
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
+  .info = snd_cs4231_info_single, \
+  .get = snd_cs4231_get_single, .put = snd_cs4231_put_single, \
+  .private_value = reg | (shift << 8) | (mask << 16) | (invert << 24) }
 
 int snd_cs4231_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo);
 int snd_cs4231_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol);
 int snd_cs4231_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol);
 
 #define CS4231_DOUBLE(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \
-{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \
-  info: snd_cs4231_info_double, \
-  get: snd_cs4231_get_double, put: snd_cs4231_put_double, \
-  private_value: left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) }
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
+  .info = snd_cs4231_info_double, \
+  .get = snd_cs4231_get_double, .put = snd_cs4231_put_double, \
+  .private_value = left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) }
 
 int snd_cs4231_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo);
 int snd_cs4231_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol);
diff -Nru a/include/sound/cs46xx.h b/include/sound/cs46xx.h
--- a/include/sound/cs46xx.h	Mon Oct  7 11:25:29 2002
+++ b/include/sound/cs46xx.h	Mon Oct  7 11:25:29 2002
@@ -1710,9 +1710,12 @@
 #define CS46XX_SECONDARY_CODEC_OFFSET		0x80
 #define CS46XX_DSP_CAPTURE_CHANNEL          1
 
-/*
- *
- */
+/* capture */
+#define CS46XX_DSP_CAPTURE_CHANNEL          1
+
+/* mixer */
+#define CS46XX_MIXER_SPDIF_INPUT_ELEMENT    1
+#define CS46XX_MIXER_SPDIF_OUTPUT_ELEMENT   2
 
 typedef struct _snd_cs46xx cs46xx_t;
 
@@ -1799,6 +1802,7 @@
 	int amplifier;
 	void (*amplifier_ctrl)(cs46xx_t *, int);
 	void (*active_ctrl)(cs46xx_t *, int);
+  	void (*mixer_init)(cs46xx_t *);
 	struct pci_dev *acpi_dev;
 	int acpi_port;
 	snd_kcontrol_t *eapd_switch; /* for amplifier hack */
@@ -1809,8 +1813,12 @@
 #ifdef CONFIG_PM
 	struct pm_dev *pm_dev;
 #endif
-
+#ifdef CONFIG_SND_CS46XX_DEBUG_GPIO
+  int current_gpio;
+#endif
 #ifdef CONFIG_SND_CS46XX_NEW_DSP
+	struct semaphore spos_mutex;
+
 	dsp_spos_instance_t * dsp_spos_instance;
 #else /* for compatibility */
 	cs46xx_pcm_t *playback_pcm;
diff -Nru a/include/sound/cs46xx_dsp_spos.h b/include/sound/cs46xx_dsp_spos.h
--- a/include/sound/cs46xx_dsp_spos.h	Mon Oct  7 11:25:35 2002
+++ b/include/sound/cs46xx_dsp_spos.h	Mon Oct  7 11:25:35 2002
@@ -109,6 +109,7 @@
 
 	snd_info_entry_t *proc_info;
 	int ref_count;
+	spinlock_t lock;
 
 	int deleted;
 } dsp_scb_descriptor_t;
@@ -141,8 +142,6 @@
 	segment_desc_t code;
 
 	/* PCM playback */
-	struct semaphore pcm_mutex;
-
 	dsp_scb_descriptor_t * master_mix_scb;
 	int npcm_channels;
 	int nsrc_scb;
@@ -162,7 +161,6 @@
 	snd_info_entry_t * proc_sample_dump_info_entry;
 
 	/* SCB's descriptors */
-	struct semaphore scb_mutex;
 	int nscb;
 	int scb_highest_frag_index;
 	dsp_scb_descriptor_t scbs[DSP_MAX_SCB_DESC];
@@ -177,6 +175,27 @@
 	/* SPDIF status */
 	int spdif_status_out;
 	int spdif_status_in;
+	u32 spdif_input_volume;
+
+	/* SPDIF input sample rate converter */
+	dsp_scb_descriptor_t * spdif_in_src;
+	/* SPDIF input asynch. receiver */
+	dsp_scb_descriptor_t * asynch_rx_scb;
+
+	/* Capture record mixer SCB */
+	dsp_scb_descriptor_t * record_mixer_scb;
+    
+	/* CODEC input SCB */
+	dsp_scb_descriptor_t * codec_in_scb;
+
+	/* reference snooper */
+	dsp_scb_descriptor_t * ref_snoop_scb;
+
+	/* record sources */
+	dsp_scb_descriptor_t * pcm_input;
+	dsp_scb_descriptor_t * adc_input;
+
+	int spdif_in_sample_rate;
 } dsp_spos_instance_t;
 
 #endif /* __DSP_SPOS_H__ */
diff -Nru a/include/sound/emu10k1.h b/include/sound/emu10k1.h
--- a/include/sound/emu10k1.h	Mon Oct  7 11:25:28 2002
+++ b/include/sound/emu10k1.h	Mon Oct  7 11:25:28 2002
@@ -795,7 +795,8 @@
 	snd_pcm_substream_t *substream;
 	emu10k1_voice_t *voices[2];
 	emu10k1_voice_t *extra;
-	int running;
+	unsigned short running;
+	unsigned short first_ptr;
 	snd_util_memblk_t *memblk;
 	unsigned int start_addr;
 	unsigned int ccca_start_addr;
@@ -1194,8 +1195,12 @@
 #define A_EXTIN_AC97_R		0x01	/* AC'97 capture channel - right */
 #define A_EXTIN_SPDIF_CD_L	0x02	/* digital CD left */
 #define A_EXTIN_SPDIF_CD_R	0x03	/* digital CD left */
+#define A_EXTIN_OPT_SPDIF_L     0x04    /* audigy drive Optical SPDIF - left */
+#define A_EXTIN_OPT_SPDIF_R     0x05    /*                              right */ 
 #define A_EXTIN_LINE2_L		0x08	/* audigy drive line2/mic2 - left */
 #define A_EXTIN_LINE2_R		0x09	/*                           right */
+#define A_EXTIN_RCA_SPDIF_L     0x0a    /* audigy drive RCA SPDIF - left */
+#define A_EXTIN_RCA_SPDIF_R     0x0b    /*                          right */
 #define A_EXTIN_AUX2_L		0x0c	/* audigy drive aux2 - left */
 #define A_EXTIN_AUX2_R		0x0d	/*                   - right */
 
@@ -1296,15 +1301,24 @@
 
 typedef struct {
 	char name[128];
+
 	unsigned long gpr_valid[0x100/(sizeof(unsigned long)*8)]; /* bitmask of valid initializers */
 	unsigned int gpr_map[0x100];	  /* initializers */
+
 	unsigned int gpr_add_control_count; /* count of GPR controls to add/replace */
 	emu10k1_fx8010_control_gpr_t *gpr_add_controls; /* GPR controls to add/replace */
+
 	unsigned int gpr_del_control_count; /* count of GPR controls to remove */
 	snd_ctl_elem_id_t *gpr_del_controls; /* IDs of GPR controls to remove */
+
+	unsigned int gpr_list_control_count; /* count of GPR controls to list */
+	unsigned int gpr_list_control_total; /* total count of GPR controls */
+	emu10k1_fx8010_control_gpr_t *gpr_list_controls; /* listed GPR controls */
+
 	unsigned long tram_valid[0xa0/(sizeof(unsigned long)*8)]; /* bitmask of valid initializers */
 	unsigned int tram_data_map[0xa0]; /* data initializers */
 	unsigned int tram_addr_map[0xa0]; /* map initializers */
+
 	unsigned long code_valid[512/(sizeof(unsigned long)*8)];  /* bitmask of valid instructions */
 	unsigned int code[512][2];	  /* one instruction - 64 bits */
 } emu10k1_fx8010_code_t;
diff -Nru a/include/sound/mixer_oss.h b/include/sound/mixer_oss.h
--- a/include/sound/mixer_oss.h	Mon Oct  7 11:25:35 2002
+++ b/include/sound/mixer_oss.h	Mon Oct  7 11:25:35 2002
@@ -34,6 +34,8 @@
 typedef int (*snd_mixer_oss_get_recsrce_t)(snd_mixer_oss_file_t *fmixer, int *active_index);
 typedef int (*snd_mixer_oss_put_recsrce_t)(snd_mixer_oss_file_t *fmixer, int active_index);
 
+#define SNDRV_OSS_MAX_MIXERS	32
+
 struct _snd_oss_mixer_slot {
 	int number;
 	int stereo: 1;
@@ -44,24 +46,26 @@
 	unsigned long private_value;
 	void *private_data;
 	void (*private_free)(snd_mixer_oss_slot_t *slot);
+	int volume[2];
 };
 
 struct _snd_oss_mixer {
 	snd_card_t *card;
 	char id[16];
 	char name[32];
-	snd_mixer_oss_slot_t slots[32];		/* OSS mixer slots */
+	snd_mixer_oss_slot_t slots[SNDRV_OSS_MAX_MIXERS]; /* OSS mixer slots */
 	unsigned int mask_recsrc;		/* exclusive recsrc mask */
 	snd_mixer_oss_get_recsrce_t get_recsrc;
 	snd_mixer_oss_put_recsrce_t put_recsrc;
 	void *private_data_recsrc;
 	void (*private_free_recsrc)(snd_mixer_oss_t *mixer);
+	struct semaphore reg_mutex;
+	snd_info_entry_t *proc_entry;
 	/* --- */
 	int oss_recsrc;
 };
 
 struct _snd_oss_file {
-	int volume[32][2];
 	snd_card_t *card;
 	snd_mixer_oss_t *mixer;
 };
diff -Nru a/include/sound/mpu401.h b/include/sound/mpu401.h
--- a/include/sound/mpu401.h	Mon Oct  7 11:25:37 2002
+++ b/include/sound/mpu401.h	Mon Oct  7 11:25:37 2002
@@ -40,6 +40,7 @@
 #define MPU401_HW_YMFPCI		14	/* YMF DS-XG PCI */
 #define MPU401_HW_CMIPCI		15	/* CMIPCI MPU-401 UART */
 #define MPU401_HW_ALS4000		16	/* Avance Logic ALS4000 */
+#define MPU401_HW_INTEL8X0		17	/* Intel8x0 driver */
 
 #define MPU401_MODE_BIT_INPUT		0
 #define MPU401_MODE_BIT_OUTPUT		1
diff -Nru a/include/sound/opl3.h b/include/sound/opl3.h
--- a/include/sound/opl3.h	Mon Oct  7 11:25:26 2002
+++ b/include/sound/opl3.h	Mon Oct  7 11:25:26 2002
@@ -288,7 +288,7 @@
 	snd_midi_channel_set_t * chset;
 
 #ifdef CONFIG_SND_SEQUENCER_OSS
-	snd_seq_device_t *oss_seq_dev;	/* OSS sequencer device, WIP */
+	snd_seq_device_t *oss_seq_dev;	/* OSS sequencer device */
 	snd_midi_channel_set_t * oss_chset;
 #endif
  
diff -Nru a/include/sound/pcm.h b/include/sound/pcm.h
--- a/include/sound/pcm.h	Mon Oct  7 11:25:35 2002
+++ b/include/sound/pcm.h	Mon Oct  7 11:25:35 2002
@@ -460,6 +460,7 @@
 int snd_pcm_new(snd_card_t * card, char *id, int device,
 		int playback_count, int capture_count,
 		snd_pcm_t **rpcm);
+int snd_pcm_new_stream(snd_pcm_t *pcm, int stream, int substream_count);
 
 int snd_pcm_notify(snd_pcm_notify_t *notify, int nfree);
 
@@ -526,7 +527,7 @@
 	int c = 32;
 	while (n > 0xffffffffU) {
 		q1 <<= 1;
-		if (n > d) {
+		if (n >= d) {
 			n -= d;
 			q1 |= 1;
 		}
diff -Nru a/include/sound/pcm_sgbuf.h b/include/sound/pcm_sgbuf.h
--- a/include/sound/pcm_sgbuf.h	Mon Oct  7 11:25:36 2002
+++ b/include/sound/pcm_sgbuf.h	Mon Oct  7 11:25:36 2002
@@ -45,6 +45,13 @@
 	return (size + PAGE_SIZE - 1) >> PAGE_SHIFT;
 }
 
+/*
+ * return the physical address at the corresponding offset
+ */
+static inline dma_addr_t snd_pcm_sgbuf_get_addr(struct snd_sg_buf *sgbuf, size_t offset)
+{
+	return sgbuf->table[offset >> PAGE_SHIFT].addr + offset % PAGE_SIZE;
+}
 
 int snd_pcm_sgbuf_init(snd_pcm_substream_t *substream, struct pci_dev *pci, int tblsize);
 int snd_pcm_sgbuf_delete(snd_pcm_substream_t *substream);
diff -Nru a/include/sound/snd_wavefront.h b/include/sound/snd_wavefront.h
--- a/include/sound/snd_wavefront.h	Mon Oct  7 11:25:26 2002
+++ b/include/sound/snd_wavefront.h	Mon Oct  7 11:25:26 2002
@@ -91,8 +91,9 @@
 	int samples_used;                  /* how many */
 	char interrupts_are_midi;          /* h/w MPU interrupts enabled ? */
 	char rom_samples_rdonly;           /* can we write on ROM samples */
+	spinlock_t irq_lock;
 	wait_queue_head_t interrupt_sleeper; 
-        snd_wavefront_midi_t midi;         /* ICS2115 MIDI interface */
+	snd_wavefront_midi_t midi;         /* ICS2115 MIDI interface */
 };
 
 struct _snd_wavefront_card {
diff -Nru a/include/sound/sndmagic.h b/include/sound/sndmagic.h
--- a/include/sound/sndmagic.h	Mon Oct  7 11:25:29 2002
+++ b/include/sound/sndmagic.h	Mon Oct  7 11:25:29 2002
@@ -113,7 +113,7 @@
 #define intel8x0_t_magic			0xa15a2a01
 #define es1968_t_magic				0xa15a2b01
 #define esschan_t_magic				0xa15a2b02
-#define via686a_t_magic				0xa15a2c01
+#define via82xx_t_magic				0xa15a2c01
 #define pdplus_t_magic				0xa15a2d01
 #define cmipci_t_magic				0xa15a2e01
 #define ymfpci_t_magic				0xa15a2f01
@@ -126,7 +126,6 @@
 #define m3_dma_t_magic				0xa15a3202
 #define nm256_t_magic				0xa15a3301
 #define nm256_dma_t_magic			0xa15a3302
-#define via8233_t_magic				0xa15a3401
 #define pmac_t_magic				0xa15a3501
 #define ali_t_magic				0xa15a3601
 #define mtpav_t_magic				0xa15a3701
@@ -137,6 +136,13 @@
 #define sa11xx_uda1341_t_magic			0xa15a3b00
 #define uda1341_t_magic                         0xa15a3c00
 #define l3_client_t_magic                       0xa15a3d00
+#define snd_usb_audio_t_magic			0xa15a3e01
+#define usb_mixer_elem_info_t_magic		0xa15a3e02
+#define snd_usb_stream_t_magic			0xa15a3e03
+#define snd_usb_midi_t_magic			0xa15a3f01
+#define snd_usb_midi_out_endpoint_t_magic	0xa15a3f02
+#define snd_usb_midi_in_endpoint_t_magic	0xa15a3f03
+
 
 #else
 
diff -Nru a/include/sound/sscape_ioctl.h b/include/sound/sscape_ioctl.h
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/include/sound/sscape_ioctl.h	Mon Oct  7 11:25:40 2002
@@ -0,0 +1,19 @@
+#ifndef SSCAPE_IOCTL_H
+#define SSCAPE_IOCTL_H
+
+
+struct sscape_bootblock
+{
+  unsigned char code[256];
+  unsigned version;
+};
+
+struct sscape_microcode
+{
+  unsigned char code[65536];
+};
+
+#define SND_SSCAPE_LOAD_BOOTB  _IOWR('P', 100, struct sscape_bootblock)
+#define SND_SSCAPE_LOAD_MCODE  _IOW('P', 101, struct sscape_microcode)
+
+#endif
diff -Nru a/include/sound/version.h b/include/sound/version.h
--- a/include/sound/version.h	Mon Oct  7 11:25:31 2002
+++ b/include/sound/version.h	Mon Oct  7 11:25:31 2002
@@ -1,3 +1,3 @@
 /* include/version.h.  Generated automatically by configure.  */
-#define CONFIG_SND_VERSION "0.9.0rc2"
-#define CONFIG_SND_DATE " (Mon Aug 05 14:24:05 2002 UTC)"
+#define CONFIG_SND_VERSION "0.9.0rc3"
+#define CONFIG_SND_DATE " (Fri Oct 04 13:09:13 2002 UTC)"
diff -Nru a/include/video/font.h b/include/video/font.h
--- a/include/video/font.h	Mon Oct  7 11:25:32 2002
+++ b/include/video/font.h	Mon Oct  7 11:25:32 2002
@@ -28,6 +28,7 @@
 #define SUN8x16_IDX	4
 #define SUN12x22_IDX	5
 #define ACORN8x8_IDX	6
+#define	MINI4x6_IDX	7
 
 extern struct fbcon_font_desc	font_vga_8x8,
 				font_vga_8x16,
@@ -35,7 +36,8 @@
 				font_vga_6x11,
 				font_sun_8x16,
 				font_sun_12x22,
-				font_acorn_8x8;
+				font_acorn_8x8,
+				font_mini_4x6;
 
 /* Find a font with a specific name */
 
diff -Nru a/init/do_mounts.c b/init/do_mounts.c
--- a/init/do_mounts.c	Mon Oct  7 11:25:32 2002
+++ b/init/do_mounts.c	Mon Oct  7 11:25:32 2002
@@ -642,63 +642,6 @@
 	return rd_load_image("/dev/root");
 }
 
-#ifdef CONFIG_DEVFS_FS
-
-static void __init convert_name(char *prefix, char *name, char *p, int part)
-{
-	int host, bus, target, lun;
-	char dest[64];
-	char src[64];
-	char *base = p - 1;
-
-	/*  Decode "c#b#t#u#"  */
-	if (*p++ != 'c')
-		return;
-	host = simple_strtol(p, &p, 10);
-	if (*p++ != 'b')
-		return;
-	bus = simple_strtol(p, &p, 10);
-	if (*p++ != 't')
-		return;
-	target = simple_strtol(p, &p, 10);
-	if (*p++ != 'u')
-		return;
-	lun = simple_strtol(p, &p, 10);
-	if (!part)
-		sprintf(dest, "%s/host%d/bus%d/target%d/lun%d",
-				prefix, host, bus, target, lun);
-	else if (*p++ == 'p')
-		sprintf(dest, "%s/host%d/bus%d/target%d/lun%d/part%s",
-				prefix, host, bus, target, lun, p);
-	else
-		sprintf(dest, "%s/host%d/bus%d/target%d/lun%d/disc",
-				prefix, host, bus, target, lun);
-	*base = '\0';
-	sprintf(src, "/dev/%s", name);
-	sys_mkdir(src, 0755);
-	*base = '/';
-	sprintf(src, "/dev/%s", name);
-	sys_symlink(dest, src);
-}
-
-static void __init devfs_make_root(char *name)
-{
-
-	if (!strncmp(name, "sd/", 3))
-		convert_name("../scsi", name, name+3, 1);
-	else if (!strncmp(name, "sr/", 3))
-		convert_name("../scsi", name, name+3, 0);
-	else if (!strncmp(name, "ide/hd/", 7))
-		convert_name("..", name, name + 7, 1);
-	else if (!strncmp(name, "ide/cd/", 7))
-		convert_name("..", name, name + 7, 0);
-}
-#else
-static void __init devfs_make_root(char *name)
-{
-}
-#endif
-
 static void __init mount_root(void)
 {
 #ifdef CONFIG_ROOT_NFS
@@ -713,7 +656,6 @@
 		ROOT_DEV = Root_FD0;
 	}
 #endif
-	devfs_make_root(root_device_name);
 	create_dev("/dev/root", ROOT_DEV, root_device_name);
 #ifdef CONFIG_BLK_DEV_FD
 	if (MAJOR(ROOT_DEV) == FLOPPY_MAJOR) {
@@ -768,7 +710,7 @@
 
 	pid = kernel_thread(do_linuxrc, "/linuxrc", SIGCHLD);
 	if (pid > 0) {
-		while (pid != wait(&i))
+		while (pid != waitpid(-1, &i, 0))
 			yield();
 	}
 
diff -Nru a/init/main.c b/init/main.c
--- a/init/main.c	Mon Oct  7 11:25:26 2002
+++ b/init/main.c	Mon Oct  7 11:25:26 2002
@@ -30,6 +30,7 @@
 #include <linux/percpu.h>
 #include <linux/kernel_stat.h>
 #include <linux/security.h>
+#include <linux/workqueue.h>
 
 #include <asm/io.h>
 #include <asm/bugs.h>
@@ -432,6 +433,7 @@
 		initrd_start = 0;
 	}
 #endif
+	page_address_init();
 	mem_init();
 	kmem_cache_sizes_init();
 	pidhash_init();
@@ -477,7 +479,7 @@
 	} while (call < &__initcall_end);
 
 	/* Make sure there is no pending stuff from the initcall sequence */
-	flush_scheduled_tasks();
+	flush_scheduled_work();
 }
 
 /*
@@ -493,17 +495,10 @@
 	sysctl_init();
 #endif
 
-	/*
-	 * Ok, at this point all CPU's should be initialized, so
-	 * we can start looking into devices..
-	 */
-#if defined(CONFIG_ARCH_S390)
-	s390_init_machine_check();
-#endif
 	/* Networking initialization needs a process context */ 
 	sock_init();
 
-	start_context_thread();
+	init_workqueues();
 	do_initcalls();
 }
 
@@ -553,8 +548,6 @@
 	free_initmem();
 	unlock_kernel();
 	system_running = 1;
-
-	kstat.pgfree = 0;
 
 	if (open("/dev/console", O_RDWR, 0) < 0)
 		printk("Warning: unable to open an initial console.\n");
diff -Nru a/kernel/Makefile b/kernel/Makefile
--- a/kernel/Makefile	Mon Oct  7 11:25:26 2002
+++ b/kernel/Makefile	Mon Oct  7 11:25:26 2002
@@ -2,13 +2,13 @@
 # Makefile for the linux kernel.
 #
 
-export-objs = signal.o sys.o kmod.o context.o ksyms.o pm.o exec_domain.o \
+export-objs = signal.o sys.o kmod.o workqueue.o ksyms.o pm.o exec_domain.o \
 	      printk.o platform.o suspend.o dma.o module.o cpufreq.o
 
 obj-y     = sched.o fork.o exec_domain.o panic.o printk.o \
 	    module.o exit.o itimer.o time.o softirq.o resource.o \
 	    sysctl.o capability.o ptrace.o timer.o user.o \
-	    signal.o sys.o kmod.o context.o futex.o platform.o pid.o
+	    signal.o sys.o kmod.o workqueue.o futex.o platform.o pid.o
 
 obj-$(CONFIG_GENERIC_ISA_DMA) += dma.o
 obj-$(CONFIG_SMP) += cpu.o
diff -Nru a/kernel/context.c b/kernel/context.c
--- a/kernel/context.c	Mon Oct  7 11:25:32 2002
+++ /dev/null	Wed Dec 31 16:00:00 1969
@@ -1,221 +0,0 @@
-/*
- * linux/kernel/context.c
- *
- * Mechanism for running arbitrary tasks in process context
- *
- * dwmw2@redhat.com:		Genesis
- *
- * andrewm@uow.edu.au:		2.4.0-test12
- *	- Child reaping
- *	- Support for tasks which re-add themselves
- *	- flush_scheduled_tasks.
- */
-
-#define __KERNEL_SYSCALLS__
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/init.h>
-#include <linux/unistd.h>
-#include <linux/signal.h>
-#include <linux/completion.h>
-#include <linux/tqueue.h>
-
-static DECLARE_TASK_QUEUE(tq_context);
-static DECLARE_WAIT_QUEUE_HEAD(context_task_wq);
-static DECLARE_WAIT_QUEUE_HEAD(context_task_done);
-static int keventd_running;
-static struct task_struct *keventd_task;
-
-static spinlock_t tqueue_lock __cacheline_aligned_in_smp = SPIN_LOCK_UNLOCKED;
-
-typedef struct list_head task_queue;
-
-/*
- * Queue a task on a tq.  Return non-zero if it was successfully
- * added.
- */
-static inline int queue_task(struct tq_struct *tq, task_queue *list)
-{
-	int ret = 0;
-	unsigned long flags;
-
-	if (!test_and_set_bit(0, &tq->sync)) {
-		spin_lock_irqsave(&tqueue_lock, flags);
-		list_add_tail(&tq->list, list);
-		spin_unlock_irqrestore(&tqueue_lock, flags);
-		ret = 1;
-	}
-	return ret;
-}
-
-#define TQ_ACTIVE(q)	(!list_empty(&q))
-
-static inline void run_task_queue(task_queue *list)
-{
-	struct list_head head, *next;
-	unsigned long flags;
-
-	if (!TQ_ACTIVE(*list))
-		return;
-
-	spin_lock_irqsave(&tqueue_lock, flags);
-	list_add(&head, list);
-	list_del_init(list);
-	spin_unlock_irqrestore(&tqueue_lock, flags);
-
-	next = head.next;
-	while (next != &head) {
-		void (*f) (void *);
-		struct tq_struct *p;
-		void *data;
-
-		p = list_entry(next, struct tq_struct, list);
-		next = next->next;
-		f = p->routine;
-		data = p->data;
-		wmb();
-		p->sync = 0;
-		if (f)
-			f(data);
-	}
-}
-
-static int need_keventd(const char *who)
-{
-	if (keventd_running == 0)
-		printk(KERN_ERR "%s(): keventd has not started\n", who);
-	return keventd_running;
-}
-	
-int current_is_keventd(void)
-{
-	int ret = 0;
-	if (need_keventd(__FUNCTION__))
-		ret = (current == keventd_task);
-	return ret;
-}
-
-/**
- * schedule_task - schedule a function for subsequent execution in process context.
- * @task: pointer to a &tq_struct which defines the function to be scheduled.
- *
- * May be called from interrupt context.  The scheduled function is run at some
- * time in the near future by the keventd kernel thread.  If it can sleep, it
- * should be designed to do so for the minimum possible time, as it will be
- * stalling all other scheduled tasks.
- *
- * schedule_task() returns non-zero if the task was successfully scheduled.
- * If @task is already residing on a task queue then schedule_task() fails
- * to schedule your task and returns zero.
- */
-int schedule_task(struct tq_struct *task)
-{
-	int ret;
-	need_keventd(__FUNCTION__);
-	ret = queue_task(task, &tq_context);
-	wake_up(&context_task_wq);
-	return ret;
-}
-
-static int context_thread(void *startup)
-{
-	struct task_struct *curtask = current;
-	DECLARE_WAITQUEUE(wait, curtask);
-	struct k_sigaction sa;
-
-	daemonize();
-	strcpy(curtask->comm, "keventd");
-	current->flags |= PF_IOTHREAD;
-	keventd_running = 1;
-	keventd_task = curtask;
-
-	spin_lock_irq(&curtask->sig->siglock);
-	siginitsetinv(&curtask->blocked, sigmask(SIGCHLD));
-	recalc_sigpending();
-	spin_unlock_irq(&curtask->sig->siglock);
-
-	complete((struct completion *)startup);
-
-	/* Install a handler so SIGCLD is delivered */
-	sa.sa.sa_handler = SIG_IGN;
-	sa.sa.sa_flags = 0;
-	siginitset(&sa.sa.sa_mask, sigmask(SIGCHLD));
-	do_sigaction(SIGCHLD, &sa, (struct k_sigaction *)0);
-
-	/*
-	 * If one of the functions on a task queue re-adds itself
-	 * to the task queue we call schedule() in state TASK_RUNNING
-	 */
-	for (;;) {
-		set_task_state(curtask, TASK_INTERRUPTIBLE);
-		add_wait_queue(&context_task_wq, &wait);
-		if (TQ_ACTIVE(tq_context))
-			set_task_state(curtask, TASK_RUNNING);
-		schedule();
-		remove_wait_queue(&context_task_wq, &wait);
-		run_task_queue(&tq_context);
-		wake_up(&context_task_done);
-		if (signal_pending(curtask)) {
-			while (waitpid(-1, (unsigned int *)0, __WALL|WNOHANG) > 0)
-				;
-			spin_lock_irq(&curtask->sig->siglock);
-			flush_signals(curtask);
-			recalc_sigpending();
-			spin_unlock_irq(&curtask->sig->siglock);
-		}
-	}
-}
-
-/**
- * flush_scheduled_tasks - ensure that any scheduled tasks have run to completion.
- *
- * Forces execution of the schedule_task() queue and blocks until its completion.
- *
- * If a kernel subsystem uses schedule_task() and wishes to flush any pending
- * tasks, it should use this function.  This is typically used in driver shutdown
- * handlers.
- *
- * The caller should hold no spinlocks and should hold no semaphores which could
- * cause the scheduled tasks to block.
- */
-static struct tq_struct dummy_task;
-
-void flush_scheduled_tasks(void)
-{
-	int count;
-	DECLARE_WAITQUEUE(wait, current);
-
-	/*
-	 * Do it twice. It's possible, albeit highly unlikely, that
-	 * the caller queued a task immediately before calling us,
-	 * and that the eventd thread was already past the run_task_queue()
-	 * but not yet into wake_up(), so it woke us up before completing
-	 * the caller's queued task or our new dummy task.
-	 */
-	add_wait_queue(&context_task_done, &wait);
-	for (count = 0; count < 2; count++) {
-		set_current_state(TASK_UNINTERRUPTIBLE);
-
-		/* Queue a dummy task to make sure we get kicked */
-		schedule_task(&dummy_task);
-
-		/* Wait for it to complete */
-		schedule();
-	}
-	remove_wait_queue(&context_task_done, &wait);
-}
-	
-int start_context_thread(void)
-{
-	static struct completion startup __initdata = COMPLETION_INITIALIZER(startup);
-
-	kernel_thread(context_thread, &startup, CLONE_FS | CLONE_FILES);
-	wait_for_completion(&startup);
-	return 0;
-}
-
-EXPORT_SYMBOL(schedule_task);
-EXPORT_SYMBOL(flush_scheduled_tasks);
-
diff -Nru a/kernel/fork.c b/kernel/fork.c
--- a/kernel/fork.c	Mon Oct  7 11:25:25 2002
+++ b/kernel/fork.c	Mon Oct  7 11:25:25 2002
@@ -166,8 +166,11 @@
 	 */
 	max_threads = mempages / (THREAD_SIZE/PAGE_SIZE) / 8;
 
-	init_task.rlim[RLIMIT_NPROC].rlim_cur = max_threads/2;
-	init_task.rlim[RLIMIT_NPROC].rlim_max = max_threads/2;
+	/*
+	 * we need to allow at least 10 threads to boot a system
+	 */
+	init_task.rlim[RLIMIT_NPROC].rlim_cur = max(10, max_threads/2);
+	init_task.rlim[RLIMIT_NPROC].rlim_max = max(10, max_threads/2);
 }
 
 static struct task_struct *dup_task_struct(struct task_struct *orig)
diff -Nru a/kernel/futex.c b/kernel/futex.c
--- a/kernel/futex.c	Mon Oct  7 11:25:28 2002
+++ b/kernel/futex.c	Mon Oct  7 11:25:28 2002
@@ -9,6 +9,9 @@
  *  "The futexes are also cursed."
  *  "But they come in a choice of three flavours!"
  *
+ *  Generalized futexes for every mapping type, Ingo Molnar, 2002
+ *
+ *
  *  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
@@ -23,44 +26,29 @@
  *  along with this program; if not, write to the Free Software
  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
-#include <linux/kernel.h>
-#include <linux/spinlock.h>
-#include <linux/sched.h>
-#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/poll.h>
+#include <linux/file.h>
 #include <linux/hash.h>
 #include <linux/init.h>
-#include <linux/fs.h>
 #include <linux/futex.h>
 #include <linux/vcache.h>
-#include <linux/highmem.h>
-#include <linux/time.h>
-#include <linux/pagemap.h>
-#include <linux/slab.h>
-#include <linux/poll.h>
-#include <linux/file.h>
-#include <linux/dcache.h>
-
-/* Simple "sleep if unchanged" interface. */
 
-/* FIXME: This may be way too small. --RR */
-#define FUTEX_HASHBITS 6
-
-extern void send_sigio(struct fown_struct *fown, int fd, int band);
+#define FUTEX_HASHBITS 8
 
-/* Everyone needs a dentry and inode */
-static struct vfsmount *futex_mnt;
-
-/* We use this instead of a normal wait_queue_t, so we can wake only
-   the relevent ones (hashed queues may be shared) */
+/*
+ * We use this hashed waitqueue instead of a normal wait_queue_t, so
+ * we can wake only the relevent ones (hashed queues may be shared):
+ */
 struct futex_q {
 	struct list_head list;
 	wait_queue_head_t waiters;
 
 	/* Page struct and offset within it. */
 	struct page *page;
-	unsigned int offset;
+	int offset;
 
-	/* the virtual => physical cache */
+	/* the virtual => physical COW-safe cache */
 	vcache_t vcache;
 
 	/* For fd, sigio sent using these. */
@@ -72,14 +60,36 @@
 static struct list_head futex_queues[1<<FUTEX_HASHBITS];
 static spinlock_t futex_lock = SPIN_LOCK_UNLOCKED;
 
-static inline struct list_head *hash_futex(struct page *page,
-					   unsigned long offset)
+extern void send_sigio(struct fown_struct *fown, int fd, int band);
+
+/* Futex-fs vfsmount entry: */
+static struct vfsmount *futex_mnt;
+
+/*
+ * These are all locks that are necessery to look up a physical
+ * mapping safely, and modify/search the futex hash, atomically:
+ */
+static inline void lock_futex_mm(void)
+{
+	spin_lock(&current->mm->page_table_lock);
+	spin_lock(&vcache_lock);
+	spin_lock(&futex_lock);
+}
+
+static inline void unlock_futex_mm(void)
 {
-	unsigned long h;
+	spin_unlock(&futex_lock);
+	spin_unlock(&vcache_lock);
+	spin_unlock(&current->mm->page_table_lock);
+}
 
-	/* struct page is shared, so we can hash on its address */
-	h = (unsigned long)page + offset;
-	return &futex_queues[hash_long(h, FUTEX_HASHBITS)];
+/*
+ * The physical page is shared, so we can hash on its address:
+ */
+static inline struct list_head *hash_futex(struct page *page, int offset)
+{
+	return &futex_queues[hash_long((unsigned long)page + offset,
+							FUTEX_HASHBITS)];
 }
 
 /* Waiter either waiting in FUTEX_WAIT or poll(), or expecting signal */
@@ -90,45 +100,77 @@
 		send_sigio(&q->filp->f_owner, q->fd, POLL_IN);
 }
 
-/* Get kernel address of the user page and pin it. */
-static struct page *pin_page(unsigned long page_start)
+/*
+ * Get kernel address of the user page and pin it.
+ *
+ * Must be called with (and returns with) all futex-MM locks held.
+ */
+static struct page *__pin_page(unsigned long addr)
 {
 	struct mm_struct *mm = current->mm;
-	struct page *page = NULL;
+	struct page *page, *tmp;
 	int err;
 
-	down_read(&mm->mmap_sem);
+	/*
+	 * Do a quick atomic lookup first - this is the fastpath.
+	 */
+	page = follow_page(mm, addr, 0);
+	if (likely(page != NULL)) {
+		get_page(page);
+		return page;
+	}
+
+	/*
+	 * No luck - need to fault in the page:
+	 */
+repeat_lookup:
 
-	err = get_user_pages(current, mm, page_start,
-			     1 /* one page */,
-			     0 /* not writable */,
-			     0 /* don't force */,
-			     &page,
-			     NULL /* don't return vmas */);
+	unlock_futex_mm();
+
+	down_read(&mm->mmap_sem);
+	err = get_user_pages(current, mm, addr, 1, 0, 0, &page, NULL);
 	up_read(&mm->mmap_sem);
+
+	lock_futex_mm();
+
 	if (err < 0)
-		return ERR_PTR(err);
+		return NULL;
+	/*
+	 * Since the faulting happened with locks released, we have to
+	 * check for races:
+	 */
+	tmp = follow_page(mm, addr, 0);
+	if (tmp != page)
+		goto repeat_lookup;
+
 	return page;
 }
 
 static inline void unpin_page(struct page *page)
 {
-	page_cache_release(page);
+	put_page(page);
 }
 
-static int futex_wake(unsigned long uaddr, unsigned int offset, int num)
+/*
+ * Wake up all waiters hashed on the physical page that is mapped
+ * to this virtual address:
+ */
+static int futex_wake(unsigned long uaddr, int offset, int num)
 {
 	struct list_head *i, *next, *head;
 	struct page *page;
-	int ret;
+	int ret = 0;
+
+	lock_futex_mm();
+
+	page = __pin_page(uaddr - offset);
+	if (!page) {
+		unlock_futex_mm();
+		return -EFAULT;
+	}
 
-	page = pin_page(uaddr - offset);
-	ret = IS_ERR(page);
-	if (ret)
-		goto out;
 	head = hash_futex(page, offset);
 
-	spin_lock(&futex_lock);
 	list_for_each_safe(i, next, head) {
 		struct futex_q *this = list_entry(i, struct futex_q, list);
 
@@ -140,12 +182,19 @@
 				break;
 		}
 	}
-	spin_unlock(&futex_lock);
+
+	unlock_futex_mm();
 	unpin_page(page);
-out:
+
 	return ret;
 }
 
+/*
+ * This gets called by the COW code, we have to rehash any
+ * futexes that were pending on the old physical page, and
+ * rehash it to the new physical page. The pagetable_lock
+ * and vcache_lock is already held:
+ */
 static void futex_vcache_callback(vcache_t *vcache, struct page *new_page)
 {
 	struct futex_q *q = container_of(vcache, struct futex_q, vcache);
@@ -162,47 +211,23 @@
 	spin_unlock(&futex_lock);
 }
 
-/* Add at end to avoid starvation */
-static inline int queue_me(struct list_head *head,
-			    struct futex_q *q,
-			    struct page *page,
-			    unsigned int offset,
-			    int fd,
-			    struct file *filp,
-			    unsigned long uaddr)
+static inline void __queue_me(struct futex_q *q, struct page *page,
+				unsigned long uaddr, int offset,
+				int fd, struct file *filp)
 {
-	struct page *tmp;
-	int ret = 0;
+	struct list_head *head = hash_futex(page, offset);
 
 	q->offset = offset;
 	q->fd = fd;
 	q->filp = filp;
+	q->page = page;
 
-	spin_lock(&vcache_lock);
-	spin_lock(&futex_lock);
-	spin_lock(&current->mm->page_table_lock);
-
+	list_add_tail(&q->list, head);
 	/*
-	 * Has the mapping changed meanwhile?
+	 * We register a futex callback to this virtual address,
+	 * to make sure a COW properly rehashes the futex-queue.
 	 */
-	tmp = follow_page(current->mm, uaddr, 0);
-
-	if (tmp == page) {
-		q->page = page;
-		list_add_tail(&q->list, head);
-		/*
-		 * We register a futex callback to this virtual address,
-		 * to make sure a COW properly rehashes the futex-queue.
-		 */
-		__attach_vcache(&q->vcache, uaddr, current->mm, futex_vcache_callback);
-	} else
-		ret = 1;
-
-	spin_unlock(&current->mm->page_table_lock);
-	spin_unlock(&futex_lock);
-	spin_unlock(&vcache_lock);
-
-	return ret;
+	__attach_vcache(&q->vcache, uaddr, current->mm, futex_vcache_callback);
 }
 
 /* Return 1 if we were still queued (ie. 0 means we were woken) */
@@ -210,12 +235,15 @@
 {
 	int ret = 0;
 
+	detach_vcache(&q->vcache);
+
 	spin_lock(&futex_lock);
 	if (!list_empty(&q->list)) {
 		list_del(&q->list);
 		ret = 1;
 	}
 	spin_unlock(&futex_lock);
+
 	return ret;
 }
 
@@ -225,53 +253,59 @@
 		      unsigned long time)
 {
 	DECLARE_WAITQUEUE(wait, current);
-	struct list_head *head;
 	int ret = 0, curval;
 	struct page *page;
 	struct futex_q q;
 
-repeat_lookup:
-	page = pin_page(uaddr - offset);
-	ret = IS_ERR(page);
-	if (ret)
-		goto out;
-	head = hash_futex(page, offset);
-
-	set_current_state(TASK_INTERRUPTIBLE);
 	init_waitqueue_head(&q.waiters);
-	add_wait_queue(&q.waiters, &wait);
-	if (queue_me(head, &q, page, offset, -1, NULL, uaddr)) {
-		unpin_page(page);
-		goto repeat_lookup;
+
+	lock_futex_mm();
+
+	page = __pin_page(uaddr - offset);
+	if (!page) {
+		unlock_futex_mm();
+		return -EFAULT;
 	}
+	__queue_me(&q, page, uaddr, offset, -1, NULL);
+
+	unlock_futex_mm();
 
 	/* Page is pinned, but may no longer be in this address space. */
 	if (get_user(curval, (int *)uaddr) != 0) {
 		ret = -EFAULT;
 		goto out;
 	}
-
 	if (curval != val) {
 		ret = -EWOULDBLOCK;
 		goto out;
 	}
-	time = schedule_timeout(time);
+	/*
+	 * The get_user() above might fault and schedule so we
+	 * cannot just set TASK_INTERRUPTIBLE state when queueing
+	 * ourselves into the futex hash. This code thus has to
+	 * rely on the FUTEX_WAKE code doing a wakeup after removing
+	 * the waiter from the list.
+	 */
+	add_wait_queue(&q.waiters, &wait);
+	set_current_state(TASK_INTERRUPTIBLE);
+	if (!list_empty(&q.list))
+		time = schedule_timeout(time);
+	set_current_state(TASK_RUNNING);
+	/*
+	 * NOTE: we dont remove ourselves from the waitqueue because
+	 * we are the only user of it.
+	 */
 	if (time == 0) {
 		ret = -ETIMEDOUT;
 		goto out;
 	}
-	if (signal_pending(current)) {
+	if (signal_pending(current))
 		ret = -EINTR;
-		goto out;
-	}
 out:
-	detach_vcache(&q.vcache);
-	set_current_state(TASK_RUNNING);
 	/* Were we woken up anyway? */
 	if (!unqueue_me(&q))
 		ret = 0;
-	if (page)
-		unpin_page(page);
+	unpin_page(page);
 
 	return ret;
 }
@@ -318,7 +352,6 @@
 static int futex_fd(unsigned long uaddr, int offset, int signal)
 {
 	struct page *page = NULL;
-	struct list_head *head;
 	struct futex_q *q;
 	struct file *filp;
 	int ret;
@@ -360,25 +393,24 @@
 		goto out;
 	}
 
-repeat_lookup:
-	page = pin_page(uaddr - offset);
-	ret = IS_ERR(page);
-	if (ret) {
+	lock_futex_mm();
+
+	page = __pin_page(uaddr - offset);
+	if (!page) {
+		unlock_futex_mm();
+
 		put_unused_fd(ret);
 		put_filp(filp);
 		kfree(q);
-		page = NULL;
-		goto out;
+		return -EFAULT;
 	}
-	head = hash_futex(page, offset);
 
-	/* Initialize queue structure, and add to hash table. */
-	filp->private_data = q;
 	init_waitqueue_head(&q->waiters);
-	if (queue_me(head, q, page, offset, ret, filp, uaddr)) {
-		unpin_page(page);
-		goto repeat_lookup;
-	}
+	filp->private_data = q;
+
+	__queue_me(q, page, uaddr, offset, ret, filp);
+
+	unlock_futex_mm();
 
 	/* Now we map fd to filp, so userspace can access it */
 	fd_install(ret, filp);
diff -Nru a/kernel/kallsyms.c b/kernel/kallsyms.c
--- a/kernel/kallsyms.c	Mon Oct  7 11:25:39 2002
+++ b/kernel/kallsyms.c	Mon Oct  7 11:25:39 2002
@@ -225,3 +225,9 @@
 	}
 	return(1);
 }
+
+/* Allocate the __kallsyms section, so it's already present in
+ * the temporary vmlinux that kallsyms is run on, so the first
+ * run will pick up the section info already. */
+
+__asm__(".section __kallsyms,\"a\"\n.previous");
diff -Nru a/kernel/kmod.c b/kernel/kmod.c
--- a/kernel/kmod.c	Mon Oct  7 11:25:29 2002
+++ b/kernel/kmod.c	Mon Oct  7 11:25:29 2002
@@ -28,7 +28,7 @@
 #include <linux/namespace.h>
 #include <linux/completion.h>
 #include <linux/file.h>
-#include <linux/tqueue.h>
+#include <linux/workqueue.h>
 
 #include <asm/uaccess.h>
 
@@ -346,18 +346,15 @@
  */
 int call_usermodehelper(char *path, char **argv, char **envp)
 {
-	DECLARE_COMPLETION(work);
+	DECLARE_COMPLETION(done);
 	struct subprocess_info sub_info = {
-		.complete	= &work,
+		.complete	= &done,
 		.path		= path,
 		.argv		= argv,
 		.envp		= envp,
 		.retval		= 0,
 	};
-	struct tq_struct tqs = {
-		.routine	= __call_usermodehelper,
-		.data		= &sub_info,
-	};
+	DECLARE_WORK(work, __call_usermodehelper, &sub_info);
 
 	if (!system_running)
 		return -EBUSY;
@@ -369,8 +366,8 @@
 		/* We can't wait on keventd! */
 		__call_usermodehelper(&sub_info);
 	} else {
-		schedule_task(&tqs);
-		wait_for_completion(&work);
+		schedule_work(&work);
+		wait_for_completion(&done);
 	}
 out:
 	return sub_info.retval;
diff -Nru a/kernel/ksyms.c b/kernel/ksyms.c
--- a/kernel/ksyms.c	Mon Oct  7 11:25:25 2002
+++ b/kernel/ksyms.c	Mon Oct  7 11:25:25 2002
@@ -65,8 +65,6 @@
 
 extern void set_device_ro(kdev_t dev,int flag);
 
-extern void *sys_call_table;
-
 extern struct timezone sys_tz;
 
 #ifdef CONFIG_MODVERSIONS
@@ -134,6 +132,9 @@
 EXPORT_SYMBOL(kmap_prot);
 EXPORT_SYMBOL(kmap_pte);
 #endif
+#ifdef HASHED_PAGE_VIRTUAL
+EXPORT_SYMBOL(page_address);
+#endif
 EXPORT_SYMBOL(get_user_pages);
 
 /* filesystem internal functions */
@@ -192,6 +193,7 @@
 EXPORT_SYMBOL(invalidate_inodes);
 EXPORT_SYMBOL(invalidate_device);
 EXPORT_SYMBOL(invalidate_inode_pages);
+EXPORT_SYMBOL_GPL(invalidate_inode_pages2);
 EXPORT_SYMBOL(truncate_inode_pages);
 EXPORT_SYMBOL(fsync_bdev);
 EXPORT_SYMBOL(permission);
@@ -517,9 +519,6 @@
 EXPORT_SYMBOL(simple_strtol);
 EXPORT_SYMBOL(system_utsname);	/* UTS data */
 EXPORT_SYMBOL(uts_sem);		/* UTS semaphore */
-#ifndef __mips__
-EXPORT_SYMBOL(sys_call_table);
-#endif
 EXPORT_SYMBOL(machine_restart);
 EXPORT_SYMBOL(machine_halt);
 EXPORT_SYMBOL(machine_power_off);
diff -Nru a/kernel/sched.c b/kernel/sched.c
--- a/kernel/sched.c	Mon Oct  7 11:25:36 2002
+++ b/kernel/sched.c	Mon Oct  7 11:25:36 2002
@@ -28,6 +28,7 @@
 #include <linux/kernel_stat.h>
 #include <linux/security.h>
 #include <linux/notifier.h>
+#include <linux/blkdev.h>
 #include <linux/delay.h>
 #include <linux/timer.h>
 
@@ -854,6 +855,9 @@
 /*
  * This function gets called by the timer code, with HZ frequency.
  * We call it with interrupts disabled.
+ *
+ * It also gets called by the fork code, when changing the parent's
+ * timeslices.
  */
 void scheduler_tick(int user_ticks, int sys_ticks)
 {
@@ -861,11 +865,14 @@
 	runqueue_t *rq = this_rq();
 	task_t *p = current;
 
-	run_local_timers();
 	if (p == rq->idle) {
 		/* note: this timer irq context must be accounted for as well */
 		if (irq_count() - HARDIRQ_OFFSET >= SOFTIRQ_OFFSET)
 			kstat.per_cpu_system[cpu] += sys_ticks;
+		else if (atomic_read(&nr_iowait_tasks) > 0)
+			kstat.per_cpu_iowait[cpu] += sys_ticks;
+		else
+			kstat.per_cpu_idle[cpu] += sys_ticks;
 #if CONFIG_SMP
 		idle_tick(rq);
 #endif
@@ -2103,8 +2110,6 @@
  */
 spinlock_t kernel_flag __cacheline_aligned_in_smp = SPIN_LOCK_UNLOCKED;
 #endif
-
-extern void init_timers(void);
 
 void __init sched_init(void)
 {
diff -Nru a/kernel/signal.c b/kernel/signal.c
--- a/kernel/signal.c	Mon Oct  7 11:25:32 2002
+++ b/kernel/signal.c	Mon Oct  7 11:25:32 2002
@@ -405,14 +405,8 @@
 	return 0;
 }
 
-/*
- * Dequeue a signal and return the element to the caller, which is 
- * expected to free it.
- *
- * All callers have to hold the siglock.
- */
-
-int dequeue_signal(struct sigpending *pending, sigset_t *mask, siginfo_t *info)
+static int __dequeue_signal(struct sigpending *pending, sigset_t *mask,
+			siginfo_t *info)
 {
 	int sig = 0;
 
@@ -438,6 +432,27 @@
 	return sig;
 }
 
+/*
+ * Dequeue a signal and return the element to the caller, which is 
+ * expected to free it.
+ *
+ * All callers have to hold the siglock.
+ */
+int dequeue_signal(sigset_t *mask, siginfo_t *info)
+{
+	/*
+	 * Here we handle shared pending signals. To implement the full
+	 * semantics we need to unqueue and resend them. It will likely
+	 * get into our own pending queue.
+	 */
+	if (current->sig->shared_pending.head) {
+		int signr = __dequeue_signal(&current->sig->shared_pending, mask, info);
+		if (signr)
+			__send_sig_info(signr, info, current);
+	}
+	return __dequeue_signal(&current->pending, mask, info);
+}
+
 static int rm_from_queue(int sig, struct sigpending *s)
 {
 	struct sigqueue *q, **pp;
@@ -843,8 +858,7 @@
 	struct pid *pid;
 
 	for_each_task_pid(p->tgid, PIDTYPE_TGID, tmp, l, pid)
-		if (!sigismember(&tmp->blocked, signr) &&
-					!sigismember(&tmp->real_blocked, signr))
+		if (!sigismember(&tmp->blocked, signr))
 			return tmp;
 	return NULL;
 }
@@ -887,6 +901,10 @@
 		ret = specific_send_sig_info(sig, info, p, 1);
 		goto out_unlock;
 	}
+	if (sigismember(&t->real_blocked,sig)) {
+		ret = specific_send_sig_info(sig, info, t, 0);
+		goto out_unlock;
+	}
 	if (sig_kernel_broadcast(sig) || sig_kernel_coredump(sig)) {
 		ret = __broadcast_thread_group(p, sig);
 		goto out_unlock;
@@ -1169,10 +1187,7 @@
 		struct k_sigaction *ka;
 
 		spin_lock_irq(&current->sig->siglock);
-		if (current->sig->shared_pending.head)
-			signr = dequeue_signal(&current->sig->shared_pending, mask, info);
-		if (!signr)
-			signr = dequeue_signal(&current->pending, mask, info);
+		signr = dequeue_signal(mask, info);
 		spin_unlock_irq(&current->sig->siglock);
 
 		if (!signr)
@@ -1268,7 +1283,7 @@
 #endif
 
 EXPORT_SYMBOL(recalc_sigpending);
-EXPORT_SYMBOL(dequeue_signal);
+EXPORT_SYMBOL_GPL(dequeue_signal);
 EXPORT_SYMBOL(flush_signals);
 EXPORT_SYMBOL(force_sig);
 EXPORT_SYMBOL(force_sig_info);
@@ -1469,9 +1484,7 @@
 	}
 
 	spin_lock_irq(&current->sig->siglock);
-	sig = dequeue_signal(&current->sig->shared_pending, &these, &info);
-	if (!sig)
-		sig = dequeue_signal(&current->pending, &these, &info);
+	sig = dequeue_signal(&these, &info);
 	if (!sig) {
 		timeout = MAX_SCHEDULE_TIMEOUT;
 		if (uts)
@@ -1491,9 +1504,7 @@
 			timeout = schedule_timeout(timeout);
 
 			spin_lock_irq(&current->sig->siglock);
-			sig = dequeue_signal(&current->sig->shared_pending, &these, &info);
-			if (!sig)
-				sig = dequeue_signal(&current->pending, &these, &info);
+			sig = dequeue_signal(&these, &info);
 			current->blocked = current->real_blocked;
 			siginitset(&current->real_blocked, 0);
 			recalc_sigpending();
diff -Nru a/kernel/suspend.c b/kernel/suspend.c
--- a/kernel/suspend.c	Mon Oct  7 11:25:28 2002
+++ b/kernel/suspend.c	Mon Oct  7 11:25:28 2002
@@ -470,19 +470,22 @@
 	int nr_copy_pages = 0;
 	int pfn;
 	struct page *page;
-
-#ifndef CONFIG_DISCONTIGMEM	
-	if (max_mapnr != num_physpages)
-		panic("mapnr is not expected");
+	
+#ifdef CONFIG_DISCONTIGMEM
+	panic("Discontingmem not supported");
+#else
+	BUG_ON (max_mapnr != num_physpages);
 #endif
-	for (pfn = 0; pfn < num_physpages; pfn++) {
+	for (pfn = 0; pfn < max_mapnr; pfn++) {
 		page = pfn_to_page(pfn);
 		if (PageHighMem(page))
 			panic("Swsusp not supported on highmem boxes. Send 1GB of RAM to <pavel@ucw.cz> and try again ;-).");
+
 		if (!PageReserved(page)) {
 			if (PageNosave(page))
 				continue;
 
+
 			if ((chunk_size=is_head_of_free_region(page))!=0) {
 				pfn += chunk_size - 1;
 				continue;
@@ -776,9 +779,10 @@
 	BUG_ON (nr_copy_pages_check != nr_copy_pages);
 	BUG_ON (pagedir_order_check != pagedir_order);
 
+	__flush_tlb_global();		/* Even mappings of "global" things (vmalloc) need to be fixed */
+
 	PRINTK( "Freeing prev allocated pagedir\n" );
 	free_suspend_pagedir((unsigned long) pagedir_save);
-	__flush_tlb_global();		/* Even mappings of "global" things (vmalloc) need to be fixed */
 	drivers_resume(RESUME_ALL_PHASES);
 	spin_unlock_irq(&suspend_pagedir_lock);
 
@@ -809,12 +813,10 @@
 
 	barrier();
 	mb();
-	drivers_resume(RESUME_PHASE2);
 	spin_lock_irq(&suspend_pagedir_lock);	/* Done to disable interrupts */ 
 	mdelay(1000);
 
 	free_pages((unsigned long) pagedir_nosave, pagedir_order);
-	drivers_resume(RESUME_PHASE1);
 	spin_unlock_irq(&suspend_pagedir_lock);
 	mark_swapfiles(((swp_entry_t) {0}), MARK_SWAP_RESUME);
 	PRINTK(KERN_WARNING "%sLeaving do_magic_suspend_2...\n", name_suspend);	
@@ -1037,6 +1039,7 @@
 	return 0;
 #endif
 	printk(KERN_CRIT "%sWarning %s: Fixing swap signatures unimplemented...\n", name_resume, resume_file);
+	return 0;
 }
 
 extern kdev_t __init name_to_kdev_t(const char *line);
diff -Nru a/kernel/sys.c b/kernel/sys.c
--- a/kernel/sys.c	Mon Oct  7 11:25:25 2002
+++ b/kernel/sys.c	Mon Oct  7 11:25:25 2002
@@ -16,7 +16,7 @@
 #include <linux/init.h>
 #include <linux/highuid.h>
 #include <linux/fs.h>
-#include <linux/tqueue.h>
+#include <linux/workqueue.h>
 #include <linux/device.h>
 #include <linux/times.h>
 #include <linux/security.h>
@@ -442,12 +442,10 @@
  */
 void ctrl_alt_del(void)
 {
-	static struct tq_struct cad_tq = {
-		.routine = deferred_cad,
-	};
+	static DECLARE_WORK(cad_work, deferred_cad, NULL);
 
 	if (C_A_D)
-		schedule_task(&cad_tq);
+		schedule_work(&cad_work);
 	else
 		kill_proc(cad_pid, SIGINT, 1);
 }
diff -Nru a/kernel/timer.c b/kernel/timer.c
--- a/kernel/timer.c	Mon Oct  7 11:25:29 2002
+++ b/kernel/timer.c	Mon Oct  7 11:25:29 2002
@@ -47,10 +47,12 @@
 	struct list_head vec[TVR_SIZE];
 } tvec_root_t;
 
+typedef struct timer_list timer_t;
+
 struct tvec_t_base_s {
 	spinlock_t lock;
 	unsigned long timer_jiffies;
-	volatile timer_t * volatile running_timer;
+	timer_t *running_timer;
 	tvec_root_t tv1;
 	tvec_t tv2;
 	tvec_t tv3;
@@ -69,7 +71,7 @@
 {
 	unsigned long expires = timer->expires;
 	unsigned long idx = expires - base->timer_jiffies;
-	struct list_head * vec;
+	struct list_head *vec;
 
 	if (idx < TVR_SIZE) {
 		int i = expires & TVR_MASK;
@@ -94,22 +96,36 @@
 		vec = base->tv5.vec + i;
 	} else {
 		/* Can only get here on architectures with 64-bit jiffies */
-		INIT_LIST_HEAD(&timer->list);
+		INIT_LIST_HEAD(&timer->entry);
 		return;
 	}
 	/*
 	 * Timers are FIFO:
 	 */
-	list_add_tail(&timer->list, vec);
+	list_add_tail(&timer->entry, vec);
 }
 
+/***
+ * add_timer - start a timer
+ * @timer: the timer to be added
+ *
+ * The kernel will do a ->function(->data) callback from the
+ * timer interrupt at the ->expired point in the future. The
+ * current time is 'jiffies'.
+ *
+ * The timer's ->expired, ->function (and if the handler uses it, ->data)
+ * fields must be set prior calling this function.
+ *
+ * Timers with an ->expired field in the past will be executed in the next
+ * timer tick. It's illegal to add an already pending timer.
+ */
 void add_timer(timer_t *timer)
 {
 	int cpu = get_cpu();
 	tvec_base_t *base = tvec_bases + cpu;
   	unsigned long flags;
   
-  	BUG_ON(timer_pending(timer));
+  	BUG_ON(timer_pending(timer) || !timer->function);
 
 	spin_lock_irqsave(&base->lock, flags);
 	internal_add_timer(base, timer);
@@ -118,25 +134,38 @@
 	put_cpu();
 }
 
-static inline int detach_timer (timer_t *timer)
-{
-	if (!timer_pending(timer))
-		return 0;
-	list_del(&timer->list);
-	return 1;
-}
-
-/*
- * mod_timer() has subtle locking semantics because parallel
- * calls to it must happen serialized.
+/***
+ * mod_timer - modify a timer's timeout
+ * @timer: the timer to be modified
+ *
+ * mod_timer is a more efficient way to update the expire field of an
+ * active timer (if the timer is inactive it will be activated)
+ *
+ * mod_timer(timer, expires) is equivalent to:
+ *
+ *     del_timer(timer); timer->expires = expires; add_timer(timer);
+ *
+ * Note that if there are multiple unserialized concurrent users of the
+ * same timer, then mod_timer() is the only safe way to modify the timeout,
+ * since add_timer() cannot modify an already running timer.
+ *
+ * The function returns whether it has modified a pending timer or not.
+ * (ie. mod_timer() of an inactive timer returns 0, mod_timer() of an
+ * active timer returns 1.)
  */
 int mod_timer(timer_t *timer, unsigned long expires)
 {
 	tvec_base_t *old_base, *new_base;
 	unsigned long flags;
-	int ret;
+	int ret = 0;
 
-	if (timer_pending(timer) && timer->expires == expires)
+	BUG_ON(!timer->function);
+	/*
+	 * This is a common optimization triggered by the
+	 * networking code - if the timer is re-modified
+	 * to be the same thing then just return:
+	 */
+	if (timer->expires == expires && timer_pending(timer))
 		return 1;
 
 	local_irq_save(flags);
@@ -156,8 +185,8 @@
 			spin_lock(&new_base->lock);
 		}
 		/*
-		 * Subtle, we rely on timer->base being always
-		 * valid and being updated atomically.
+		 * The timer base might have changed while we were
+		 * trying to take the lock(s):
 		 */
 		if (timer->base != old_base) {
 			spin_unlock(&new_base->lock);
@@ -167,8 +196,15 @@
 	} else
 		spin_lock(&new_base->lock);
 
+	/*
+	 * Delete the previous timeout (if there was any), and install
+	 * the new one:
+	 */
+	if (old_base) {
+		list_del(&timer->entry);
+		ret = 1;
+	}
 	timer->expires = expires;
-	ret = detach_timer(timer);
 	internal_add_timer(new_base, timer);
 	timer->base = new_base;
 
@@ -179,66 +215,74 @@
 	return ret;
 }
 
-int del_timer(timer_t * timer)
+/***
+ * del_timer - deactive a timer.
+ * @timer: the timer to be deactivated
+ *
+ * del_timer() deactivates a timer - this works on both active and inactive
+ * timers.
+ *
+ * The function returns whether it has deactivated a pending timer or not.
+ * (ie. del_timer() of an inactive timer returns 0, del_timer() of an
+ * active timer returns 1.)
+ */
+int del_timer(timer_t *timer)
 {
 	unsigned long flags;
-	tvec_base_t * base;
-	int ret;
+	tvec_base_t *base;
 
-	if (!timer->base)
-		return 0;
 repeat:
  	base = timer->base;
+	if (!base)
+		return 0;
 	spin_lock_irqsave(&base->lock, flags);
 	if (base != timer->base) {
 		spin_unlock_irqrestore(&base->lock, flags);
 		goto repeat;
 	}
-	ret = detach_timer(timer);
-	timer->list.next = timer->list.prev = NULL;
+	list_del(&timer->entry);
+	timer->base = NULL;
 	spin_unlock_irqrestore(&base->lock, flags);
 
-	return ret;
+	return 1;
 }
 
 #ifdef CONFIG_SMP
-/*
- * SMP specific function to delete periodic timer.
- * Caller must disable by some means restarting the timer
- * for new. Upon exit the timer is not queued and handler is not running
- * on any CPU. It returns number of times, which timer was deleted
- * (for reference counting).
+/***
+ * del_timer_sync - deactivate a timer and wait for the handler to finish.
+ * @timer: the timer to be deactivated
+ *
+ * This function only differs from del_timer() on SMP: besides deactivating
+ * the timer it also makes sure the handler has finished executing on other
+ * CPUs.
+ *
+ * Synchronization rules: callers must prevent restarting of the timer,
+ * otherwise this function is meaningless. It must not be called from
+ * interrupt contexts. Upon exit the timer is not queued and the handler
+ * is not running on any CPU.
+ *
+ * The function returns whether it has deactivated a pending timer or not.
  */
-
-int del_timer_sync(timer_t * timer)
+int del_timer_sync(timer_t *timer)
 {
-	tvec_base_t * base;
-	int ret = 0;
-
-	if (!timer->base)
-		return 0;
-	for (;;) {
-		unsigned long flags;
-		int running;
+	tvec_base_t *base = tvec_bases;
+	int i, ret;
 
-repeat:
-	 	base = timer->base;
-		spin_lock_irqsave(&base->lock, flags);
-		if (base != timer->base) {
-			spin_unlock_irqrestore(&base->lock, flags);
-			goto repeat;
-		}
-		ret += detach_timer(timer);
-		timer->list.next = timer->list.prev = 0;
-		running = timer_is_running(base, timer);
-		spin_unlock_irqrestore(&base->lock, flags);
+	ret = del_timer(timer);
 
-		if (!running)
+	for (i = 0; i < NR_CPUS; i++) {
+		if (!cpu_online(i))
+			continue;
+		if (base->running_timer == timer) {
+			while (base->running_timer == timer) {
+				cpu_relax();
+				preempt_disable();
+				preempt_enable();
+			}
 			break;
-
-		timer_synchronize(base, timer);
+		}
+		base++;
 	}
-
 	return ret;
 }
 #endif
@@ -258,11 +302,10 @@
 	while (curr != head) {
 		timer_t *tmp;
 
-		tmp = list_entry(curr, timer_t, list);
+		tmp = list_entry(curr, timer_t, entry);
 		if (tmp->base != base)
 			BUG();
 		next = curr->next;
-		list_del(curr); // not needed
 		internal_add_timer(base, tmp);
 		curr = next;
 	}
@@ -270,7 +313,14 @@
 	tv->index = (tv->index + 1) & TVN_MASK;
 }
 
-static void __run_timers(tvec_base_t *base)
+/***
+ * __run_timers - run all expired timers (if any) on this CPU.
+ * @base: the timer vector to be processed.
+ *
+ * This function cascades all vectors and executes all expired timer
+ * vectors.
+ */
+static inline void __run_timers(tvec_base_t *base)
 {
 	unsigned long flags;
 
@@ -300,22 +350,26 @@
 			unsigned long data;
 			timer_t *timer;
 
-			timer = list_entry(curr, timer_t, list);
+			timer = list_entry(curr, timer_t, entry);
  			fn = timer->function;
  			data = timer->data;
 
-			detach_timer(timer);
-			timer->list.next = timer->list.prev = NULL;
-			timer_enter(base, timer);
+			list_del(&timer->entry);
+			timer->base = NULL;
+#if CONFIG_SMP
+			base->running_timer = timer;
+#endif
 			spin_unlock_irq(&base->lock);
 			fn(data);
 			spin_lock_irq(&base->lock);
-			timer_exit(base);
 			goto repeat;
 		}
 		++base->timer_jiffies; 
 		base->tv1.index = (base->tv1.index + 1) & TVR_MASK;
 	}
+#if CONFIG_SMP
+	base->running_timer = NULL;
+#endif
 	spin_unlock_irqrestore(&base->lock, flags);
 }
 
@@ -607,6 +661,7 @@
 	int cpu = smp_processor_id(), system = user_tick ^ 1;
 
 	update_one_process(p, user_tick, system, cpu);
+	run_local_timers();
 	scheduler_tick(user_tick, system);
 }
 
diff -Nru a/kernel/workqueue.c b/kernel/workqueue.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/kernel/workqueue.c	Mon Oct  7 11:26:06 2002
@@ -0,0 +1,389 @@
+/*
+ * linux/kernel/workqueue.c
+ *
+ * Generic mechanism for defining kernel helper threads for running
+ * arbitrary tasks in process context.
+ *
+ * Started by Ingo Molnar, Copyright (C) 2002
+ *
+ * Derived from the taskqueue/keventd code by:
+ *
+ *   David Woodhouse <dwmw2@redhat.com>
+ *   Andrew Morton <andrewm@uow.edu.au>
+ *   Kai Petzke <wpp@marie.physik.tu-berlin.de>
+ *   Theodore Ts'o <tytso@mit.edu>
+ */
+
+#define __KERNEL_SYSCALLS__
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/init.h>
+#include <linux/unistd.h>
+#include <linux/signal.h>
+#include <linux/completion.h>
+#include <linux/workqueue.h>
+#include <linux/slab.h>
+
+/*
+ * The per-CPU workqueue:
+ */
+struct cpu_workqueue_struct {
+
+	spinlock_t lock;
+
+	atomic_t nr_queued;
+	struct list_head worklist;
+	wait_queue_head_t more_work;
+	wait_queue_head_t work_done;
+
+	struct workqueue_struct *wq;
+	task_t *thread;
+	struct completion exit;
+
+} ____cacheline_aligned;
+
+/*
+ * The externally visible workqueue abstraction is an array of
+ * per-CPU workqueues:
+ */
+struct workqueue_struct {
+	struct cpu_workqueue_struct cpu_wq[NR_CPUS];
+};
+
+/*
+ * Queue work on a workqueue. Return non-zero if it was successfully
+ * added.
+ *
+ * We queue the work to the CPU it was submitted, but there is no
+ * guarantee that it will be processed by that CPU.
+ */
+int queue_work(struct workqueue_struct *wq, struct work_struct *work)
+{
+	unsigned long flags;
+	int ret = 0, cpu = get_cpu();
+	struct cpu_workqueue_struct *cwq = wq->cpu_wq + cpu;
+
+	if (!test_and_set_bit(0, &work->pending)) {
+		BUG_ON(!list_empty(&work->entry));
+		work->wq_data = cwq;
+
+		spin_lock_irqsave(&cwq->lock, flags);
+		list_add_tail(&work->entry, &cwq->worklist);
+		atomic_inc(&cwq->nr_queued);
+		spin_unlock_irqrestore(&cwq->lock, flags);
+
+		wake_up(&cwq->more_work);
+		ret = 1;
+	}
+	put_cpu();
+	return ret;
+}
+
+static void delayed_work_timer_fn(unsigned long __data)
+{
+	struct work_struct *work = (struct work_struct *)__data;
+	struct cpu_workqueue_struct *cwq = work->wq_data;
+	unsigned long flags;
+
+	/*
+	 * Do the wakeup within the spinlock, so that flushing
+	 * can be done in a guaranteed way.
+	 */
+	spin_lock_irqsave(&cwq->lock, flags);
+	list_add_tail(&work->entry, &cwq->worklist);
+	wake_up(&cwq->more_work);
+	spin_unlock_irqrestore(&cwq->lock, flags);
+}
+
+int queue_delayed_work(struct workqueue_struct *wq, struct work_struct *work, unsigned long delay)
+{
+	int ret = 0, cpu = get_cpu();
+	struct timer_list *timer = &work->timer;
+	struct cpu_workqueue_struct *cwq = wq->cpu_wq + cpu;
+
+	if (!test_and_set_bit(0, &work->pending)) {
+		BUG_ON(timer_pending(timer));
+		BUG_ON(!list_empty(&work->entry));
+
+		/*
+		 * Increase nr_queued so that the flush function
+		 * knows that there's something pending.
+		 */
+		atomic_inc(&cwq->nr_queued);
+		work->wq_data = cwq;
+
+		timer->expires = jiffies + delay;
+		timer->data = (unsigned long)work;
+		timer->function = delayed_work_timer_fn;
+		add_timer(timer);
+
+		ret = 1;
+	}
+	put_cpu();
+	return ret;
+}
+
+static inline void run_workqueue(struct cpu_workqueue_struct *cwq)
+{
+	unsigned long flags;
+
+	/*
+	 * Keep taking off work from the queue until
+	 * done.
+	 */
+	spin_lock_irqsave(&cwq->lock, flags);
+	while (!list_empty(&cwq->worklist)) {
+		struct work_struct *work = list_entry(cwq->worklist.next, struct work_struct, entry);
+		void (*f) (void *) = work->func;
+		void *data = work->data;
+
+		list_del_init(cwq->worklist.next);
+		spin_unlock_irqrestore(&cwq->lock, flags);
+
+		BUG_ON(work->wq_data != cwq);
+		clear_bit(0, &work->pending);
+		f(data);
+
+		/*
+		 * We only wake up 'work done' waiters (flush) when
+		 * the last function has been fully processed.
+		 */
+		if (atomic_dec_and_test(&cwq->nr_queued))
+			wake_up(&cwq->work_done);
+
+		spin_lock_irqsave(&cwq->lock, flags);
+	}
+	spin_unlock_irqrestore(&cwq->lock, flags);
+}
+
+typedef struct startup_s {
+	struct cpu_workqueue_struct *cwq;
+	struct completion done;
+	const char *name;
+} startup_t;
+
+static int worker_thread(void *__startup)
+{
+	startup_t *startup = __startup;
+	struct cpu_workqueue_struct *cwq = startup->cwq;
+	int cpu = cwq - cwq->wq->cpu_wq;
+	DECLARE_WAITQUEUE(wait, current);
+	struct k_sigaction sa;
+
+	daemonize();
+	sprintf(current->comm, "%s/%d", startup->name, cpu);
+	current->flags |= PF_IOTHREAD;
+	cwq->thread = current;
+
+	set_cpus_allowed(current, 1UL << cpu);
+
+	spin_lock_irq(&current->sig->siglock);
+	siginitsetinv(&current->blocked, sigmask(SIGCHLD));
+	recalc_sigpending();
+	spin_unlock_irq(&current->sig->siglock);
+
+	complete(&startup->done);
+
+	/* Install a handler so SIGCLD is delivered */
+	sa.sa.sa_handler = SIG_IGN;
+	sa.sa.sa_flags = 0;
+	siginitset(&sa.sa.sa_mask, sigmask(SIGCHLD));
+	do_sigaction(SIGCHLD, &sa, (struct k_sigaction *)0);
+
+	for (;;) {
+		set_task_state(current, TASK_INTERRUPTIBLE);
+
+		add_wait_queue(&cwq->more_work, &wait);
+		if (!cwq->thread)
+			break;
+		if (list_empty(&cwq->worklist))
+			schedule();
+		else
+			set_task_state(current, TASK_RUNNING);
+		remove_wait_queue(&cwq->more_work, &wait);
+
+		if (!list_empty(&cwq->worklist))
+			run_workqueue(cwq);
+
+		if (signal_pending(current)) {
+			while (waitpid(-1, NULL, __WALL|WNOHANG) > 0)
+				/* SIGCHLD - auto-reaping */ ;
+
+			/* zap all other signals */
+			spin_lock_irq(&current->sig->siglock);
+			flush_signals(current);
+			recalc_sigpending();
+			spin_unlock_irq(&current->sig->siglock);
+		}
+	}
+	remove_wait_queue(&cwq->more_work, &wait);
+	complete(&cwq->exit);
+
+	return 0;
+}
+
+/*
+ * flush_workqueue - ensure that any scheduled work has run to completion.
+ *
+ * Forces execution of the workqueue and blocks until its completion.
+ * This is typically used in driver shutdown handlers.
+ *
+ * NOTE: if work is being added to the queue constantly by some other
+ * context then this function might block indefinitely.
+ */
+void flush_workqueue(struct workqueue_struct *wq)
+{
+	struct cpu_workqueue_struct *cwq;
+	int cpu;
+
+	for (cpu = 0; cpu < NR_CPUS; cpu++) {
+		if (!cpu_online(cpu))
+			continue;
+		cwq = wq->cpu_wq + cpu;
+
+		if (atomic_read(&cwq->nr_queued)) {
+			DECLARE_WAITQUEUE(wait, current);
+
+			if (!list_empty(&cwq->worklist))
+				run_workqueue(cwq);
+
+			/*
+			 * Wait for helper thread(s) to finish up
+			 * the queue:
+			 */
+			set_task_state(current, TASK_INTERRUPTIBLE);
+			add_wait_queue(&cwq->work_done, &wait);
+			if (atomic_read(&cwq->nr_queued))
+				schedule();
+			else
+				set_task_state(current, TASK_RUNNING);
+			remove_wait_queue(&cwq->work_done, &wait);
+		}
+	}
+}
+
+struct workqueue_struct *create_workqueue(const char *name)
+{
+	int ret, cpu, destroy = 0;
+	struct cpu_workqueue_struct *cwq;
+	startup_t startup;
+	struct workqueue_struct *wq;
+
+	BUG_ON(strlen(name) > 10);
+	startup.name = name;
+
+	wq = kmalloc(sizeof(*wq), GFP_KERNEL);
+	if (!wq)
+		return NULL;
+
+	for (cpu = 0; cpu < NR_CPUS; cpu++) {
+		if (!cpu_online(cpu))
+			continue;
+		cwq = wq->cpu_wq + cpu;
+
+		spin_lock_init(&cwq->lock);
+		cwq->wq = wq;
+		cwq->thread = NULL;
+		atomic_set(&cwq->nr_queued, 0);
+		INIT_LIST_HEAD(&cwq->worklist);
+		init_waitqueue_head(&cwq->more_work);
+		init_waitqueue_head(&cwq->work_done);
+
+		init_completion(&startup.done);
+		startup.cwq = cwq;
+		ret = kernel_thread(worker_thread, &startup,
+						CLONE_FS | CLONE_FILES);
+		if (ret < 0)
+			destroy = 1;
+		else {
+			wait_for_completion(&startup.done);
+			BUG_ON(!cwq->thread);
+		}
+	}
+	/*
+	 * Was there any error during startup? If yes then clean up:
+	 */
+	if (destroy) {
+		destroy_workqueue(wq);
+		wq = NULL;
+	}
+	return wq;
+}
+
+void destroy_workqueue(struct workqueue_struct *wq)
+{
+	struct cpu_workqueue_struct *cwq;
+	int cpu;
+
+	flush_workqueue(wq);
+
+	for (cpu = 0; cpu < NR_CPUS; cpu++) {
+		if (!cpu_online(cpu))
+			continue;
+		cwq = wq->cpu_wq + cpu;
+		if (!cwq->thread)
+			continue;
+		/*
+		 * Initiate an exit and wait for it:
+		 */
+		init_completion(&cwq->exit);
+		cwq->thread = NULL;
+		wake_up(&cwq->more_work);
+
+		wait_for_completion(&cwq->exit);
+	}
+	kfree(wq);
+}
+
+static struct workqueue_struct *keventd_wq;
+
+int schedule_work(struct work_struct *work)
+{
+	return queue_work(keventd_wq, work);
+}
+
+int schedule_delayed_work(struct work_struct *work, unsigned long delay)
+{
+	return queue_delayed_work(keventd_wq, work, delay);
+}
+
+void flush_scheduled_work(void)
+{
+	flush_workqueue(keventd_wq);
+}
+
+int current_is_keventd(void)
+{
+	struct cpu_workqueue_struct *cwq;
+	int cpu;
+
+	BUG_ON(!keventd_wq);
+
+	for (cpu = 0; cpu < NR_CPUS; cpu++) {
+		if (!cpu_online(cpu))
+			continue;
+		cwq = keventd_wq->cpu_wq + cpu;
+		if (current == cwq->thread)
+			return 1;
+	}
+	return 0;
+}
+
+void init_workqueues(void)
+{
+	keventd_wq = create_workqueue("events");
+	BUG_ON(!keventd_wq);
+}
+
+EXPORT_SYMBOL_GPL(create_workqueue);
+EXPORT_SYMBOL_GPL(queue_work);
+EXPORT_SYMBOL_GPL(queue_delayed_work);
+EXPORT_SYMBOL_GPL(flush_workqueue);
+EXPORT_SYMBOL_GPL(destroy_workqueue);
+
+EXPORT_SYMBOL(schedule_work);
+EXPORT_SYMBOL(schedule_delayed_work);
+EXPORT_SYMBOL(flush_scheduled_work);
+
diff -Nru a/lib/radix-tree.c b/lib/radix-tree.c
--- a/lib/radix-tree.c	Mon Oct  7 11:25:27 2002
+++ b/lib/radix-tree.c	Mon Oct  7 11:25:27 2002
@@ -43,6 +43,7 @@
 };
 
 #define RADIX_TREE_INDEX_BITS  (8 /* CHAR_BIT */ * sizeof(unsigned long))
+#define RADIX_TREE_MAX_PATH (RADIX_TREE_INDEX_BITS/RADIX_TREE_MAP_SHIFT + 2)
 
 /*
  * Radix tree node cache.
@@ -218,9 +219,113 @@
 
 	return (void *) *slot;
 }
-
 EXPORT_SYMBOL(radix_tree_lookup);
 
+static /* inline */ unsigned int
+__lookup(struct radix_tree_root *root, void **results, unsigned long index,
+	unsigned int max_items, unsigned long *next_index,
+	unsigned long max_index)
+{
+	unsigned int nr_found = 0;
+	unsigned int shift;
+	unsigned int height = root->height;
+	struct radix_tree_node *slot;
+
+	shift = (height-1) * RADIX_TREE_MAP_SHIFT;
+	slot = root->rnode;
+
+	while (height > 0) {
+		unsigned long i = (index >> shift) & RADIX_TREE_MAP_MASK;
+		for ( ; i < RADIX_TREE_MAP_SIZE; i++) {
+			if (slot->slots[i] != NULL)
+				break;
+			index &= ~((1 << shift) - 1);
+			index += 1 << shift;
+		}
+		if (i == RADIX_TREE_MAP_SIZE)
+			goto out;
+		height--;
+		shift -= RADIX_TREE_MAP_SHIFT;
+		if (height == 0) {
+			/* Bottom level: grab some items */
+			unsigned long j;
+
+			BUG_ON((shift + RADIX_TREE_MAP_SHIFT) != 0);
+			
+			j = index & RADIX_TREE_MAP_MASK;
+			for ( ; j < RADIX_TREE_MAP_SIZE; j++) {
+				index++;
+				if (slot->slots[j]) {
+					results[nr_found++] = slot->slots[j];
+					if (nr_found == max_items)
+						goto out;
+				}
+			}
+		}
+		slot = slot->slots[i];
+	}
+out:
+	*next_index = index;
+	return nr_found;
+	
+}
+/**
+ *	radix_tree_gang_lookup - perform multiple lookup on a radix tree
+ *	@root:		radix tree root
+ *	@results:	where the results of the lookup are placed
+ *	@first_index:	start the lookup from this key
+ *	@max_items:	place up to this many items at *results
+ *
+ *	Performs an index-ascending scan of the tree for present items.  Places
+ *	them at *@results and returns the number of items which were placed at
+ *	*@results.
+ *
+ *	The implementation is naive.
+ */
+unsigned int
+radix_tree_gang_lookup(struct radix_tree_root *root, void **results,
+			unsigned long first_index, unsigned int max_items)
+{
+	const unsigned long max_index = radix_tree_maxindex(root->height);
+	unsigned long cur_index = first_index;
+	unsigned int ret = 0;
+
+	if (root->rnode == NULL)
+		goto out;
+	if (max_index == 0) {			/* Bah.  Special case */
+		if (first_index == 0) {
+			if (max_items > 0) {
+				*results = root->rnode;
+				ret = 1;
+			}
+		}
+		goto out;
+	}
+	while (ret < max_items) {
+		unsigned int nr_found;
+		unsigned long next_index;	/* Index of next search */
+
+		if (cur_index > max_index)
+			break;
+		nr_found = __lookup(root, results + ret, cur_index,
+				max_items - ret, &next_index, max_index);
+		if (nr_found == 0) {
+			 if (!(cur_index & RADIX_TREE_MAP_MASK))
+				break;
+			/*
+			 * It could be that there simply were no items to the
+			 * right of `cur_index' in the leaf node.  So we still
+			 * need to search for additional nodes to the right of
+			 * this one.
+			 */
+		}
+		ret += nr_found;
+		cur_index = next_index;
+	}
+out:
+	return ret;
+}
+EXPORT_SYMBOL(radix_tree_gang_lookup);
 
 /**
  *	radix_tree_delete    -    delete an item from a radix tree
@@ -231,7 +336,7 @@
  */
 int radix_tree_delete(struct radix_tree_root *root, unsigned long index)
 {
-	struct radix_tree_path path[RADIX_TREE_INDEX_BITS/RADIX_TREE_MAP_SHIFT + 2], *pathp = path;
+	struct radix_tree_path path[RADIX_TREE_MAX_PATH], *pathp = path;
 	unsigned int height, shift;
 
 	height = root->height;
diff -Nru a/mm/Makefile b/mm/Makefile
--- a/mm/Makefile	Mon Oct  7 11:25:36 2002
+++ b/mm/Makefile	Mon Oct  7 11:25:36 2002
@@ -9,6 +9,7 @@
 	    vmalloc.o slab.o bootmem.o swap.o vmscan.o page_io.o \
 	    page_alloc.o swap_state.o swapfile.o numa.o oom_kill.o \
 	    shmem.o highmem.o mempool.o msync.o mincore.o readahead.o \
-	    pdflush.o page-writeback.o rmap.o madvise.o vcache.o
+	    pdflush.o page-writeback.o rmap.o madvise.o vcache.o \
+	    truncate.o
 
 include $(TOPDIR)/Rules.make
diff -Nru a/mm/filemap.c b/mm/filemap.c
--- a/mm/filemap.c	Mon Oct  7 11:25:30 2002
+++ b/mm/filemap.c	Mon Oct  7 11:25:30 2002
@@ -24,6 +24,7 @@
 #include <linux/hash.h>
 #include <linux/writeback.h>
 #include <linux/pagevec.h>
+#include <linux/blkdev.h>
 #include <linux/security.h>
 /*
  * This is needed for the following functions:
@@ -51,7 +52,6 @@
  * SMP-threaded pagemap-LRU 1999, Andrea Arcangeli <andrea@suse.de>
  */
 
-
 /*
  * Lock ordering:
  *
@@ -73,10 +73,9 @@
 {
 	struct address_space *mapping = page->mapping;
 
-	if (unlikely(PageDirty(page)) && !PageSwapCache(page))
-		BUG();
+	BUG_ON(PageDirty(page) && !PageSwapCache(page));
 
-	radix_tree_delete(&page->mapping->page_tree, page->index);
+	radix_tree_delete(&mapping->page_tree, page->index);
 	list_del(&page->list);
 	page->mapping = NULL;
 
@@ -105,341 +104,6 @@
 	return 0;
 }
 
-/**
- * invalidate_inode_pages - Invalidate all the unlocked pages of one inode
- * @inode: the inode which pages we want to invalidate
- *
- * This function only removes the unlocked pages, if you want to
- * remove all the pages of one inode, you must call truncate_inode_pages.
- */
-
-void invalidate_inode_pages(struct inode * inode)
-{
-	struct list_head *head, *curr;
-	struct page * page;
-	struct address_space *mapping = inode->i_mapping;
-	struct pagevec pvec;
-
-	head = &mapping->clean_pages;
-	pagevec_init(&pvec);
-	write_lock(&mapping->page_lock);
-	curr = head->next;
-
-	while (curr != head) {
-		page = list_entry(curr, struct page, list);
-		curr = curr->next;
-
-		/* We cannot invalidate something in dirty.. */
-		if (PageDirty(page))
-			continue;
-
-		/* ..or locked */
-		if (TestSetPageLocked(page))
-			continue;
-
-		if (PagePrivate(page) && !try_to_release_page(page, 0))
-			goto unlock;
-
-		if (page_count(page) != 1)
-			goto unlock;
-
-		__remove_from_page_cache(page);
-		unlock_page(page);
-		if (!pagevec_add(&pvec, page))
-			__pagevec_release(&pvec);
-		continue;
-unlock:
-		unlock_page(page);
-		continue;
-	}
-
-	write_unlock(&mapping->page_lock);
-	pagevec_release(&pvec);
-}
-
-static int do_invalidatepage(struct page *page, unsigned long offset)
-{
-	int (*invalidatepage)(struct page *, unsigned long);
-	invalidatepage = page->mapping->a_ops->invalidatepage;
-	if (invalidatepage)
-		return (*invalidatepage)(page, offset);
-	return block_invalidatepage(page, offset);
-}
-
-static inline void truncate_partial_page(struct page *page, unsigned partial)
-{
-	memclear_highpage_flush(page, partial, PAGE_CACHE_SIZE-partial);
-	if (PagePrivate(page))
-		do_invalidatepage(page, partial);
-}
-
-/*
- * If truncate cannot remove the fs-private metadata from the page, the page
- * becomes anonymous.  It will be left on the LRU and may even be mapped into
- * user pagetables if we're racing with filemap_nopage().
- */
-static void truncate_complete_page(struct page *page)
-{
-	if (PagePrivate(page))
-		do_invalidatepage(page, 0);
-
-	clear_page_dirty(page);
-	ClearPageUptodate(page);
-	remove_from_page_cache(page);
-	page_cache_release(page);
-}
-
-/*
- * Writeback walks the page list in ->prev order, which is low-to-high file
- * offsets in the common case where he file was written linearly. So truncate
- * walks the page list in the opposite (->next) direction, to avoid getting
- * into lockstep with writeback's cursor.  To prune as many pages as possible
- * before the truncate cursor collides with the writeback cursor.
- */
-static int truncate_list_pages(struct address_space *mapping,
-	struct list_head *head, unsigned long start, unsigned *partial)
-{
-	struct list_head *curr;
-	struct page * page;
-	int unlocked = 0;
-	struct pagevec release_pvec;
-
-	pagevec_init(&release_pvec);
-restart:
-	curr = head->next;
-	while (curr != head) {
-		unsigned long offset;
-
-		page = list_entry(curr, struct page, list);
-		offset = page->index;
-
-		/* Is one of the pages to truncate? */
-		if ((offset >= start) || (*partial && (offset + 1) == start)) {
-			int failed;
-
-			page_cache_get(page);
-			failed = TestSetPageLocked(page);
-			if (!failed && PageWriteback(page)) {
-				unlock_page(page);
-				list_del(head);
-				list_add_tail(head, curr);
-				write_unlock(&mapping->page_lock);
-				wait_on_page_writeback(page);
-				if (!pagevec_add(&release_pvec, page))
-					__pagevec_release(&release_pvec);
-				unlocked = 1;
-				write_lock(&mapping->page_lock);
-				goto restart;
-			}
-
-			list_del(head);
-			if (!failed)		/* Restart after this page */
-				list_add(head, curr);
-			else			/* Restart on this page */
-				list_add_tail(head, curr);
-
-			write_unlock(&mapping->page_lock);
-			unlocked = 1;
-
- 			if (!failed) {
-				if (*partial && (offset + 1) == start) {
-					truncate_partial_page(page, *partial);
-					*partial = 0;
-				} else {
-					truncate_complete_page(page);
-				}
-				unlock_page(page);
-			} else {
- 				wait_on_page_locked(page);
-			}
-			if (!pagevec_add(&release_pvec, page))
-				__pagevec_release(&release_pvec);
-			cond_resched();
-			write_lock(&mapping->page_lock);
-			goto restart;
-		}
-		curr = curr->next;
-	}
-	if (pagevec_count(&release_pvec)) {
-		write_unlock(&mapping->page_lock);
-		pagevec_release(&release_pvec);
-		write_lock(&mapping->page_lock);
-		unlocked = 1;
-	}
-	return unlocked;
-}
-
-/*
- * Unconditionally clean all pages outside `start'.  The mapping lock
- * must be held.
- */
-static void clean_list_pages(struct address_space *mapping,
-		struct list_head *head, unsigned long start)
-{
-	struct page *page;
-	struct list_head *curr;
-
-	for (curr = head->next; curr != head; curr = curr->next) {
-		page = list_entry(curr, struct page, list);
-		if (page->index > start)
-			clear_page_dirty(page);
-	}
-}
-
-/**
- * truncate_inode_pages - truncate *all* the pages from an offset
- * @mapping: mapping to truncate
- * @lstart: offset from with to truncate
- *
- * Truncate the page cache at a set offset, removing the pages
- * that are beyond that offset (and zeroing out partial pages).
- * If any page is locked we wait for it to become unlocked.
- */
-void truncate_inode_pages(struct address_space * mapping, loff_t lstart) 
-{
-	unsigned long start = (lstart + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
-	unsigned partial = lstart & (PAGE_CACHE_SIZE - 1);
-	int unlocked;
-
-	write_lock(&mapping->page_lock);
-	clean_list_pages(mapping, &mapping->io_pages, start);
-	clean_list_pages(mapping, &mapping->dirty_pages, start);
-	do {
-		unlocked = truncate_list_pages(mapping,
-				&mapping->io_pages, start, &partial);
-		unlocked |= truncate_list_pages(mapping,
-				&mapping->dirty_pages, start, &partial);
-		unlocked |= truncate_list_pages(mapping,
-				&mapping->clean_pages, start, &partial);
-		unlocked |= truncate_list_pages(mapping,
-				&mapping->locked_pages, start, &partial);
-	} while (unlocked);
-	/* Traversed all three lists without dropping the lock */
-	write_unlock(&mapping->page_lock);
-}
-
-static inline int invalidate_this_page2(struct address_space * mapping,
-					struct page * page,
-					struct list_head * curr,
-					struct list_head * head)
-{
-	int unlocked = 1;
-
-	/*
-	 * The page is locked and we hold the mapping lock as well
-	 * so both page_count(page) and page_buffers stays constant here.
-	 * AKPM: fixme: No global lock any more.  Is this still OK?
-	 */
-	if (page_count(page) == 1 + !!page_has_buffers(page)) {
-		/* Restart after this page */
-		list_del(head);
-		list_add_tail(head, curr);
-
-		page_cache_get(page);
-		write_unlock(&mapping->page_lock);
-		truncate_complete_page(page);
-	} else {
-		if (page_has_buffers(page)) {
-			/* Restart after this page */
-			list_del(head);
-			list_add_tail(head, curr);
-
-			page_cache_get(page);
-			write_unlock(&mapping->page_lock);
-			do_invalidatepage(page, 0);
-		} else
-			unlocked = 0;
-
-		clear_page_dirty(page);
-		ClearPageUptodate(page);
-	}
-
-	return unlocked;
-}
-
-static int invalidate_list_pages2(struct address_space * mapping,
-				  struct list_head * head)
-{
-	struct list_head *curr;
-	struct page * page;
-	int unlocked = 0;
-	struct pagevec release_pvec;
-
-	pagevec_init(&release_pvec);
-restart:
-	curr = head->prev;
-	while (curr != head) {
-		page = list_entry(curr, struct page, list);
-
-		if (!TestSetPageLocked(page)) {
-			int __unlocked;
-
-			if (PageWriteback(page)) {
-				write_unlock(&mapping->page_lock);
-				wait_on_page_writeback(page);
-				unlocked = 1;
-				write_lock(&mapping->page_lock);
-				unlock_page(page);
-				goto restart;
-			}
-
-			__unlocked = invalidate_this_page2(mapping,
-						page, curr, head);
-			unlock_page(page);
-			unlocked |= __unlocked;
-			if (!__unlocked) {
-				curr = curr->prev;
-				continue;
-			}
-		} else {
-			/* Restart on this page */
-			list_del(head);
-			list_add(head, curr);
-
-			page_cache_get(page);
-			write_unlock(&mapping->page_lock);
-			unlocked = 1;
-			wait_on_page_locked(page);
-		}
-
-		if (!pagevec_add(&release_pvec, page))
-			__pagevec_release(&release_pvec);
-		cond_resched();
-		write_lock(&mapping->page_lock);
-		goto restart;
-	}
-	if (pagevec_count(&release_pvec)) {
-		write_unlock(&mapping->page_lock);
-		pagevec_release(&release_pvec);
-		write_lock(&mapping->page_lock);
-		unlocked = 1;
-	}
-	return unlocked;
-}
-
-/**
- * invalidate_inode_pages2 - Clear all the dirty bits around if it can't
- * free the pages because they're mapped.
- * @mapping: the address_space which pages we want to invalidate
- */
-void invalidate_inode_pages2(struct address_space *mapping)
-{
-	int unlocked;
-
-	write_lock(&mapping->page_lock);
-	do {
-		unlocked = invalidate_list_pages2(mapping,
-				&mapping->clean_pages);
-		unlocked |= invalidate_list_pages2(mapping,
-				&mapping->dirty_pages);
-		unlocked |= invalidate_list_pages2(mapping,
-				&mapping->io_pages);
-		unlocked |= invalidate_list_pages2(mapping,
-				&mapping->locked_pages);
-	} while (unlocked);
-	write_unlock(&mapping->page_lock);
-}
-
 /*
  * In-memory filesystems have to fail their
  * writepage function - and this has to be
@@ -564,7 +228,6 @@
 	if (!error) {
 		SetPageLocked(page);
 		___add_to_page_cache(page, mapping, offset);
-		ClearPageDirty(page);
 	} else {
 		page_cache_release(page);
 	}
@@ -638,7 +301,7 @@
 		prepare_to_wait(waitqueue, &wait, TASK_UNINTERRUPTIBLE);
 		sync_page(page);
 		if (test_bit(bit_nr, &page->flags))
-			schedule();
+			io_schedule();
 	} while (test_bit(bit_nr, &page->flags));
 	finish_wait(waitqueue, &wait);
 }
@@ -702,7 +365,7 @@
 		prepare_to_wait(wqh, &wait, TASK_UNINTERRUPTIBLE);
 		sync_page(page);
 		if (PageLocked(page))
-			schedule();
+			io_schedule();
 	}
 	finish_wait(wqh, &wait);
 }
@@ -824,6 +487,37 @@
 	return page;
 }
 
+/**
+ * find_get_pages - gang pagecache lookup
+ * @mapping:	The address_space to search
+ * @start:	The starting page index
+ * @nr_pages:	The maximum number of pages
+ * @pages:	Where the resulting pages are placed
+ *
+ * find_get_pages() will search for and return a group of up to
+ * @nr_pages pages in the mapping.  The pages are placed at @pages.
+ * find_get_pages() takes a reference against the returned pages.
+ *
+ * The search returns a group of mapping-contiguous pages with ascending
+ * indexes.  There may be holes in the indices due to not-present pages.
+ *
+ * find_get_pages() returns the number of pages which were found.
+ */
+unsigned int find_get_pages(struct address_space *mapping, pgoff_t start,
+			    unsigned int nr_pages, struct page **pages)
+{
+	unsigned int i;
+	unsigned int ret;
+
+	read_lock(&mapping->page_lock);
+	ret = radix_tree_gang_lookup(&mapping->page_tree,
+				(void **)pages, start, nr_pages);
+	for (i = 0; i < ret; i++)
+		page_cache_get(pages[i]);
+	read_unlock(&mapping->page_lock);
+	return ret;
+}
+
 /*
  * Same as grab_cache_page, but do not wait if the page is unavailable.
  * This is intended for speculative data generators, where the data can
@@ -1158,7 +852,7 @@
 				nr_segs = iov_shorten((struct iovec *)iov,
 							nr_segs, count);
 			}
-			retval = generic_file_direct_IO(READ, inode, 
+			retval = generic_file_direct_IO(READ, filp,
 					iov, pos, nr_segs);
 			if (retval > 0)
 				*ppos = pos + retval;
@@ -1403,7 +1097,7 @@
 	return NULL;
 
 page_not_uptodate:
-	KERNEL_STAT_INC(pgmajfault);
+	inc_page_state(pgmajfault);
 	lock_page(page);
 
 	/* Did it get unhashed while we waited for it? */
@@ -1840,7 +1534,7 @@
 		if (count != ocount)
 			nr_segs = iov_shorten((struct iovec *)iov,
 						nr_segs, count);
-		written = generic_file_direct_IO(WRITE, inode, 
+		written = generic_file_direct_IO(WRITE, file,
 					iov, pos, nr_segs);
 		if (written > 0) {
 			loff_t end = pos + written;
diff -Nru a/mm/highmem.c b/mm/highmem.c
--- a/mm/highmem.c	Mon Oct  7 11:25:36 2002
+++ b/mm/highmem.c	Mon Oct  7 11:25:36 2002
@@ -22,7 +22,9 @@
 #include <linux/mempool.h>
 #include <linux/blkdev.h>
 #include <linux/init.h>
+#include <linux/hash.h>
 #include <asm/pgalloc.h>
+#include <asm/tlbflush.h>
 
 static mempool_t *page_pool, *isa_page_pool;
 
@@ -88,7 +90,7 @@
 		page = pte_page(pkmap_page_table[i]);
 		pte_clear(&pkmap_page_table[i]);
 
-		page->virtual = NULL;
+		set_page_address(page, NULL);
 	}
 	flush_tlb_kernel_range(PKMAP_ADDR(0), PKMAP_ADDR(LAST_PKMAP));
 }
@@ -126,8 +128,8 @@
 			spin_lock(&kmap_lock);
 
 			/* Somebody else might have mapped it while we slept */
-			if (page->virtual)
-				return (unsigned long) page->virtual;
+			if (page_address(page))
+				return (unsigned long)page_address(page);
 
 			/* Re-start */
 			goto start;
@@ -137,7 +139,7 @@
 	set_pte(&(pkmap_page_table[last_pkmap_nr]), mk_pte(page, kmap_prot));
 
 	pkmap_count[last_pkmap_nr] = 1;
-	page->virtual = (void *) vaddr;
+	set_page_address(page, (void *)vaddr);
 
 	return vaddr;
 }
@@ -153,7 +155,7 @@
 	 * We cannot call this from interrupts, as it may block
 	 */
 	spin_lock(&kmap_lock);
-	vaddr = (unsigned long) page->virtual;
+	vaddr = (unsigned long)page_address(page);
 	if (!vaddr)
 		vaddr = map_new_virtual(page);
 	pkmap_count[PKMAP_NR(vaddr)]++;
@@ -170,7 +172,7 @@
 	int need_wakeup;
 
 	spin_lock(&kmap_lock);
-	vaddr = (unsigned long) page->virtual;
+	vaddr = (unsigned long)page_address(page);
 	if (!vaddr)
 		BUG();
 	nr = PKMAP_NR(vaddr);
@@ -467,7 +469,7 @@
 	*bio_orig = bio;
 }
 
-#if CONFIG_DEBUG_HIGHMEM
+#if defined(CONFIG_DEBUG_HIGHMEM) && defined(CONFIG_HIGHMEM)
 void check_highmem_ptes(void)
 {
 	int idx, type;
@@ -484,3 +486,121 @@
 }
 #endif
 
+#if defined(HASHED_PAGE_VIRTUAL)
+
+#define PA_HASH_ORDER	7
+
+/*
+ * Describes one page->virtual association
+ */
+struct page_address_map {
+	struct page *page;
+	void *virtual;
+	struct list_head list;
+};
+
+/*
+ * page_address_map freelist, allocated from page_address_maps.
+ */
+static struct list_head page_address_pool;	/* freelist */
+static spinlock_t pool_lock;			/* protects page_address_pool */
+
+/*
+ * Hash table bucket
+ */
+static struct page_address_slot {
+	struct list_head lh;			/* List of page_address_maps */
+	spinlock_t lock;			/* Protect this bucket's list */
+} ____cacheline_aligned_in_smp page_address_htable[1<<PA_HASH_ORDER];
+
+static struct page_address_slot *page_slot(struct page *page)
+{
+	return &page_address_htable[hash_ptr(page, PA_HASH_ORDER)];
+}
+
+void *page_address(struct page *page)
+{
+	unsigned long flags;
+	void *ret;
+	struct page_address_slot *pas;
+
+	if (!PageHighMem(page))
+		return lowmem_page_address(page);
+
+	pas = page_slot(page);
+	ret = NULL;
+	spin_lock_irqsave(&pas->lock, flags);
+	if (!list_empty(&pas->lh)) {
+		struct page_address_map *pam;
+
+		list_for_each_entry(pam, &pas->lh, list) {
+			if (pam->page == page) {
+				ret = pam->virtual;
+				goto done;
+			}
+		}
+	}
+done:
+	spin_unlock_irqrestore(&pas->lock, flags);
+	return ret;
+}
+
+void set_page_address(struct page *page, void *virtual)
+{
+	unsigned long flags;
+	struct page_address_slot *pas;
+	struct page_address_map *pam;
+
+	BUG_ON(!PageHighMem(page));
+
+	pas = page_slot(page);
+	if (virtual) {		/* Add */
+		BUG_ON(list_empty(&page_address_pool));
+
+		spin_lock_irqsave(&pool_lock, flags);
+		pam = list_entry(page_address_pool.next,
+				struct page_address_map, list);
+		list_del(&pam->list);
+		spin_unlock_irqrestore(&pool_lock, flags);
+
+		pam->page = page;
+		pam->virtual = virtual;
+
+		spin_lock_irqsave(&pas->lock, flags);
+		list_add_tail(&pam->list, &pas->lh);
+		spin_unlock_irqrestore(&pas->lock, flags);
+	} else {		/* Remove */
+		spin_lock_irqsave(&pas->lock, flags);
+		list_for_each_entry(pam, &pas->lh, list) {
+			if (pam->page == page) {
+				list_del(&pam->list);
+				spin_unlock_irqrestore(&pas->lock, flags);
+				spin_lock_irqsave(&pool_lock, flags);
+				list_add_tail(&pam->list, &page_address_pool);
+				spin_unlock_irqrestore(&pool_lock, flags);
+				goto done;
+			}
+		}
+		spin_unlock_irqrestore(&pas->lock, flags);
+	}
+done:
+	return;
+}
+
+static struct page_address_map page_address_maps[LAST_PKMAP];
+
+void __init page_address_init(void)
+{
+	int i;
+
+	INIT_LIST_HEAD(&page_address_pool);
+	for (i = 0; i < ARRAY_SIZE(page_address_maps); i++)
+		list_add(&page_address_maps[i].list, &page_address_pool);
+	for (i = 0; i < ARRAY_SIZE(page_address_htable); i++) {
+		INIT_LIST_HEAD(&page_address_htable[i].lh);
+		spin_lock_init(&page_address_htable[i].lock);
+	}
+	spin_lock_init(&pool_lock);
+}
+
+#endif	/* defined(CONFIG_HIGHMEM) && !defined(WANT_PAGE_VIRTUAL) */
diff -Nru a/mm/memory.c b/mm/memory.c
--- a/mm/memory.c	Mon Oct  7 11:25:35 2002
+++ b/mm/memory.c	Mon Oct  7 11:25:35 2002
@@ -1216,7 +1216,7 @@
 
 		/* Had to read the page from swap area: Major fault */
 		ret = VM_FAULT_MAJOR;
-		KERNEL_STAT_INC(pgmajfault);
+		inc_page_state(pgmajfault);
 	}
 
 	mark_page_accessed(page);
@@ -1461,7 +1461,7 @@
 	current->state = TASK_RUNNING;
 	pgd = pgd_offset(mm, address);
 
-	KERNEL_STAT_INC(pgfault);
+	inc_page_state(pgfault);
 	/*
 	 * We need the page table lock to synchronize with kswapd
 	 * and the SMP-safe atomic PTE updates.
diff -Nru a/mm/mempool.c b/mm/mempool.c
--- a/mm/mempool.c	Mon Oct  7 11:25:36 2002
+++ b/mm/mempool.c	Mon Oct  7 11:25:36 2002
@@ -12,6 +12,7 @@
 #include <linux/slab.h>
 #include <linux/module.h>
 #include <linux/mempool.h>
+#include <linux/blkdev.h>
 #include <linux/writeback.h>
 
 static void add_element(mempool_t *pool, void *element)
@@ -184,8 +185,7 @@
 {
 	void *element;
 	unsigned long flags;
-	int curr_nr;
-	DECLARE_WAITQUEUE(wait, current);
+	DEFINE_WAIT(wait);
 	int gfp_nowait = gfp_mask & ~(__GFP_WAIT | __GFP_IO);
 	int pf_flags = current->flags;
 
@@ -226,18 +226,10 @@
 
 	blk_run_queues();
 
-	add_wait_queue_exclusive(&pool->wait, &wait);
-	set_task_state(current, TASK_UNINTERRUPTIBLE);
-
-	spin_lock_irqsave(&pool->lock, flags);
-	curr_nr = pool->curr_nr;
-	spin_unlock_irqrestore(&pool->lock, flags);
-
-	if (!curr_nr)
-		schedule();
-
-	current->state = TASK_RUNNING;
-	remove_wait_queue(&pool->wait, &wait);
+	prepare_to_wait(&pool->wait, &wait, TASK_UNINTERRUPTIBLE);
+	if (!pool->curr_nr)
+		io_schedule();
+	finish_wait(&pool->wait, &wait);
 
 	goto repeat_alloc;
 }
diff -Nru a/mm/mmap.c b/mm/mmap.c
--- a/mm/mmap.c	Mon Oct  7 11:25:25 2002
+++ b/mm/mmap.c	Mon Oct  7 11:25:25 2002
@@ -1058,7 +1058,7 @@
 	if (new_below) {
 		new->vm_end = addr;
 		vma->vm_start = addr;
-		vma->vm_pgoff += ((addr - vma->vm_start) >> PAGE_SHIFT);
+		vma->vm_pgoff += ((addr - new->vm_start) >> PAGE_SHIFT);
 	} else {
 		vma->vm_end = addr;
 		new->vm_start = addr;
diff -Nru a/mm/mprotect.c b/mm/mprotect.c
--- a/mm/mprotect.c	Mon Oct  7 11:25:32 2002
+++ b/mm/mprotect.c	Mon Oct  7 11:25:32 2002
@@ -186,8 +186,10 @@
 		/*
 		 * Try to merge with the previous vma.
 		 */
-		if (mprotect_attempt_merge(vma, *pprev, end, newflags))
+		if (mprotect_attempt_merge(vma, *pprev, end, newflags)) {
+			vma = *pprev;
 			goto success;
+		}
 	} else {
 		error = split_vma(mm, vma, start, 1);
 		if (error)
diff -Nru a/mm/page-writeback.c b/mm/page-writeback.c
--- a/mm/page-writeback.c	Mon Oct  7 11:25:36 2002
+++ b/mm/page-writeback.c	Mon Oct  7 11:25:36 2002
@@ -41,10 +41,8 @@
  */
 static long ratelimit_pages = 32;
 
-/*
- * The total number of pages in the machine.
- */
-static long total_pages;
+static long total_pages;	/* The total number of pages in the machine. */
+static int dirty_exceeded;	/* Dirty mem may be over limit */
 
 /*
  * When balance_dirty_pages decides that the caller needs to perform some
@@ -60,16 +58,12 @@
 /* The following parameters are exported via /proc/sys/vm */
 
 /*
- * Dirty memory thresholds, in percentages
- */
-
-/*
- * Start background writeback (via pdflush) at this level
+ * Start background writeback (via pdflush) at this percentage
  */
 int dirty_background_ratio = 10;
 
 /*
- * The generator of dirty data starts async writeback at this level
+ * The generator of dirty data starts async writeback at this percentage
  */
 int dirty_async_ratio = 40;
 
@@ -80,7 +74,7 @@
 int dirty_writeback_centisecs = 5 * 100;
 
 /*
- * The longest amount of time for which data is allowed to remain dirty
+ * The longest number of centiseconds for which data is allowed to remain dirty
  */
 int dirty_expire_centisecs = 30 * 100;
 
@@ -90,22 +84,17 @@
 static void background_writeout(unsigned long _min_pages);
 
 /*
- * balance_dirty_pages() must be called by processes which are
- * generating dirty data.  It looks at the number of dirty pages
- * in the machine and either:
- *
- * - Starts background writeback or
- * - Causes the caller to perform async writeback or
- * - Causes the caller to perform synchronous writeback, then
- *   tells a pdflush thread to perform more writeback or
- * - Does nothing at all.
- *
- * balance_dirty_pages() can sleep.
+ * balance_dirty_pages() must be called by processes which are generating dirty
+ * data.  It looks at the number of dirty pages in the machine and will force
+ * the caller to perform writeback if the system is over `async_thresh'.
+ * If we're over `background_thresh' then pdflush is woken to perform some
+ * writeout.
  */
 void balance_dirty_pages(struct address_space *mapping)
 {
 	struct page_state ps;
-	long background_thresh, async_thresh;
+	long background_thresh;
+	long async_thresh;
 	unsigned long dirty_and_writeback;
 	struct backing_dev_info *bdi;
 
@@ -123,9 +112,13 @@
 			.older_than_this = NULL,
 			.nr_to_write	= sync_writeback_pages(),
 		};
-
+		if (!dirty_exceeded)
+			dirty_exceeded = 1;
 		writeback_inodes(&wbc);
 		get_page_state(&ps);
+	} else {
+		if (dirty_exceeded)
+			dirty_exceeded = 0;
 	}
 
 	if (!writeback_in_progress(bdi) && ps.nr_dirty > background_thresh)
@@ -141,17 +134,25 @@
  * which was newly dirtied.  The function will periodically check the system's
  * dirty state and will initiate writeback if needed.
  *
- * balance_dirty_pages_ratelimited() may sleep.
+ * On really big machines, get_page_state is expensive, so try to avoid calling
+ * it too often (ratelimiting).  But once we're over the dirty memory limit we
+ * decrease the ratelimiting by a lot, to prevent individual processes from
+ * overshooting the limit by (ratelimit_pages) each.
  */
 void balance_dirty_pages_ratelimited(struct address_space *mapping)
 {
 	static struct rate_limit_struct {
 		int count;
-	} ____cacheline_aligned ratelimits[NR_CPUS];
+	} ____cacheline_aligned_in_smp ratelimits[NR_CPUS];
 	int cpu;
+	long ratelimit;
+
+	ratelimit = ratelimit_pages;
+	if (dirty_exceeded)
+		ratelimit = 8;
 
 	cpu = get_cpu();
-	if (ratelimits[cpu].count++ >= ratelimit_pages) {
+	if (ratelimits[cpu].count++ >= ratelimit) {
 		ratelimits[cpu].count = 0;
 		put_cpu();
 		balance_dirty_pages(mapping);
diff -Nru a/mm/page_alloc.c b/mm/page_alloc.c
--- a/mm/page_alloc.c	Mon Oct  7 11:25:25 2002
+++ b/mm/page_alloc.c	Mon Oct  7 11:25:25 2002
@@ -13,7 +13,7 @@
  */
 
 #include <linux/config.h>
-#include <linux/kernel_stat.h>
+#include <linux/stddef.h>
 #include <linux/mm.h>
 #include <linux/swap.h>
 #include <linux/interrupt.h>
@@ -24,6 +24,7 @@
 #include <linux/suspend.h>
 #include <linux/pagevec.h>
 #include <linux/blkdev.h>
+#include <linux/slab.h>
 
 unsigned long totalram_pages;
 unsigned long totalhigh_pages;
@@ -47,7 +48,7 @@
  */
 static inline int bad_range(struct zone *zone, struct page *page)
 {
-	if (page_to_pfn(page) >= zone->zone_start_pfn + zone->size)
+	if (page_to_pfn(page) >= zone->zone_start_pfn + zone->spanned_pages)
 		return 1;
 	if (page_to_pfn(page) < zone->zone_start_pfn)
 		return 1;
@@ -86,7 +87,7 @@
 	struct page *base;
 	struct zone *zone;
 
-	KERNEL_STAT_ADD(pgfree, 1<<order);
+	mod_page_state(pgfree, 1<<order);
 
 	BUG_ON(PageLRU(page));
 	BUG_ON(PagePrivate(page));
@@ -324,7 +325,7 @@
 	if (gfp_mask & __GFP_WAIT)
 		might_sleep();
 
-	KERNEL_STAT_ADD(pgalloc, 1<<order);
+	mod_page_state(pgalloc, 1<<order);
 
 	zones = zonelist->zones;  /* the list of zones suitable for gfp_mask */
 	classzone = zones[0]; 
@@ -345,8 +346,6 @@
 		}
 	}
 
-	classzone->need_balance = 1;
-	mb();
 	/* we're somewhat low on memory, failed to find what we needed */
 	for (i = 0; zones[i] != NULL; i++) {
 		struct zone *z = zones[i];
@@ -397,7 +396,7 @@
 	if (!(gfp_mask & __GFP_WAIT))
 		goto nopage;
 
-	KERNEL_STAT_INC(allocstall);
+	inc_page_state(allocstall);
 	page = balance_classzone(classzone, gfp_mask, order, &freed);
 	if (page)
 		return page;
@@ -508,7 +507,7 @@
 		struct zone *zone;
 
 		for (zone = *zonep++; zone; zone = *zonep++) {
-			unsigned long size = zone->size;
+			unsigned long size = zone->present_pages;
 			unsigned long high = zone->pages_high;
 			if (size > high)
 				sum += size - high;
@@ -555,28 +554,39 @@
 struct page_state page_states[NR_CPUS] __cacheline_aligned;
 EXPORT_SYMBOL(page_states);
 
-void get_page_state(struct page_state *ret)
+void __get_page_state(struct page_state *ret, int nr)
 {
-	int pcpu;
+	int cpu;
 
 	memset(ret, 0, sizeof(*ret));
-	for (pcpu = 0; pcpu < NR_CPUS; pcpu++) {
-		struct page_state *ps;
+	for (cpu = 0; cpu < NR_CPUS; cpu++) {
+		unsigned long *in, *out, off;
 
-		if (!cpu_online(pcpu))
+		if (!cpu_online(cpu))
 			continue;
 
-		ps = &page_states[pcpu];
-		ret->nr_dirty += ps->nr_dirty;
-		ret->nr_writeback += ps->nr_writeback;
-		ret->nr_pagecache += ps->nr_pagecache;
-		ret->nr_page_table_pages += ps->nr_page_table_pages;
-		ret->nr_reverse_maps += ps->nr_reverse_maps;
-		ret->nr_mapped += ps->nr_mapped;
-		ret->nr_slab += ps->nr_slab;
+		in = (unsigned long *)(page_states + cpu);
+		out = (unsigned long *)ret;
+		for (off = 0; off < nr; off++)
+			*out++ += *in++;
 	}
 }
 
+void get_page_state(struct page_state *ret)
+{
+	int nr;
+
+	nr = offsetof(struct page_state, GET_PAGE_STATE_LAST);
+	nr /= sizeof(unsigned long);
+
+	__get_page_state(ret, nr + 1);
+}
+
+void get_full_page_state(struct page_state *ret)
+{
+	__get_page_state(ret, sizeof(*ret) / sizeof(unsigned long));
+}
+
 void get_zone_counts(unsigned long *active, unsigned long *inactive)
 {
 	struct zone *zone;
@@ -669,7 +679,7 @@
 			struct zone *zone = &pgdat->node_zones[type];
  			unsigned long nr, flags, order, total = 0;
 
-			if (!zone->size)
+			if (!zone->present_pages)
 				continue;
 
 			spin_lock_irqsave(&zone->lock, flags);
@@ -698,7 +708,7 @@
 		BUG();
 	case ZONE_HIGHMEM:
 		zone = pgdat->node_zones + ZONE_HIGHMEM;
-		if (zone->size) {
+		if (zone->present_pages) {
 #ifndef CONFIG_HIGHMEM
 			BUG();
 #endif
@@ -706,11 +716,11 @@
 		}
 	case ZONE_NORMAL:
 		zone = pgdat->node_zones + ZONE_NORMAL;
-		if (zone->size)
+		if (zone->present_pages)
 			zonelist->zones[j++] = zone;
 	case ZONE_DMA:
 		zone = pgdat->node_zones + ZONE_DMA;
-		if (zone->size)
+		if (zone->present_pages)
 			zonelist->zones[j++] = zone;
 	}
 
@@ -854,13 +864,13 @@
 			realsize -= zholes_size[j];
 
 		printk("  %s zone: %lu pages\n", zone_names[j], realsize);
-		zone->size = size;
+		zone->spanned_pages = size;
+		zone->present_pages = realsize;
 		zone->name = zone_names[j];
 		spin_lock_init(&zone->lock);
 		spin_lock_init(&zone->lru_lock);
 		zone->zone_pgdat = pgdat;
 		zone->free_pages = 0;
-		zone->need_balance = 0;
 		INIT_LIST_HEAD(&zone->active_list);
 		INIT_LIST_HEAD(&zone->inactive_list);
 		atomic_set(&zone->refill_counter, 0);
@@ -911,12 +921,15 @@
 			set_page_count(page, 0);
 			SetPageReserved(page);
 			INIT_LIST_HEAD(&page->list);
+#ifdef WANT_PAGE_VIRTUAL
 			if (j != ZONE_HIGHMEM)
 				/*
 				 * The shift left won't overflow because the
 				 * ZONE_NORMAL is below 4G.
 				 */
-				set_page_address(page, __va(zone_start_pfn << PAGE_SHIFT));
+				set_page_address(page,
+					__va(zone_start_pfn << PAGE_SHIFT));
+#endif
 			zone_start_pfn++;
 		}
 
@@ -1022,7 +1035,7 @@
 	int order;
 
 	for (zone = node_zones; zone - node_zones < MAX_NR_ZONES; ++zone) {
-		if (!zone->size)
+		if (!zone->present_pages)
 			continue;
 
 		spin_lock_irqsave(&zone->lock, flags);
@@ -1046,6 +1059,78 @@
 	.next	= frag_next,
 	.stop	= frag_stop,
 	.show	= frag_show,
+};
+
+static char *vmstat_text[] = {
+	"nr_dirty",
+	"nr_writeback",
+	"nr_pagecache",
+	"nr_page_table_pages",
+	"nr_reverse_maps",
+	"nr_mapped",
+	"nr_slab",
+
+	"pgpgin",
+	"pgpgout",
+	"pswpin",
+	"pswpout",
+	"pgalloc",
+	"pgfree",
+	"pgactivate",
+	"pgdeactivate",
+	"pgfault",
+	"pgmajfault",
+	"pgscan",
+	"pgrefill",
+	"pgsteal",
+	"kswapd_steal",
+	"pageoutrun",
+	"allocstall",
+};
+
+static void *vmstat_start(struct seq_file *m, loff_t *pos)
+{
+	struct page_state *ps;
+
+	if (*pos >= ARRAY_SIZE(vmstat_text))
+		return NULL;
+
+	ps = kmalloc(sizeof(*ps), GFP_KERNEL);
+	m->private = ps;
+	if (!ps)
+		return ERR_PTR(-ENOMEM);
+	get_full_page_state(ps);
+	return (unsigned long *)ps + *pos;
+}
+
+static void *vmstat_next(struct seq_file *m, void *arg, loff_t *pos)
+{
+	(*pos)++;
+	if (*pos >= ARRAY_SIZE(vmstat_text))
+		return NULL;
+	return (unsigned long *)m->private + *pos;
+}
+
+static int vmstat_show(struct seq_file *m, void *arg)
+{
+	unsigned long *l = arg;
+	unsigned long off = l - (unsigned long *)m->private;
+
+	seq_printf(m, "%s %lu\n", vmstat_text[off], *l);
+	return 0;
+}
+
+static void vmstat_stop(struct seq_file *m, void *arg)
+{
+	kfree(m->private);
+	m->private = NULL;
+}
+
+struct seq_operations vmstat_op = {
+	.start	= vmstat_start,
+	.next	= vmstat_next,
+	.stop	= vmstat_stop,
+	.show	= vmstat_show,
 };
 
 #endif /* CONFIG_PROC_FS */
diff -Nru a/mm/page_io.c b/mm/page_io.c
--- a/mm/page_io.c	Mon Oct  7 11:25:26 2002
+++ b/mm/page_io.c	Mon Oct  7 11:25:26 2002
@@ -100,7 +100,7 @@
 		ret = -ENOMEM;
 		goto out;
 	}
-	kstat.pswpout++;
+	inc_page_state(pswpout);
 	SetPageWriteback(page);
 	unlock_page(page);
 	submit_bio(WRITE, bio);
@@ -119,7 +119,7 @@
 		ret = -ENOMEM;
 		goto out;
 	}
-	kstat.pswpin++;
+	inc_page_state(pswpin);
 	submit_bio(READ, bio);
 out:
 	return ret;
diff -Nru a/mm/rmap.c b/mm/rmap.c
--- a/mm/rmap.c	Mon Oct  7 11:25:39 2002
+++ b/mm/rmap.c	Mon Oct  7 11:25:39 2002
@@ -274,11 +274,11 @@
 		BUG();
 	if (!pfn_valid(page_to_pfn(page)) || PageReserved(page))
 		return;
+	if (!page_mapped(page))
+		return;		/* remap_page_range() from a driver? */
 
 	pte_chain_lock(page);
 
-	BUG_ON(page->pte.direct == 0);
- 
 	if (PageDirect(page)) {
 		if (page->pte.direct == pte_paddr) {
 			page->pte.direct = 0;
diff -Nru a/mm/shmem.c b/mm/shmem.c
--- a/mm/shmem.c	Mon Oct  7 11:25:33 2002
+++ b/mm/shmem.c	Mon Oct  7 11:25:33 2002
@@ -37,12 +37,64 @@
 #define TMPFS_MAGIC	0x01021994
 
 #define ENTRIES_PER_PAGE (PAGE_CACHE_SIZE/sizeof(unsigned long))
+#define ENTRIES_PER_PAGEPAGE (ENTRIES_PER_PAGE*ENTRIES_PER_PAGE)
 #define BLOCKS_PER_PAGE  (PAGE_CACHE_SIZE/512)
 
-#define SHMEM_MAX_INDEX  (SHMEM_NR_DIRECT + ENTRIES_PER_PAGE * (ENTRIES_PER_PAGE/2) * (ENTRIES_PER_PAGE+1))
+#define SHMEM_MAX_INDEX  (SHMEM_NR_DIRECT + (ENTRIES_PER_PAGEPAGE/2) * (ENTRIES_PER_PAGE+1))
 #define SHMEM_MAX_BYTES  ((unsigned long long)SHMEM_MAX_INDEX << PAGE_CACHE_SHIFT)
 
-#define VM_ACCT(size)    (((size) + PAGE_CACHE_SIZE - 1) >> PAGE_SHIFT)
+#define VM_ACCT(size)    (PAGE_CACHE_ALIGN(size) >> PAGE_SHIFT)
+
+/* Pretend that each entry is of this size in directory's i_size */
+#define BOGO_DIRENT_SIZE 20
+
+/* Keep swapped page count in private field of indirect struct page */
+#define nr_swapped		private
+
+static inline struct page *shmem_dir_alloc(unsigned int gfp_mask)
+{
+	/*
+	 * The above definition of ENTRIES_PER_PAGE, and the use of
+	 * BLOCKS_PER_PAGE on indirect pages, assume PAGE_CACHE_SIZE:
+	 * might be reconsidered if it ever diverges from PAGE_SIZE.
+	 */
+	return alloc_pages(gfp_mask, PAGE_CACHE_SHIFT-PAGE_SHIFT);
+}
+
+static inline void shmem_dir_free(struct page *page)
+{
+	__free_pages(page, PAGE_CACHE_SHIFT-PAGE_SHIFT);
+}
+
+static struct page **shmem_dir_map(struct page *page)
+{
+	return (struct page **)kmap_atomic(page, KM_USER0);
+}
+
+static inline void shmem_dir_unmap(struct page **dir)
+{
+	kunmap_atomic(dir, KM_USER0);
+}
+
+static swp_entry_t *shmem_swp_map(struct page *page)
+{
+	/*
+	 * We have to avoid the unconditional inc_preempt_count()
+	 * in kmap_atomic(), since shmem_swp_unmap() will also be
+	 * applied to the low memory addresses within i_direct[].
+	 * PageHighMem and high_memory tests are good for all arches
+	 * and configs: highmem_start_page and FIXADDR_START are not.
+	 */
+	return PageHighMem(page)?
+		(swp_entry_t *)kmap_atomic(page, KM_USER1):
+		(swp_entry_t *)page_address(page);
+}
+
+static inline void shmem_swp_unmap(swp_entry_t *entry)
+{
+	if (entry >= (swp_entry_t *)high_memory)
+		kunmap_atomic(entry, KM_USER1);
+}
 
 static inline struct shmem_sb_info *SHMEM_SB(struct super_block *sb)
 {
@@ -65,40 +117,42 @@
 static spinlock_t shmem_ilock = SPIN_LOCK_UNLOCKED;
 atomic_t shmem_nrpages = ATOMIC_INIT(0); /* Not used right now */
 
-static struct page *shmem_getpage_locked(struct shmem_inode_info *, struct inode *, unsigned long);
+static void shmem_free_block(struct inode *inode)
+{
+	struct shmem_sb_info *sbinfo = SHMEM_SB(inode->i_sb);
+	spin_lock(&sbinfo->stat_lock);
+	sbinfo->free_blocks++;
+	inode->i_blocks -= BLOCKS_PER_PAGE;
+	spin_unlock(&sbinfo->stat_lock);
+}
 
 /*
  * shmem_recalc_inode - recalculate the size of an inode
  *
  * @inode: inode to recalc
- * @swap:  additional swap pages freed externally
  *
- * We have to calculate the free blocks since the mm can drop pages
- * behind our back
+ * We have to calculate the free blocks since the mm can drop
+ * undirtied hole pages behind our back.  Later we should be
+ * able to use the releasepage method to handle this better.
  *
- * But we know that normally
- * inodes->i_blocks/BLOCKS_PER_PAGE == 
- * 			inode->i_mapping->nrpages + info->swapped
- *
- * So the mm freed 
- * inodes->i_blocks/BLOCKS_PER_PAGE - 
- * 			(inode->i_mapping->nrpages + info->swapped)
+ * But normally   info->alloced == inode->i_mapping->nrpages + info->swapped
+ * So mm freed is info->alloced - (inode->i_mapping->nrpages + info->swapped)
  *
  * It has to be called with the spinlock held.
  */
-
-static void shmem_recalc_inode(struct inode * inode)
+static void shmem_recalc_inode(struct inode *inode)
 {
-	unsigned long freed;
+	struct shmem_inode_info *info = SHMEM_I(inode);
+	long freed;
 
-	freed = (inode->i_blocks/BLOCKS_PER_PAGE) -
-		(inode->i_mapping->nrpages + SHMEM_I(inode)->swapped);
-	if (freed){
-		struct shmem_sb_info * sbinfo = SHMEM_SB(inode->i_sb);
-		inode->i_blocks -= freed*BLOCKS_PER_PAGE;
-		spin_lock (&sbinfo->stat_lock);
+	freed = info->alloced - info->swapped - inode->i_mapping->nrpages;
+	if (freed > 0) {
+		struct shmem_sb_info *sbinfo = SHMEM_SB(inode->i_sb);
+		info->alloced -= freed;
+		spin_lock(&sbinfo->stat_lock);
 		sbinfo->free_blocks += freed;
-		spin_unlock (&sbinfo->stat_lock);
+		inode->i_blocks -= freed*BLOCKS_PER_PAGE;
+		spin_unlock(&sbinfo->stat_lock);
 	}
 }
 
@@ -110,11 +164,9 @@
  * @page:  optional page to add to the structure. Has to be preset to
  *         all zeros
  *
- * If there is no space allocated yet it will return -ENOMEM when
- * page == 0 else it will use the page for the needed block.
- *
- * returns -EFBIG if the index is too big.
- *
+ * If there is no space allocated yet it will return NULL when
+ * page is NULL, else it will use the page for the needed block,
+ * setting it to NULL on return to indicate that it has been used.
  *
  * The swap vector is organized the following way:
  *
@@ -142,233 +194,285 @@
  * 	      	       +-> 48-51
  * 	      	       +-> 52-55
  */
-static swp_entry_t * shmem_swp_entry (struct shmem_inode_info *info, unsigned long index, unsigned long page) 
+static swp_entry_t *shmem_swp_entry(struct shmem_inode_info *info, unsigned long index, struct page **page)
 {
 	unsigned long offset;
-	void **dir;
+	struct page **dir;
+	struct page *subdir;
 
+	if (index >= info->next_index)
+		return NULL;
 	if (index < SHMEM_NR_DIRECT)
 		return info->i_direct+index;
+	if (!info->i_indirect) {
+		if (page) {
+			info->i_indirect = *page;
+			*page = NULL;
+		}
+		return NULL;			/* need another page */
+	}
 
 	index -= SHMEM_NR_DIRECT;
 	offset = index % ENTRIES_PER_PAGE;
 	index /= ENTRIES_PER_PAGE;
+	dir = shmem_dir_map(info->i_indirect);
 
-	if (!info->i_indirect) {
-		info->i_indirect = (void *) page;
-		return ERR_PTR(-ENOMEM);
-	}
-
-	dir = info->i_indirect + index;
 	if (index >= ENTRIES_PER_PAGE/2) {
 		index -= ENTRIES_PER_PAGE/2;
-		dir = info->i_indirect + ENTRIES_PER_PAGE/2 
-			+ index/ENTRIES_PER_PAGE;
+		dir += ENTRIES_PER_PAGE/2 + index/ENTRIES_PER_PAGE;
 		index %= ENTRIES_PER_PAGE;
-
-		if(!*dir) {
-			*dir = (void *) page;
-			/* We return since we will need another page
-                           in the next step */
-			return ERR_PTR(-ENOMEM);
+		subdir = *dir;
+		if (!subdir) {
+			if (page) {
+				*dir = *page;
+				*page = NULL;
+			}
+			shmem_dir_unmap(dir);
+			return NULL;		/* need another page */
 		}
-		dir = ((void **)*dir) + index;
+		shmem_dir_unmap(dir);
+		dir = shmem_dir_map(subdir);
 	}
-	if (!*dir) {
-		if (!page)
-			return ERR_PTR(-ENOMEM);
-		*dir = (void *)page;
-	}
-	return ((swp_entry_t *)*dir) + offset;
-}
-
-/*
- * shmem_alloc_entry - get the position of the swap entry for the
- *                     page. If it does not exist allocate the entry
- *
- * @info:	info structure for the inode
- * @index:	index of the page to find
- */
-static inline swp_entry_t * shmem_alloc_entry (struct shmem_inode_info *info, unsigned long index)
-{
-	unsigned long page = 0;
-	swp_entry_t * res;
 
-	if (index >= SHMEM_MAX_INDEX)
-		return ERR_PTR(-EFBIG);
-
-	if (info->next_index <= index)
-		info->next_index = index + 1;
-
-	while ((res = shmem_swp_entry(info,index,page)) == ERR_PTR(-ENOMEM)) {
-		page = get_zeroed_page(GFP_USER);
-		if (!page)
-			break;
+	dir += index;
+	subdir = *dir;
+	if (!subdir) {
+		if (!page || !(subdir = *page)) {
+			shmem_dir_unmap(dir);
+			return NULL;		/* need a page */
+		}
+		*dir = subdir;
+		*page = NULL;
 	}
-	return res;
+	shmem_dir_unmap(dir);
+
+	/*
+	 * With apologies... caller shmem_swp_alloc passes non-NULL
+	 * page (though perhaps NULL *page); and now we know that this
+	 * indirect page has been allocated, we can shortcut the final
+	 * kmap if we know it contains no swap entries, as is commonly
+	 * the case: return pointer to a 0 which doesn't need kmapping.
+	 */
+	return (page && !subdir->nr_swapped)?
+		(swp_entry_t *)&subdir->nr_swapped:
+		shmem_swp_map(subdir) + offset;
 }
 
-/*
- * shmem_free_swp - free some swap entries in a directory
- *
- * @dir:   pointer to the directory
- * @count: number of entries to scan
- */
-static int shmem_free_swp(swp_entry_t *dir, unsigned int count)
+static void shmem_swp_set(struct shmem_inode_info *info, swp_entry_t *entry, unsigned long value)
 {
-	swp_entry_t *ptr, entry;
-	int freed = 0;
+	long incdec = value? 1: -1;
 
-	for (ptr = dir; ptr < dir + count; ptr++) {
-		if (!ptr->val)
-			continue;
-		entry = *ptr;
-		*ptr = (swp_entry_t){0};
-		freed++;
-		free_swap_and_cache(entry);
-	}
-	return freed;
+	entry->val = value;
+	info->swapped += incdec;
+	if ((unsigned long)(entry - info->i_direct) >= SHMEM_NR_DIRECT)
+		kmap_atomic_to_page(entry)->nr_swapped += incdec;
 }
 
 /*
- * shmem_truncate_direct - free the swap entries of a whole doubly
- *                         indirect block
+ * shmem_swp_alloc - get the position of the swap entry for the page.
+ *                   If it does not exist allocate the entry.
  *
- * @dir:	pointer to the pointer to the block
- * @start:	offset to start from (in pages)
- * @len:	how many pages are stored in this block
- *
- * Returns the number of freed swap entries.
+ * @info:	info structure for the inode
+ * @index:	index of the page to find
  */
+static swp_entry_t *shmem_swp_alloc(struct shmem_inode_info *info, unsigned long index)
+{
+	struct inode *inode = &info->vfs_inode;
+	struct shmem_sb_info *sbinfo = SHMEM_SB(inode->i_sb);
+	struct page *page = NULL;
+	swp_entry_t *entry;
 
-static inline unsigned long 
-shmem_truncate_direct(swp_entry_t *** dir, unsigned long start, unsigned long len) {
-	swp_entry_t **last, **ptr;
-	unsigned long off, freed = 0;
- 
-	if (!*dir)
-		return 0;
+	while (!(entry = shmem_swp_entry(info, index, &page))) {
+		if (index >= info->next_index) {
+			entry = ERR_PTR(-EFAULT);
+			break;
+		}
 
-	last = *dir + (len + ENTRIES_PER_PAGE-1) / ENTRIES_PER_PAGE;
-	off = start % ENTRIES_PER_PAGE;
+		/*
+		 * Test free_blocks against 1 not 0, since we have 1 data
+		 * page (and perhaps indirect index pages) yet to allocate:
+		 * a waste to allocate index if we cannot allocate data.
+		 */
+		spin_lock(&sbinfo->stat_lock);
+		if (sbinfo->free_blocks <= 1) {
+			spin_unlock(&sbinfo->stat_lock);
+			return ERR_PTR(-ENOSPC);
+		}
+		sbinfo->free_blocks--;
+		inode->i_blocks += BLOCKS_PER_PAGE;
+		spin_unlock(&sbinfo->stat_lock);
 
-	for (ptr = *dir + start/ENTRIES_PER_PAGE; ptr < last; ptr++) {
-		if (!*ptr) {
-			off = 0;
-			continue;
+		spin_unlock(&info->lock);
+		page = shmem_dir_alloc(inode->i_mapping->gfp_mask);
+		if (page) {
+			clear_highpage(page);
+			page->nr_swapped = 0;
 		}
+		spin_lock(&info->lock);
 
-		if (!off) {
-			freed += shmem_free_swp(*ptr, ENTRIES_PER_PAGE);
-			free_page ((unsigned long) *ptr);
-			*ptr = 0;
-		} else {
-			freed += shmem_free_swp(*ptr+off,ENTRIES_PER_PAGE-off);
-			off = 0;
+		if (!page) {
+			shmem_free_block(inode);
+			return ERR_PTR(-ENOMEM);
 		}
 	}
-	
-	if (!start) {
-		free_page((unsigned long) *dir);
-		*dir = 0;
+	if (page) {
+		/* another task gave its page, or truncated the file */
+		shmem_free_block(inode);
+		shmem_dir_free(page);
 	}
-	return freed;
+	return entry;
 }
 
 /*
- * shmem_truncate_indirect - truncate an inode
- *
- * @info:  the info structure of the inode
- * @index: the index to truncate
+ * shmem_free_swp - free some swap entries in a directory
  *
- * This function locates the last doubly indirect block and calls
- * then shmem_truncate_direct to do the real work
+ * @dir:   pointer to the directory
+ * @edir:  pointer after last entry of the directory
  */
-static inline unsigned long
-shmem_truncate_indirect(struct shmem_inode_info *info, unsigned long index)
+static int shmem_free_swp(swp_entry_t *dir, swp_entry_t *edir)
 {
-	swp_entry_t ***base;
-	unsigned long baseidx, len, start;
-	unsigned long max = info->next_index-1;
-
-	if (max < SHMEM_NR_DIRECT) {
-		info->next_index = index;
-		return shmem_free_swp(info->i_direct + index,
-				      SHMEM_NR_DIRECT - index);
-	}
-
-	if (max < ENTRIES_PER_PAGE * ENTRIES_PER_PAGE/2 + SHMEM_NR_DIRECT) {
-		max -= SHMEM_NR_DIRECT;
-		base = (swp_entry_t ***) &info->i_indirect;
-		baseidx = SHMEM_NR_DIRECT;
-		len = max+1;
-	} else {
-		max -= ENTRIES_PER_PAGE*ENTRIES_PER_PAGE/2+SHMEM_NR_DIRECT;
-		if (max >= ENTRIES_PER_PAGE*ENTRIES_PER_PAGE*ENTRIES_PER_PAGE/2)
-			BUG();
-
-		baseidx = max & ~(ENTRIES_PER_PAGE*ENTRIES_PER_PAGE-1);
-		base = (swp_entry_t ***) info->i_indirect + ENTRIES_PER_PAGE/2 + baseidx/ENTRIES_PER_PAGE/ENTRIES_PER_PAGE ;
-		len = max - baseidx + 1;
-		baseidx += ENTRIES_PER_PAGE*ENTRIES_PER_PAGE/2+SHMEM_NR_DIRECT;
-	}
+	swp_entry_t *ptr;
+	int freed = 0;
 
-	if (index > baseidx) {
-		info->next_index = index;
-		start = index - baseidx;
-	} else {
-		info->next_index = baseidx;
-		start = 0;
+	for (ptr = dir; ptr < edir; ptr++) {
+		if (ptr->val) {
+			free_swap_and_cache(*ptr);
+			*ptr = (swp_entry_t){0};
+			freed++;
+		}
 	}
-	return shmem_truncate_direct(base, start, len);
+	return freed;
 }
 
-static void shmem_truncate (struct inode * inode)
+static void shmem_truncate(struct inode *inode)
 {
-	unsigned long index;
-	unsigned long partial;
-	unsigned long freed = 0;
-	struct shmem_inode_info * info = SHMEM_I(inode);
+	struct shmem_inode_info *info = SHMEM_I(inode);
+	unsigned long idx;
+	unsigned long size;
+	unsigned long limit;
+	unsigned long stage;
+	struct page **dir;
+	struct page *subdir;
+	struct page *empty;
+	swp_entry_t *ptr;
+	int offset;
+	int freed;
 
-	down(&info->sem);
 	inode->i_ctime = inode->i_mtime = CURRENT_TIME;
-	spin_lock (&info->lock);
-	index = (inode->i_size + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
-	partial = inode->i_size & ~PAGE_CACHE_MASK;
+	idx = (inode->i_size + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
+	if (idx >= info->next_index)
+		return;
 
-	if (partial) {
-		swp_entry_t *entry = shmem_swp_entry(info, index-1, 0);
-		struct page *page;
-		/*
-		 * This check is racy: it's faintly possible that page
-		 * was assigned to swap during truncate_inode_pages,
-		 * and now assigned to file; but better than nothing.
-		 */
-		if (!IS_ERR(entry) && entry->val) {
-			spin_unlock(&info->lock);
-			page = shmem_getpage_locked(info, inode, index-1);
-			if (!IS_ERR(page)) {
-				memclear_highpage_flush(page, partial,
-					PAGE_CACHE_SIZE - partial);
-				unlock_page(page);
-				page_cache_release(page);
+	spin_lock(&info->lock);
+	limit = info->next_index;
+	info->next_index = idx;
+	if (info->swapped && idx < SHMEM_NR_DIRECT) {
+		ptr = info->i_direct;
+		size = limit;
+		if (size > SHMEM_NR_DIRECT)
+			size = SHMEM_NR_DIRECT;
+		info->swapped -= shmem_free_swp(ptr+idx, ptr+size);
+	}
+	if (!info->i_indirect)
+		goto done2;
+
+	BUG_ON(limit <= SHMEM_NR_DIRECT);
+	limit -= SHMEM_NR_DIRECT;
+	idx = (idx > SHMEM_NR_DIRECT)? (idx - SHMEM_NR_DIRECT): 0;
+	offset = idx % ENTRIES_PER_PAGE;
+	idx -= offset;
+
+	empty = NULL;
+	dir = shmem_dir_map(info->i_indirect);
+	stage = ENTRIES_PER_PAGEPAGE/2;
+	if (idx < ENTRIES_PER_PAGEPAGE/2)
+		dir += idx/ENTRIES_PER_PAGE;
+	else {
+		dir += ENTRIES_PER_PAGE/2;
+		dir += (idx - ENTRIES_PER_PAGEPAGE/2)/ENTRIES_PER_PAGEPAGE;
+		while (stage <= idx)
+			stage += ENTRIES_PER_PAGEPAGE;
+		if (*dir) {
+			subdir = *dir;
+			size = ((idx - ENTRIES_PER_PAGEPAGE/2) %
+				ENTRIES_PER_PAGEPAGE) / ENTRIES_PER_PAGE;
+			if (!size && !offset) {
+				empty = subdir;
+				*dir = NULL;
 			}
-			spin_lock(&info->lock);
+			shmem_dir_unmap(dir);
+			dir = shmem_dir_map(subdir) + size;
+		} else {
+			offset = 0;
+			idx = stage;
 		}
 	}
 
-	while (index < info->next_index) 
-		freed += shmem_truncate_indirect(info, index);
-
-	info->swapped -= freed;
+	for (; idx < limit; idx += ENTRIES_PER_PAGE, dir++) {
+		if (unlikely(idx == stage)) {
+			shmem_dir_unmap(dir-1);
+			dir = shmem_dir_map(info->i_indirect) +
+			    ENTRIES_PER_PAGE/2 + idx/ENTRIES_PER_PAGEPAGE;
+			while (!*dir) {
+				dir++;
+				idx += ENTRIES_PER_PAGEPAGE;
+				if (idx >= limit)
+					goto done1;
+			}
+			stage = idx + ENTRIES_PER_PAGEPAGE;
+			subdir = *dir;
+			*dir = NULL;
+			shmem_dir_unmap(dir);
+			if (empty) {
+				shmem_dir_free(empty);
+				info->alloced++;
+			}
+			empty = subdir;
+			cond_resched_lock(&info->lock);
+			dir = shmem_dir_map(subdir);
+		}
+		subdir = *dir;
+		if (subdir && subdir->nr_swapped) {
+			ptr = shmem_swp_map(subdir);
+			size = limit - idx;
+			if (size > ENTRIES_PER_PAGE)
+				size = ENTRIES_PER_PAGE;
+			freed = shmem_free_swp(ptr+offset, ptr+size);
+			shmem_swp_unmap(ptr);
+			info->swapped -= freed;
+			subdir->nr_swapped -= freed;
+			BUG_ON(subdir->nr_swapped > offset);
+		}
+		if (offset)
+			offset = 0;
+		else if (subdir) {
+			*dir = NULL;
+			shmem_dir_free(subdir);
+			info->alloced++;
+		}
+	}
+done1:
+	shmem_dir_unmap(dir-1);
+	if (empty) {
+		shmem_dir_free(empty);
+		info->alloced++;
+	}
+	if (info->next_index <= SHMEM_NR_DIRECT) {
+		shmem_dir_free(info->i_indirect);
+		info->i_indirect = NULL;
+		info->alloced++;
+	}
+done2:
+	BUG_ON(info->swapped > info->next_index);
 	shmem_recalc_inode(inode);
-	spin_unlock (&info->lock);
-	up(&info->sem);
+	spin_unlock(&info->lock);
 }
 
-static int shmem_notify_change(struct dentry * dentry, struct iattr *attr)
+static int shmem_notify_change(struct dentry *dentry, struct iattr *attr)
 {
+	static struct page *shmem_holdpage(struct inode *, unsigned long);
 	struct inode *inode = dentry->d_inode;
+	struct page *page = NULL;
 	long change = 0;
 	int error;
 
@@ -381,20 +485,33 @@
 		if (change > 0) {
 			if (!vm_enough_memory(change))
 				return -ENOMEM;
-		} else
+		} else if (attr->ia_size < inode->i_size) {
 			vm_unacct_memory(-change);
+			/*
+			 * If truncating down to a partial page, then
+			 * if that page is already allocated, hold it
+			 * in memory until the truncation is over, so
+			 * truncate_partial_page cannnot miss it were
+			 * it assigned to swap.
+			 */
+			if (attr->ia_size & (PAGE_CACHE_SIZE-1)) {
+				page = shmem_holdpage(inode,
+					attr->ia_size >> PAGE_CACHE_SHIFT);
+			}
+		}
 	}
 
 	error = inode_change_ok(inode, attr);
 	if (!error)
 		error = inode_setattr(inode, attr);
+	if (page)
+		page_cache_release(page);
 	if (error)
 		vm_unacct_memory(change);
 	return error;
 }
 
-
-static void shmem_delete_inode(struct inode * inode)
+static void shmem_delete_inode(struct inode *inode)
 {
 	struct shmem_sb_info *sbinfo = SHMEM_SB(inode->i_sb);
 	struct shmem_inode_info *info = SHMEM_I(inode);
@@ -406,128 +523,142 @@
 		if (info->flags & VM_ACCOUNT)
 			vm_unacct_memory(VM_ACCT(inode->i_size));
 		inode->i_size = 0;
-		shmem_truncate (inode);
+		shmem_truncate(inode);
 	}
-	spin_lock (&sbinfo->stat_lock);
+	BUG_ON(inode->i_blocks);
+	spin_lock(&sbinfo->stat_lock);
 	sbinfo->free_inodes++;
-	spin_unlock (&sbinfo->stat_lock);
+	spin_unlock(&sbinfo->stat_lock);
 	clear_inode(inode);
 }
 
-static inline int shmem_find_swp(swp_entry_t entry, swp_entry_t *ptr, swp_entry_t *eptr)
+static inline int shmem_find_swp(swp_entry_t entry, swp_entry_t *dir, swp_entry_t *edir)
 {
-	swp_entry_t *test;
+	swp_entry_t *ptr;
 
-	for (test = ptr; test < eptr; test++) {
-		if (test->val == entry.val)
-			return test - ptr;
+	for (ptr = dir; ptr < edir; ptr++) {
+		if (ptr->val == entry.val)
+			return ptr - dir;
 	}
 	return -1;
 }
 
 static int shmem_unuse_inode(struct shmem_inode_info *info, swp_entry_t entry, struct page *page)
 {
-	swp_entry_t *ptr;
 	unsigned long idx;
+	unsigned long size;
+	unsigned long limit;
+	unsigned long stage;
+	struct page **dir;
+	struct page *subdir;
+	swp_entry_t *ptr;
 	int offset;
-	struct inode *inode;
 
 	idx = 0;
 	ptr = info->i_direct;
-	spin_lock (&info->lock);
-	offset = info->next_index;
-	if (offset > SHMEM_NR_DIRECT)
-		offset = SHMEM_NR_DIRECT;
-	offset = shmem_find_swp(entry, ptr, ptr + offset);
+	spin_lock(&info->lock);
+	limit = info->next_index;
+	size = limit;
+	if (size > SHMEM_NR_DIRECT)
+		size = SHMEM_NR_DIRECT;
+	offset = shmem_find_swp(entry, ptr, ptr+size);
 	if (offset >= 0)
 		goto found;
-
-	for (idx = SHMEM_NR_DIRECT; idx < info->next_index; 
-	     idx += ENTRIES_PER_PAGE) {
-		ptr = shmem_swp_entry(info, idx, 0);
-		if (IS_ERR(ptr))
-			continue;
-		offset = info->next_index - idx;
-		if (offset > ENTRIES_PER_PAGE)
-			offset = ENTRIES_PER_PAGE;
-		offset = shmem_find_swp(entry, ptr, ptr + offset);
-		if (offset >= 0)
-			goto found;
+	if (!info->i_indirect)
+		goto lost2;
+	/* we might be racing with shmem_truncate */
+	if (limit <= SHMEM_NR_DIRECT)
+		goto lost2;
+
+	dir = shmem_dir_map(info->i_indirect);
+	stage = SHMEM_NR_DIRECT + ENTRIES_PER_PAGEPAGE/2;
+
+	for (idx = SHMEM_NR_DIRECT; idx < limit; idx += ENTRIES_PER_PAGE, dir++) {
+		if (unlikely(idx == stage)) {
+			shmem_dir_unmap(dir-1);
+			dir = shmem_dir_map(info->i_indirect) +
+			    ENTRIES_PER_PAGE/2 + idx/ENTRIES_PER_PAGEPAGE;
+			while (!*dir) {
+				dir++;
+				idx += ENTRIES_PER_PAGEPAGE;
+				if (idx >= limit)
+					goto lost1;
+			}
+			stage = idx + ENTRIES_PER_PAGEPAGE;
+			subdir = *dir;
+			shmem_dir_unmap(dir);
+			dir = shmem_dir_map(subdir);
+		}
+		subdir = *dir;
+		if (subdir && subdir->nr_swapped) {
+			ptr = shmem_swp_map(subdir);
+			size = limit - idx;
+			if (size > ENTRIES_PER_PAGE)
+				size = ENTRIES_PER_PAGE;
+			offset = shmem_find_swp(entry, ptr, ptr+size);
+			if (offset >= 0) {
+				shmem_dir_unmap(dir);
+				goto found;
+			}
+			shmem_swp_unmap(ptr);
+		}
 	}
-	spin_unlock (&info->lock);
+lost1:
+	shmem_dir_unmap(dir-1);
+lost2:
+	spin_unlock(&info->lock);
 	return 0;
 found:
-	idx += offset;
-	inode = igrab(&info->vfs_inode);
-	/* move head to start search for next from here */
-	list_move_tail(&shmem_inodes, &info->list);
-	spin_unlock(&shmem_ilock);
-	swap_free(entry);
-	ptr[offset] = (swp_entry_t) {0};
-
-	while (inode && move_from_swap_cache(page, idx, inode->i_mapping)) {
-		/*
-		 * Yield for kswapd, and try again - but we're still
-		 * holding the page lock - ugh! fix this up later on.
-		 * Beware of inode being unlinked or truncated: just
-		 * leave try_to_unuse to delete_from_swap_cache if so.
-		 */
-		spin_unlock(&info->lock);
-		yield();
-		spin_lock(&info->lock);
-		ptr = shmem_swp_entry(info, idx, 0);
-		if (IS_ERR(ptr))
-			break;
-	}
-
-	info->swapped--;
-	SetPageUptodate(page);
+	if (move_from_swap_cache(page, idx + offset,
+			info->vfs_inode.i_mapping) == 0)
+		shmem_swp_set(info, ptr + offset, 0);
+	shmem_swp_unmap(ptr);
 	spin_unlock(&info->lock);
-	if (inode)
-		iput(inode);
+	/*
+	 * Decrement swap count even when the entry is left behind:
+	 * try_to_unuse will skip over mms, then reincrement count.
+	 */
+	swap_free(entry);
 	return 1;
 }
 
 /*
  * shmem_unuse() search for an eventually swapped out shmem page.
- * Note shmem_unuse_inode drops shmem_ilock itself if successful.
  */
-void shmem_unuse(swp_entry_t entry, struct page *page)
+int shmem_unuse(swp_entry_t entry, struct page *page)
 {
 	struct list_head *p;
-	struct shmem_inode_info * info;
+	struct shmem_inode_info *info;
+	int found = 0;
 
-	spin_lock (&shmem_ilock);
+	spin_lock(&shmem_ilock);
 	list_for_each(p, &shmem_inodes) {
 		info = list_entry(p, struct shmem_inode_info, list);
 
-		if (info->swapped && shmem_unuse_inode(info, entry, page))
-			return;
+		if (info->swapped && shmem_unuse_inode(info, entry, page)) {
+			/* move head to start search for next from here */
+			list_move_tail(&shmem_inodes, &info->list);
+			found = 1;
+			break;
+		}
 	}
-	spin_unlock (&shmem_ilock);
+	spin_unlock(&shmem_ilock);
+	return found;
 }
 
 /*
  * Move the page from the page cache to the swap cache.
- *
- * The page lock prevents multiple occurences of shmem_writepage at
- * once.  We still need to guard against racing with
- * shmem_getpage_locked().  
  */
-static int shmem_writepage(struct page * page)
+static int shmem_writepage(struct page *page)
 {
-	int err;
 	struct shmem_inode_info *info;
 	swp_entry_t *entry, swap;
 	struct address_space *mapping;
 	unsigned long index;
 	struct inode *inode;
 
-	if (!PageLocked(page))
-		BUG();
-
-	if (!(current->flags & PF_MEMALLOC))
-		return fail_writepage(page);
+	BUG_ON(!PageLocked(page));
+	BUG_ON(page_mapped(page));
 
 	mapping = page->mapping;
 	index = page->index;
@@ -540,215 +671,247 @@
 		return fail_writepage(page);
 
 	spin_lock(&info->lock);
-	entry = shmem_swp_entry(info, index, 0);
-	if (IS_ERR(entry))	/* this had been allocated on page allocation */
-		BUG();
 	shmem_recalc_inode(inode);
-	if (entry->val)
-		BUG();
-
-	err = move_to_swap_cache(page, swap);
-	if (!err) {
-		*entry = swap;
-		info->swapped++;
+	entry = shmem_swp_entry(info, index, NULL);
+	BUG_ON(!entry);
+	BUG_ON(entry->val);
+
+	if (move_to_swap_cache(page, swap) == 0) {
+		shmem_swp_set(info, entry, swap.val);
+		shmem_swp_unmap(entry);
 		spin_unlock(&info->lock);
-		SetPageUptodate(page);
-		set_page_dirty(page);
 		unlock_page(page);
 		return 0;
 	}
 
+	shmem_swp_unmap(entry);
 	spin_unlock(&info->lock);
 	swap_free(swap);
 	return fail_writepage(page);
 }
 
+static int shmem_writepages(struct address_space *mapping, struct writeback_control *wbc)
+{
+	return 0;
+}
+
+static int shmem_vm_writeback(struct page *page, struct writeback_control *wbc)
+{
+	clear_page_dirty(page);
+	if (shmem_writepage(page) < 0)
+		set_page_dirty(page);
+	return 0;
+}
+
 /*
- * shmem_getpage_locked - either get the page from swap or allocate a new one
+ * shmem_getpage - either get the page from swap or allocate a new one
  *
  * If we allocate a new one we do not mark it dirty. That's up to the
  * vm. If we swap it in we mark it dirty since we also free the swap
  * entry since a page cannot live in both the swap and page cache
- *
- * Called with the inode locked, so it cannot race with itself, but we
- * still need to guard against racing with shm_writepage(), which might
- * be trying to move the page to the swap cache as we run.
  */
-static struct page * shmem_getpage_locked(struct shmem_inode_info *info, struct inode * inode, unsigned long idx)
+static int shmem_getpage(struct inode *inode, unsigned long idx, struct page **pagep)
 {
 	struct address_space *mapping = inode->i_mapping;
+	struct shmem_inode_info *info = SHMEM_I(inode);
 	struct shmem_sb_info *sbinfo;
 	struct page *page;
 	swp_entry_t *entry;
-	int error;
+	swp_entry_t swap;
+	int error = 0;
 
-repeat:
-	page = find_lock_page(mapping, idx);
-	if (page)
-		return page;
+	if (idx >= SHMEM_MAX_INDEX)
+		return -EFBIG;
 
-	entry = shmem_alloc_entry (info, idx);
-	if (IS_ERR(entry))
-		return (void *)entry;
-
-	spin_lock (&info->lock);
-	
-	/* The shmem_alloc_entry() call may have blocked, and
-	 * shmem_writepage may have been moving a page between the page
-	 * cache and swap cache.  We need to recheck the page cache
-	 * under the protection of the info->lock spinlock. */
+	/*
+	 * When writing, i_sem is held against truncation and other
+	 * writing, so next_index will remain as set here; but when
+	 * reading, idx must always be checked against next_index
+	 * after sleeping, lest truncation occurred meanwhile.
+	 */
+	spin_lock(&info->lock);
+	if (info->next_index <= idx)
+		info->next_index = idx + 1;
+	spin_unlock(&info->lock);
 
-	page = find_get_page(mapping, idx);
+repeat:
+	page = find_lock_page(mapping, idx);
 	if (page) {
-		if (TestSetPageLocked(page))
-			goto wait_retry;
-		spin_unlock (&info->lock);
-		return page;
+		*pagep = page;
+		return 0;
 	}
-	
+
+	spin_lock(&info->lock);
 	shmem_recalc_inode(inode);
-	if (entry->val) {
+	entry = shmem_swp_alloc(info, idx);
+	if (IS_ERR(entry)) {
+		spin_unlock(&info->lock);
+		return PTR_ERR(entry);
+	}
+	swap = *entry;
+
+	if (swap.val) {
 		/* Look it up and read it in.. */
-		page = find_get_page(&swapper_space, entry->val);
+		page = lookup_swap_cache(swap);
 		if (!page) {
-			swp_entry_t swap = *entry;
-			spin_unlock (&info->lock);
-			swapin_readahead(*entry);
-			page = read_swap_cache_async(*entry);
+			shmem_swp_unmap(entry);
+			spin_unlock(&info->lock);
+			swapin_readahead(swap);
+			page = read_swap_cache_async(swap);
 			if (!page) {
-				if (entry->val != swap.val)
-					goto repeat;
-				return ERR_PTR(-ENOMEM);
+				spin_lock(&info->lock);
+				entry = shmem_swp_alloc(info, idx);
+				if (IS_ERR(entry))
+					error = PTR_ERR(entry);
+				else {
+					if (entry->val == swap.val)
+						error = -ENOMEM;
+					shmem_swp_unmap(entry);
+				}
+				spin_unlock(&info->lock);
+				if (error)
+					return error;
+				goto repeat;
 			}
 			wait_on_page_locked(page);
-			if (!PageUptodate(page) && entry->val == swap.val) {
-				page_cache_release(page);
-				return ERR_PTR(-EIO);
-			}
-			
-			/* Too bad we can't trust this page, because we
-			 * dropped the info->lock spinlock */
 			page_cache_release(page);
 			goto repeat;
 		}
 
 		/* We have to do this with page locked to prevent races */
-		if (TestSetPageLocked(page))
-			goto wait_retry;
+		if (TestSetPageLocked(page)) {
+			shmem_swp_unmap(entry);
+			spin_unlock(&info->lock);
+			wait_on_page_locked(page);
+			page_cache_release(page);
+			goto repeat;
+		}
 		if (PageWriteback(page)) {
+			shmem_swp_unmap(entry);
 			spin_unlock(&info->lock);
 			wait_on_page_writeback(page);
 			unlock_page(page);
 			page_cache_release(page);
 			goto repeat;
 		}
-		error = move_from_swap_cache(page, idx, mapping);
-		if (error < 0) {
+
+		error = PageUptodate(page)?
+			move_from_swap_cache(page, idx, mapping): -EIO;
+		if (error) {
+			shmem_swp_unmap(entry);
 			spin_unlock(&info->lock);
 			unlock_page(page);
 			page_cache_release(page);
-			return ERR_PTR(error);
+			return error;
 		}
 
-		swap_free(*entry);
-		*entry = (swp_entry_t) {0};
-		info->swapped--;
-		spin_unlock (&info->lock);
+		shmem_swp_set(info, entry, 0);
+		shmem_swp_unmap(entry);
+		spin_unlock(&info->lock);
+		swap_free(swap);
 	} else {
+		shmem_swp_unmap(entry);
+		spin_unlock(&info->lock);
 		sbinfo = SHMEM_SB(inode->i_sb);
-		spin_unlock (&info->lock);
-		spin_lock (&sbinfo->stat_lock);
-		if (sbinfo->free_blocks == 0)
-			goto no_space;
+		spin_lock(&sbinfo->stat_lock);
+		if (sbinfo->free_blocks == 0) {
+			spin_unlock(&sbinfo->stat_lock);
+			return -ENOSPC;
+		}
 		sbinfo->free_blocks--;
-		spin_unlock (&sbinfo->stat_lock);
+		inode->i_blocks += BLOCKS_PER_PAGE;
+		spin_unlock(&sbinfo->stat_lock);
 
-		/* Ok, get a new page.  We don't have to worry about the
-		 * info->lock spinlock here: we cannot race against
-		 * shm_writepage because we have already verified that
-		 * there is no page present either in memory or in the
-		 * swap cache, so we are guaranteed to be populating a
-		 * new shm entry.  The inode semaphore we already hold
-		 * is enough to make this atomic. */
 		page = page_cache_alloc(mapping);
-		if (!page)
-			goto no_mem;
-		error = add_to_page_cache_lru(page, mapping, idx);
-		if (error < 0) {
+		if (!page) {
+			shmem_free_block(inode);
+			return -ENOMEM;
+		}
+
+		spin_lock(&info->lock);
+		entry = shmem_swp_alloc(info, idx);
+		if (IS_ERR(entry))
+			error = PTR_ERR(entry);
+		else {
+			swap = *entry;
+			shmem_swp_unmap(entry);
+		}
+		if (error || swap.val ||
+		    add_to_page_cache_lru(page, mapping, idx) < 0) {
+			spin_unlock(&info->lock);
 			page_cache_release(page);
-			goto no_mem;
+			shmem_free_block(inode);
+			if (error)
+				return error;
+			goto repeat;
 		}
+		info->alloced++;
+		spin_unlock(&info->lock);
 		clear_highpage(page);
-		inode->i_blocks += BLOCKS_PER_PAGE;
+		SetPageUptodate(page);
 	}
 
 	/* We have the page */
-	SetPageUptodate(page);
-	return page;
-
-no_mem:
-	spin_lock(&sbinfo->stat_lock);
-	sbinfo->free_blocks++;
-	spin_unlock(&sbinfo->stat_lock);
-	return ERR_PTR(-ENOMEM);
-
-no_space:
-	spin_unlock (&sbinfo->stat_lock);
-	return ERR_PTR(-ENOSPC);
-
-wait_retry:
-	spin_unlock(&info->lock);
-	wait_on_page_locked(page);
-	page_cache_release(page);
-	goto repeat;
+	*pagep = page;
+	return 0;
 }
 
-static int shmem_getpage(struct inode * inode, unsigned long idx, struct page **ptr)
+static struct page *shmem_holdpage(struct inode *inode, unsigned long idx)
 {
 	struct shmem_inode_info *info = SHMEM_I(inode);
-	int error;
-
-	down (&info->sem);
-	*ptr = ERR_PTR(-EFAULT);
-	if (inode->i_size <= (loff_t) idx * PAGE_CACHE_SIZE)
-		goto failed;
-
-	*ptr = shmem_getpage_locked(info, inode, idx);
-	if (IS_ERR (*ptr))
-		goto failed;
+	struct page *page;
+	swp_entry_t *entry;
+	swp_entry_t swap = {0};
 
-	unlock_page(*ptr);
-	up (&info->sem);
-	return 0;
-failed:
-	up (&info->sem);
-	error = PTR_ERR(*ptr);
-	*ptr = NOPAGE_SIGBUS;
-	if (error == -ENOMEM)
-		*ptr = NOPAGE_OOM;
-	return error;
+	/*
+	 * Somehow, it feels wrong for truncation down to cause any
+	 * allocation: so instead of a blind shmem_getpage, check that
+	 * the page has actually been instantiated before holding it.
+	 */
+	spin_lock(&info->lock);
+	page = find_get_page(inode->i_mapping, idx);
+	if (!page) {
+		entry = shmem_swp_entry(info, idx, NULL);
+		if (entry) {
+			swap = *entry;
+			shmem_swp_unmap(entry);
+		}
+	}
+	spin_unlock(&info->lock);
+	if (swap.val) {
+		if (shmem_getpage(inode, idx, &page) == 0)
+			unlock_page(page);
+	}
+	return page;
 }
 
-struct page * shmem_nopage(struct vm_area_struct * vma, unsigned long address, int unused)
+struct page *shmem_nopage(struct vm_area_struct *vma, unsigned long address, int unused)
 {
-	struct page * page;
-	unsigned int idx;
-	struct inode * inode = vma->vm_file->f_dentry->d_inode;
+	struct inode *inode = vma->vm_file->f_dentry->d_inode;
+	struct page *page;
+	unsigned long idx;
+	int error;
 
-	idx = (address - vma->vm_start) >> PAGE_CACHE_SHIFT;
+	idx = (address - vma->vm_start) >> PAGE_SHIFT;
 	idx += vma->vm_pgoff;
+	idx >>= PAGE_CACHE_SHIFT - PAGE_SHIFT;
+
+	if (((loff_t) idx << PAGE_CACHE_SHIFT) >= inode->i_size)
+		return NOPAGE_SIGBUS;
 
-	if (shmem_getpage(inode, idx, &page))
-		return page;
+	error = shmem_getpage(inode, idx, &page);
+	if (error)
+		return (error == -ENOMEM)? NOPAGE_OOM: NOPAGE_SIGBUS;
 
+	unlock_page(page);
 	flush_page_to_ram(page);
-	return(page);
+	return page;
 }
 
-void shmem_lock(struct file * file, int lock)
+void shmem_lock(struct file *file, int lock)
 {
-	struct inode * inode = file->f_dentry->d_inode;
-	struct shmem_inode_info * info = SHMEM_I(inode);
+	struct inode *inode = file->f_dentry->d_inode;
+	struct shmem_inode_info *info = SHMEM_I(inode);
 
 	spin_lock(&info->lock);
 	if (lock)
@@ -758,9 +921,9 @@
 	spin_unlock(&info->lock);
 }
 
-static int shmem_mmap(struct file * file, struct vm_area_struct * vma)
+static int shmem_mmap(struct file *file, struct vm_area_struct *vma)
 {
-	struct vm_operations_struct * ops;
+	struct vm_operations_struct *ops;
 	struct inode *inode = file->f_dentry->d_inode;
 
 	ops = &shmem_vm_ops;
@@ -773,17 +936,17 @@
 
 struct inode *shmem_get_inode(struct super_block *sb, int mode, int dev)
 {
-	struct inode * inode;
+	struct inode *inode;
 	struct shmem_inode_info *info;
 	struct shmem_sb_info *sbinfo = SHMEM_SB(sb);
 
-	spin_lock (&sbinfo->stat_lock);
+	spin_lock(&sbinfo->stat_lock);
 	if (!sbinfo->free_inodes) {
-		spin_unlock (&sbinfo->stat_lock);
+		spin_unlock(&sbinfo->stat_lock);
 		return NULL;
 	}
 	sbinfo->free_inodes--;
-	spin_unlock (&sbinfo->stat_lock);
+	spin_unlock(&sbinfo->stat_lock);
 
 	inode = new_inode(sb);
 	if (inode) {
@@ -797,12 +960,8 @@
 		inode->i_mapping->backing_dev_info = &shmem_backing_dev_info;
 		inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
 		info = SHMEM_I(inode);
-		spin_lock_init (&info->lock);
-		sema_init (&info->sem, 1);
-		info->next_index = 0;
-		memset (info->i_direct, 0, sizeof(info->i_direct));
-		info->i_indirect = NULL;
-		info->swapped = 0;
+		memset(info, 0, (char *)inode - (char *)info);
+		spin_lock_init(&info->lock);
 		info->flags = VM_ACCOUNT;
 		switch (mode & S_IFMT) {
 		default:
@@ -811,12 +970,14 @@
 		case S_IFREG:
 			inode->i_op = &shmem_inode_operations;
 			inode->i_fop = &shmem_file_operations;
-			spin_lock (&shmem_ilock);
-			list_add_tail(&SHMEM_I(inode)->list, &shmem_inodes);
-			spin_unlock (&shmem_ilock);
+			spin_lock(&shmem_ilock);
+			list_add_tail(&info->list, &shmem_inodes);
+			spin_unlock(&shmem_ilock);
 			break;
 		case S_IFDIR:
 			inode->i_nlink++;
+			/* Some things misbehave if size == 0 on a directory */
+			inode->i_size = 2 * BOGO_DIRENT_SIZE;
 			inode->i_op = &shmem_dir_inode_operations;
 			inode->i_fop = &simple_dir_operations;
 			break;
@@ -857,10 +1018,9 @@
 static struct inode_operations shmem_symlink_inline_operations;
 
 static ssize_t
-shmem_file_write(struct file *file,const char *buf,size_t count,loff_t *ppos)
+shmem_file_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
 {
-	struct inode	*inode = file->f_dentry->d_inode; 
-	struct shmem_inode_info *info;
+	struct inode	*inode = file->f_dentry->d_inode;
 	unsigned long	limit = current->rlim[RLIMIT_FSIZE].rlim_cur;
 	loff_t		pos;
 	struct page	*page;
@@ -951,22 +1111,12 @@
 			__get_user(dummy, buf+bytes-1);
 		}
 
-		info = SHMEM_I(inode);
-		down (&info->sem);
-		page = shmem_getpage_locked(info, inode, index);
-		up (&info->sem);
-
-		status = PTR_ERR(page);
-		if (IS_ERR(page))
+		status = shmem_getpage(inode, index, &page);
+		if (status)
 			break;
 
-		/* We have exclusive IO access to the page.. */
-		if (!PageLocked(page)) {
-			PAGE_BUG(page);
-		}
-
 		kaddr = kmap(page);
-		status = copy_from_user(kaddr+offset, buf, bytes);
+		status = __copy_from_user(kaddr+offset, buf, bytes);
 		kunmap(page);
 		if (status)
 			goto fail_write;
@@ -978,7 +1128,7 @@
 			count -= bytes;
 			pos += bytes;
 			buf += bytes;
-			if (pos > inode->i_size) 
+			if (pos > inode->i_size)
 				inode->i_size = pos;
 		}
 unlock:
@@ -1005,7 +1155,7 @@
 	goto unlock;
 }
 
-static void do_shmem_file_read(struct file * filp, loff_t *ppos, read_descriptor_t * desc)
+static void do_shmem_file_read(struct file *filp, loff_t *ppos, read_descriptor_t *desc)
 {
 	struct inode *inode = filp->f_dentry->d_inode;
 	struct address_space *mapping = inode->i_mapping;
@@ -1022,17 +1172,35 @@
 		end_index = inode->i_size >> PAGE_CACHE_SHIFT;
 		if (index > end_index)
 			break;
-		nr = PAGE_CACHE_SIZE;
 		if (index == end_index) {
 			nr = inode->i_size & ~PAGE_CACHE_MASK;
 			if (nr <= offset)
 				break;
 		}
 
-		nr = nr - offset;
-
-		if ((desc->error = shmem_getpage(inode, index, &page)))
+		desc->error = shmem_getpage(inode, index, &page);
+		if (desc->error) {
+			if (desc->error == -EFAULT)
+				desc->error = 0;
 			break;
+		}
+
+		/*
+		 * We must evaluate after, since reads (unlike writes)
+		 * are called without i_sem protection against truncate
+		 */
+		nr = PAGE_CACHE_SIZE;
+		end_index = inode->i_size >> PAGE_CACHE_SHIFT;
+		if (index == end_index) {
+			nr = inode->i_size & ~PAGE_CACHE_MASK;
+			if (nr <= offset) {
+				unlock_page(page);
+				page_cache_release(page);
+				break;
+			}
+		}
+		unlock_page(page);
+		nr -= offset;
 
 		if (!list_empty(&mapping->i_mmap_shared))
 			flush_dcache_page(page);
@@ -1051,7 +1219,7 @@
 		offset += nr;
 		index += offset >> PAGE_CACHE_SHIFT;
 		offset &= ~PAGE_CACHE_MASK;
-	
+
 		page_cache_release(page);
 	}
 
@@ -1059,7 +1227,7 @@
 	UPDATE_ATIME(inode);
 }
 
-static ssize_t shmem_file_read(struct file * filp, char * buf, size_t count, loff_t *ppos)
+static ssize_t shmem_file_read(struct file *filp, char *buf, size_t count, loff_t *ppos)
 {
 	ssize_t retval;
 
@@ -1090,13 +1258,13 @@
 
 	buf->f_type = TMPFS_MAGIC;
 	buf->f_bsize = PAGE_CACHE_SIZE;
-	spin_lock (&sbinfo->stat_lock);
+	spin_lock(&sbinfo->stat_lock);
 	buf->f_blocks = sbinfo->max_blocks;
 	buf->f_bavail = buf->f_bfree = sbinfo->free_blocks;
 	buf->f_files = sbinfo->max_inodes;
 	buf->f_ffree = sbinfo->free_inodes;
-	spin_unlock (&sbinfo->stat_lock);
-	buf->f_namelen = 255;
+	spin_unlock(&sbinfo->stat_lock);
+	buf->f_namelen = NAME_MAX;
 	return 0;
 }
 
@@ -1105,10 +1273,11 @@
  */
 static int shmem_mknod(struct inode *dir, struct dentry *dentry, int mode, int dev)
 {
-	struct inode * inode = shmem_get_inode(dir->i_sb, mode, dev);
+	struct inode *inode = shmem_get_inode(dir->i_sb, mode, dev);
 	int error = -ENOSPC;
 
 	if (inode) {
+		dir->i_size += BOGO_DIRENT_SIZE;
 		dir->i_ctime = dir->i_mtime = CURRENT_TIME;
 		d_instantiate(dentry, inode);
 		dget(dentry); /* Extra count - pin the dentry in core */
@@ -1117,7 +1286,7 @@
 	return error;
 }
 
-static int shmem_mkdir(struct inode * dir, struct dentry * dentry, int mode)
+static int shmem_mkdir(struct inode *dir, struct dentry *dentry, int mode)
 {
 	int error;
 
@@ -1135,10 +1304,11 @@
 /*
  * Link a file..
  */
-static int shmem_link(struct dentry *old_dentry, struct inode * dir, struct dentry * dentry)
+static int shmem_link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry)
 {
 	struct inode *inode = old_dentry->d_inode;
 
+	dir->i_size += BOGO_DIRENT_SIZE;
 	inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
 	inode->i_nlink++;
 	atomic_inc(&inode->i_count);	/* New dentry reference */
@@ -1180,16 +1350,18 @@
 	return 1;
 }
 
-static int shmem_unlink(struct inode * dir, struct dentry *dentry)
+static int shmem_unlink(struct inode *dir, struct dentry *dentry)
 {
 	struct inode *inode = dentry->d_inode;
+
+	dir->i_size -= BOGO_DIRENT_SIZE;
 	inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
 	inode->i_nlink--;
 	dput(dentry);	/* Undo the count from "create" - this does all the work */
 	return 0;
 }
 
-static int shmem_rmdir(struct inode * dir, struct dentry *dentry)
+static int shmem_rmdir(struct inode *dir, struct dentry *dentry)
 {
 	if (!shmem_empty(dentry))
 		return -ENOTEMPTY;
@@ -1204,36 +1376,39 @@
  * it exists so that the VFS layer correctly free's it when it
  * gets overwritten.
  */
-static int shmem_rename(struct inode * old_dir, struct dentry *old_dentry, struct inode * new_dir,struct dentry *new_dentry)
+static int shmem_rename(struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry)
 {
-	struct inode *inode;
+	struct inode *inode = old_dentry->d_inode;
+	int they_are_dirs = S_ISDIR(inode->i_mode);
 
-	if (!shmem_empty(new_dentry)) 
+	if (!shmem_empty(new_dentry))
 		return -ENOTEMPTY;
 
-	inode = new_dentry->d_inode;
-	if (inode) {
-		inode->i_ctime = CURRENT_TIME;
-		inode->i_nlink--;
-		dput(new_dentry);
-	}
-	inode = old_dentry->d_inode;
-	if (S_ISDIR(inode->i_mode)) {
+	if (new_dentry->d_inode) {
+		(void) shmem_unlink(new_dir, new_dentry);
+		if (they_are_dirs)
+			old_dir->i_nlink--;
+	} else if (they_are_dirs) {
 		old_dir->i_nlink--;
 		new_dir->i_nlink++;
 	}
 
-	inode->i_ctime = old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME;
+	old_dir->i_size -= BOGO_DIRENT_SIZE;
+	new_dir->i_size += BOGO_DIRENT_SIZE;
+	old_dir->i_ctime = old_dir->i_mtime =
+	new_dir->i_ctime = new_dir->i_mtime =
+	inode->i_ctime = CURRENT_TIME;
 	return 0;
 }
 
-static int shmem_symlink(struct inode * dir, struct dentry *dentry, const char * symname)
+static int shmem_symlink(struct inode *dir, struct dentry *dentry, const char *symname)
 {
+	int error;
 	int len;
 	struct inode *inode;
 	struct page *page;
 	char *kaddr;
-	struct shmem_inode_info * info;
+	struct shmem_inode_info *info;
 
 	len = strlen(symname) + 1;
 	if (len > PAGE_CACHE_SIZE)
@@ -1254,26 +1429,24 @@
 			iput(inode);
 			return -ENOMEM;
 		}
-		down(&info->sem);
-		page = shmem_getpage_locked(info, inode, 0);
-		if (IS_ERR(page)) {
-			up(&info->sem);
+		error = shmem_getpage(inode, 0, &page);
+		if (error) {
 			vm_unacct_memory(VM_ACCT(1));
 			iput(inode);
-			return PTR_ERR(page);
+			return error;
 		}
 		inode->i_op = &shmem_symlink_inode_operations;
-		spin_lock (&shmem_ilock);
+		spin_lock(&shmem_ilock);
 		list_add_tail(&info->list, &shmem_inodes);
-		spin_unlock (&shmem_ilock);
+		spin_unlock(&shmem_ilock);
 		kaddr = kmap(page);
 		memcpy(kaddr, symname, len);
 		kunmap(page);
 		set_page_dirty(page);
 		unlock_page(page);
 		page_cache_release(page);
-		up(&info->sem);
 	}
+	dir->i_size += BOGO_DIRENT_SIZE;
 	dir->i_ctime = dir->i_mtime = CURRENT_TIME;
 	d_instantiate(dentry, inode);
 	dget(dentry);
@@ -1282,7 +1455,7 @@
 
 static int shmem_readlink_inline(struct dentry *dentry, char *buffer, int buflen)
 {
-	return vfs_readlink(dentry,buffer,buflen, (const char *)SHMEM_I(dentry->d_inode));
+	return vfs_readlink(dentry, buffer, buflen, (const char *)SHMEM_I(dentry->d_inode));
 }
 
 static int shmem_follow_link_inline(struct dentry *dentry, struct nameidata *nd)
@@ -1292,27 +1465,26 @@
 
 static int shmem_readlink(struct dentry *dentry, char *buffer, int buflen)
 {
-	struct page * page;
+	struct page *page;
 	int res = shmem_getpage(dentry->d_inode, 0, &page);
-
 	if (res)
 		return res;
-
-	res = vfs_readlink(dentry,buffer,buflen, kmap(page));
+	res = vfs_readlink(dentry, buffer, buflen, kmap(page));
 	kunmap(page);
+	unlock_page(page);
 	page_cache_release(page);
 	return res;
 }
 
 static int shmem_follow_link(struct dentry *dentry, struct nameidata *nd)
 {
-	struct page * page;
+	struct page *page;
 	int res = shmem_getpage(dentry->d_inode, 0, &page);
 	if (res)
 		return res;
-
 	res = vfs_follow_link(nd, kmap(page));
 	kunmap(page);
+	unlock_page(page);
 	page_cache_release(page);
 	return res;
 }
@@ -1328,7 +1500,7 @@
 	.follow_link	= shmem_follow_link,
 };
 
-static int shmem_parse_options(char *options, int *mode, uid_t *uid, gid_t *gid, unsigned long * blocks, unsigned long *inodes)
+static int shmem_parse_options(char *options, int *mode, uid_t *uid, gid_t *gid, unsigned long *blocks, unsigned long *inodes)
 {
 	char *this_char, *value, *rest;
 
@@ -1338,8 +1510,8 @@
 		if ((value = strchr(this_char,'=')) != NULL) {
 			*value++ = 0;
 		} else {
-			printk(KERN_ERR 
-			    "tmpfs: No value for mount option '%s'\n", 
+			printk(KERN_ERR
+			    "tmpfs: No value for mount option '%s'\n",
 			    this_char);
 			return 1;
 		}
@@ -1385,13 +1557,13 @@
 	return 0;
 
 bad_val:
-	printk(KERN_ERR "tmpfs: Bad value '%s' for mount option '%s'\n", 
+	printk(KERN_ERR "tmpfs: Bad value '%s' for mount option '%s'\n",
 	       value, this_char);
 	return 1;
 
 }
 
-static int shmem_remount_fs (struct super_block *sb, int *flags, char *data)
+static int shmem_remount_fs(struct super_block *sb, int *flags, char *data)
 {
 	struct shmem_sb_info *sbinfo = SHMEM_SB(sb);
 	unsigned long max_blocks = sbinfo->max_blocks;
@@ -1402,16 +1574,16 @@
 	return shmem_set_size(sbinfo, max_blocks, max_inodes);
 }
 
-int shmem_sync_file(struct file * file, struct dentry *dentry, int datasync)
+int shmem_sync_file(struct file *file, struct dentry *dentry, int datasync)
 {
 	return 0;
 }
 #endif
 
-static int shmem_fill_super(struct super_block * sb, void * data, int silent)
+static int shmem_fill_super(struct super_block *sb, void *data, int silent)
 {
-	struct inode * inode;
-	struct dentry * root;
+	struct inode *inode;
+	struct dentry *root;
 	unsigned long blocks, inodes;
 	int mode   = S_IRWXUGO | S_ISVTX;
 	uid_t uid = current->fsuid;
@@ -1442,7 +1614,7 @@
 	sb->s_flags |= MS_NOUSER;
 #endif
 
-	spin_lock_init (&sbinfo->stat_lock);
+	spin_lock_init(&sbinfo->stat_lock);
 	sbinfo->max_blocks = blocks;
 	sbinfo->free_blocks = blocks;
 	sbinfo->max_inodes = inodes;
@@ -1482,7 +1654,7 @@
 	sb->u.generic_sbp = NULL;
 }
 
-static kmem_cache_t * shmem_inode_cachep;
+static kmem_cache_t *shmem_inode_cachep;
 
 static struct inode *shmem_alloc_inode(struct super_block *sb)
 {
@@ -1498,7 +1670,7 @@
 	kmem_cache_free(shmem_inode_cachep, SHMEM_I(inode));
 }
 
-static void init_once(void * foo, kmem_cache_t * cachep, unsigned long flags)
+static void init_once(void *foo, kmem_cache_t *cachep, unsigned long flags)
 {
 	struct shmem_inode_info *p = (struct shmem_inode_info *) foo;
 
@@ -1507,7 +1679,7 @@
 		inode_init_once(&p->vfs_inode);
 	}
 }
- 
+
 static int init_inodecache(void)
 {
 	shmem_inode_cachep = kmem_cache_create("shmem_inode_cache",
@@ -1527,6 +1699,8 @@
 
 static struct address_space_operations shmem_aops = {
 	.writepage	= shmem_writepage,
+	.writepages	= shmem_writepages,
+	.vm_writeback	= shmem_vm_writeback,
 	.set_page_dirty	= __set_page_dirty_nobuffers,
 };
 
@@ -1600,7 +1774,7 @@
 static int __init init_shmem_fs(void)
 {
 	int error;
-	struct vfsmount * res;
+	struct vfsmount *res;
 
 	error = init_inodecache();
 	if (error)
@@ -1608,21 +1782,21 @@
 
 	error = register_filesystem(&tmpfs_fs_type);
 	if (error) {
-		printk (KERN_ERR "Could not register tmpfs\n");
+		printk(KERN_ERR "Could not register tmpfs\n");
 		goto out2;
 	}
 #ifdef CONFIG_TMPFS
 	error = register_filesystem(&shmem_fs_type);
 	if (error) {
-		printk (KERN_ERR "Could not register shm fs\n");
+		printk(KERN_ERR "Could not register shm fs\n");
 		goto out1;
 	}
-	devfs_mk_dir (NULL, "shm", NULL);
+	devfs_mk_dir(NULL, "shm", NULL);
 #endif
 	res = kern_mount(&tmpfs_fs_type);
 	if (IS_ERR (res)) {
 		error = PTR_ERR(res);
-		printk (KERN_ERR "could not kern_mount tmpfs\n");
+		printk(KERN_ERR "could not kern_mount tmpfs\n");
 		goto out;
 	}
 	shm_mnt = res;
@@ -1663,11 +1837,11 @@
  * @size: size to be set for the file
  *
  */
-struct file *shmem_file_setup(char * name, loff_t size, unsigned long flags)
+struct file *shmem_file_setup(char *name, loff_t size, unsigned long flags)
 {
 	int error;
 	struct file *file;
-	struct inode * inode;
+	struct inode *inode;
 	struct dentry *dentry, *root;
 	struct qstr this;
 
@@ -1693,7 +1867,7 @@
 
 	error = -ENOSPC;
 	inode = shmem_get_inode(root->d_sb, S_IFREG | S_IRWXUGO, 0);
-	if (!inode) 
+	if (!inode)
 		goto close_file;
 
 	SHMEM_I(inode)->flags &= flags;
@@ -1709,11 +1883,11 @@
 close_file:
 	put_filp(file);
 put_dentry:
-	dput (dentry);
+	dput(dentry);
 put_memory:
 	if (flags & VM_ACCOUNT)
 		vm_unacct_memory(VM_ACCT(size));
-	return ERR_PTR(error);	
+	return ERR_PTR(error);
 }
 
 /*
@@ -1725,13 +1899,13 @@
 {
 	struct file *file;
 	loff_t size = vma->vm_end - vma->vm_start;
-	
+
 	file = shmem_file_setup("dev/zero", size, vma->vm_flags);
 	if (IS_ERR(file))
 		return PTR_ERR(file);
 
 	if (vma->vm_file)
-		fput (vma->vm_file);
+		fput(vma->vm_file);
 	vma->vm_file = file;
 	vma->vm_ops = &shmem_vm_ops;
 	return 0;
diff -Nru a/mm/swap.c b/mm/swap.c
--- a/mm/swap.c	Mon Oct  7 11:25:32 2002
+++ b/mm/swap.c	Mon Oct  7 11:25:32 2002
@@ -38,7 +38,7 @@
 		del_page_from_inactive_list(zone, page);
 		SetPageActive(page);
 		add_page_to_active_list(zone, page);
-		KERNEL_STAT_INC(pgactivate);
+		inc_page_state(pgactivate);
 	}
 	spin_unlock_irq(&zone->lru_lock);
 }
@@ -236,6 +236,29 @@
 			unlock_page(page);
 		}
 	}
+}
+
+/**
+ * pagevec_lookup - gang pagecache lookup
+ * @pvec:	Where the resulting pages are placed
+ * @mapping:	The address_space to search
+ * @start:	The starting page index
+ * @nr_pages:	The maximum number of pages
+ *
+ * pagevec_lookup() will search for and return a group of up to @nr_pages pages
+ * in the mapping.  The pages are placed in @pvec.  pagevec_lookup() takes a
+ * reference against the pages in @pvec.
+ *
+ * The search returns a group of mapping-contiguous pages with ascending
+ * indexes.  There may be holes in the indices due to not-present pages.
+ *
+ * pagevec_lookup() returns the number of pages which were found.
+ */
+unsigned int pagevec_lookup(struct pagevec *pvec, struct address_space *mapping,
+		pgoff_t start, unsigned int nr_pages)
+{
+	pvec->nr = find_get_pages(mapping, start, nr_pages, pvec->pages);
+	return pagevec_count(pvec);
 }
 
 /*
diff -Nru a/mm/swap_state.c b/mm/swap_state.c
--- a/mm/swap_state.c	Mon Oct  7 11:25:34 2002
+++ b/mm/swap_state.c	Mon Oct  7 11:25:34 2002
@@ -105,7 +105,6 @@
 	BUG_ON(!PageLocked(page));
 	BUG_ON(!PageSwapCache(page));
 	BUG_ON(PageWriteback(page));
-	ClearPageDirty(page);
 	__remove_from_page_cache(page);
 	INC_CACHE_INFO(del_total);
 }
@@ -146,30 +145,30 @@
 		pf_flags = current->flags;
 		current->flags &= ~PF_MEMALLOC;
 		current->flags |= PF_NOWARN;
-		ClearPageUptodate(page);		/* why? */
 
 		/*
 		 * Add it to the swap cache and mark it dirty
-		 * (adding to the page cache will clear the dirty
-		 * and uptodate bits, so we need to do it again)
 		 */
-		switch (add_to_swap_cache(page, entry)) {
+		switch (add_to_page_cache(page, &swapper_space, entry.val)) {
 		case 0:				/* Success */
 			current->flags = pf_flags;
 			SetPageUptodate(page);
+			ClearPageDirty(page);
 			set_page_dirty(page);
-			swap_free(entry);
+			INC_CACHE_INFO(add_total);
 			return 1;
-		case -ENOMEM:			/* radix-tree allocation */
+		case -EEXIST:
+			/* Raced with "speculative" read_swap_cache_async */
+			current->flags = pf_flags;
+			INC_CACHE_INFO(exist_race);
+			swap_free(entry);
+			continue;
+		default:
+			/* -ENOMEM radix-tree allocation failure */
 			current->flags = pf_flags;
 			swap_free(entry);
 			return 0;
-		default:			/* ENOENT: raced */
-			break;
 		}
-		/* Raced with "speculative" read_swap_cache_async */
-		current->flags = pf_flags;
-		swap_free(entry);
 	}
 }
 
@@ -203,33 +202,13 @@
 	void **pslot;
 	int err;
 
-	if (!mapping)
-		BUG();
-
-	if (!swap_duplicate(entry)) {
-		INC_CACHE_INFO(noent_race);
-		return -ENOENT;
-	}
-
 	write_lock(&swapper_space.page_lock);
 	write_lock(&mapping->page_lock);
 
 	err = radix_tree_reserve(&swapper_space.page_tree, entry.val, &pslot);
 	if (!err) {
-		/* Remove it from the page cache */
 		__remove_from_page_cache(page);
-
-		/* Add it to the swap cache */
 		*pslot = page;
-		/*
-		 * This code used to clear PG_uptodate, PG_error, PG_arch1,
-		 * PG_referenced and PG_checked.  What _should_ it clear?
-		 */
-		ClearPageUptodate(page);
-		ClearPageReferenced(page);
-
-		SetPageLocked(page);
-		ClearPageDirty(page);
 		___add_to_page_cache(page, &swapper_space, entry.val);
 	}
 
@@ -237,21 +216,21 @@
 	write_unlock(&swapper_space.page_lock);
 
 	if (!err) {
+		if (!swap_duplicate(entry))
+			BUG();
+		/* shift page from clean_pages to dirty_pages list */
+		BUG_ON(PageDirty(page));
+		set_page_dirty(page);
 		INC_CACHE_INFO(add_total);
-		return 0;
-	}
-
-	swap_free(entry);
-
-	if (err == -EEXIST)
+	} else if (err == -EEXIST)
 		INC_CACHE_INFO(exist_race);
-
 	return err;
 }
 
 int move_from_swap_cache(struct page *page, unsigned long index,
 		struct address_space *mapping)
 {
+	swp_entry_t entry;
 	void **pslot;
 	int err;
 
@@ -259,44 +238,27 @@
 	BUG_ON(PageWriteback(page));
 	BUG_ON(page_has_buffers(page));
 
+	entry.val = page->index;
+
 	write_lock(&swapper_space.page_lock);
 	write_lock(&mapping->page_lock);
 
 	err = radix_tree_reserve(&mapping->page_tree, index, &pslot);
 	if (!err) {
-		swp_entry_t entry;
-
-		entry.val = page->index;
 		__delete_from_swap_cache(page);
-
 		*pslot = page;
-
-		/*
-		 * This code used to clear PG_uptodate, PG_error, PG_referenced,
-		 * PG_arch_1 and PG_checked.  It's not really clear why.
-		 */
-		ClearPageUptodate(page);
-		ClearPageReferenced(page);
-
-		/*
-		 * ___add_to_page_cache puts the page on ->clean_pages,
-		 * but it's dirty.  If it's on ->clean_pages, it will basically
-		 * never get written out.
-		 */
-		SetPageDirty(page);
 		___add_to_page_cache(page, mapping, index);
-		/* fix that up */
-		list_move(&page->list, &mapping->dirty_pages);
-		write_unlock(&mapping->page_lock);
-		write_unlock(&swapper_space.page_lock);
-
-		/* Do this outside ->page_lock */
-		swap_free(entry);
-		return 0;
 	}
 
 	write_unlock(&mapping->page_lock);
 	write_unlock(&swapper_space.page_lock);
+
+	if (!err) {
+		swap_free(entry);
+		/* shift page from clean_pages to dirty_pages list */
+		ClearPageDirty(page);
+		set_page_dirty(page);
+	}
 	return err;
 }
 
diff -Nru a/mm/swapfile.c b/mm/swapfile.c
--- a/mm/swapfile.c	Mon Oct  7 11:25:26 2002
+++ b/mm/swapfile.c	Mon Oct  7 11:25:26 2002
@@ -531,6 +531,7 @@
 	int i = 0;
 	int retval = 0;
 	int reset_overflow = 0;
+	int shmem;
 
 	/*
 	 * When searching mms for an entry, a good strategy is to
@@ -611,11 +612,12 @@
 		 * Whenever we reach init_mm, there's no address space
 		 * to search, but use it as a reminder to search shmem.
 		 */
+		shmem = 0;
 		swcount = *swap_map;
 		if (swcount > 1) {
 			flush_page_to_ram(page);
 			if (start_mm == &init_mm)
-				shmem_unuse(entry, page);
+				shmem = shmem_unuse(entry, page);
 			else
 				unuse_process(start_mm, entry, page);
 		}
@@ -632,7 +634,9 @@
 				swcount = *swap_map;
 				if (mm == &init_mm) {
 					set_start_mm = 1;
-					shmem_unuse(entry, page);
+					spin_unlock(&mmlist_lock);
+					shmem = shmem_unuse(entry, page);
+					spin_lock(&mmlist_lock);
 				} else
 					unuse_process(mm, entry, page);
 				if (set_start_mm && *swap_map < swcount) {
@@ -681,15 +685,24 @@
 		 * read from disk into another page.  Splitting into two
 		 * pages would be incorrect if swap supported "shared
 		 * private" pages, but they are handled by tmpfs files.
-		 * Note shmem_unuse already deleted its from swap cache.
+		 *
+		 * Note shmem_unuse already deleted a swappage from
+		 * the swap cache, unless the move to filepage failed:
+		 * in which case it left swappage in cache, lowered its
+		 * swap count to pass quickly through the loops above,
+		 * and now we must reincrement count to try again later.
 		 */
 		if ((*swap_map > 1) && PageDirty(page) && PageSwapCache(page)) {
 			swap_writepage(page);
 			lock_page(page);
 			wait_on_page_writeback(page);
 		}
-		if (PageSwapCache(page))
-			delete_from_swap_cache(page);
+		if (PageSwapCache(page)) {
+			if (shmem)
+				swap_duplicate(entry);
+			else
+				delete_from_swap_cache(page);
+		}
 
 		/*
 		 * So we could skip searching mms once swap count went
diff -Nru a/mm/truncate.c b/mm/truncate.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/mm/truncate.c	Mon Oct  7 11:26:06 2002
@@ -0,0 +1,223 @@
+/*
+ * mm/truncate.c - code for taking down pages from address_spaces
+ *
+ * Copyright (C) 2002, Linus Torvalds
+ *
+ * 10Sep2002	akpm@zip.com.au
+ *		Initial version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/pagemap.h>
+#include <linux/pagevec.h>
+#include <linux/buffer_head.h>	/* grr. try_to_release_page,
+				   block_invalidatepage */
+
+
+static int do_invalidatepage(struct page *page, unsigned long offset)
+{
+	int (*invalidatepage)(struct page *, unsigned long);
+	invalidatepage = page->mapping->a_ops->invalidatepage;
+	if (invalidatepage == NULL)
+		invalidatepage = block_invalidatepage;
+	return (*invalidatepage)(page, offset);
+}
+
+static inline void truncate_partial_page(struct page *page, unsigned partial)
+{
+	memclear_highpage_flush(page, partial, PAGE_CACHE_SIZE-partial);
+	if (PagePrivate(page))
+		do_invalidatepage(page, partial);
+}
+
+/*
+ * If truncate cannot remove the fs-private metadata from the page, the page
+ * becomes anonymous.  It will be left on the LRU and may even be mapped into
+ * user pagetables if we're racing with filemap_nopage().
+ *
+ * We need to bale out if page->mapping is no longer equal to the original
+ * mapping.  This happens a) when the VM reclaimed the page while we waited on
+ * its lock, b) when a concurrent invalidate_inode_pages got there first and
+ * c) when tmpfs swizzles a page between a tmpfs inode and swapper_space.
+ */
+static void
+truncate_complete_page(struct address_space *mapping, struct page *page)
+{
+	if (page->mapping != mapping)
+		return;
+
+	if (PagePrivate(page))
+		do_invalidatepage(page, 0);
+
+	clear_page_dirty(page);
+	ClearPageUptodate(page);
+	remove_from_page_cache(page);
+	page_cache_release(page);
+}
+
+/**
+ * truncate_inode_pages - truncate *all* the pages from an offset
+ * @mapping: mapping to truncate
+ * @lstart: offset from which to truncate
+ *
+ * Truncate the page cache at a set offset, removing the pages that are beyond
+ * that offset (and zeroing out partial pages).
+ *
+ * Truncate takes two passes - the first pass is nonblocking.  It will not
+ * block on page locks and it will not block on writeback.  The second pass
+ * will wait.  This is to prevent as much IO as possible in the affected region.
+ * The first pass will remove most pages, so the search cost of the second pass
+ * is low.
+ *
+ * When looking at page->index outside the page lock we need to be careful to
+ * copy it into a local to avoid races (it could change at any time).
+ *
+ * Called under (and serialised by) inode->i_sem.
+ */
+void truncate_inode_pages(struct address_space *mapping, loff_t lstart)
+{
+	const pgoff_t start = (lstart + PAGE_CACHE_SIZE-1) >> PAGE_CACHE_SHIFT;
+	const unsigned partial = lstart & (PAGE_CACHE_SIZE - 1);
+	struct pagevec pvec;
+	pgoff_t next;
+	int i;
+
+	pagevec_init(&pvec);
+	next = start;
+	while (pagevec_lookup(&pvec, mapping, next, PAGEVEC_SIZE)) {
+		for (i = 0; i < pagevec_count(&pvec); i++) {
+			struct page *page = pvec.pages[i];
+			pgoff_t page_index = page->index;
+
+			if (page_index > next)
+				next = page_index;
+			next++;
+			if (TestSetPageLocked(page))
+				continue;
+			if (PageWriteback(page)) {
+				unlock_page(page);
+				continue;
+			}
+			truncate_complete_page(mapping, page);
+			unlock_page(page);
+		}
+		pagevec_release(&pvec);
+		cond_resched();
+	}
+
+	if (partial) {
+		struct page *page = find_lock_page(mapping, start - 1);
+		if (page) {
+			wait_on_page_writeback(page);
+			truncate_partial_page(page, partial);
+			unlock_page(page);
+			page_cache_release(page);
+		}
+	}
+
+	next = start;
+	for ( ; ; ) {
+		if (!pagevec_lookup(&pvec, mapping, next, PAGEVEC_SIZE)) {
+			if (next == start)
+				break;
+			next = start;
+			continue;
+		}
+		for (i = 0; i < pagevec_count(&pvec); i++) {
+			struct page *page = pvec.pages[i];
+
+			lock_page(page);
+			wait_on_page_writeback(page);
+			if (page->index > next)
+				next = page->index;
+			next++;
+			truncate_complete_page(mapping, page);
+			unlock_page(page);
+		}
+		pagevec_release(&pvec);
+	}
+	if (lstart == 0 && mapping->nrpages)
+		printk("%s: I goofed!\n", __FUNCTION__);
+}
+
+/**
+ * invalidate_inode_pages - Invalidate all the unlocked pages of one inode
+ * @inode: the inode which pages we want to invalidate
+ *
+ * This function only removes the unlocked pages, if you want to
+ * remove all the pages of one inode, you must call truncate_inode_pages.
+ *
+ * invalidate_inode_pages() will not block on IO activity. It will not
+ * invalidate pages which are dirty, locked, under writeback or mapped into
+ * pagetables.
+ */
+void invalidate_inode_pages(struct address_space *mapping)
+{
+	struct pagevec pvec;
+	pgoff_t next = 0;
+	int i;
+
+	pagevec_init(&pvec);
+	while (pagevec_lookup(&pvec, mapping, next, PAGEVEC_SIZE)) {
+		for (i = 0; i < pagevec_count(&pvec); i++) {
+			struct page *page = pvec.pages[i];
+
+			if (TestSetPageLocked(page)) {
+				next++;
+				continue;
+			}
+			if (page->index > next)
+				next = page->index;
+			next++;
+			if (PageDirty(page) || PageWriteback(page))
+				goto unlock;
+			if (PagePrivate(page) && !try_to_release_page(page, 0))
+				goto unlock;
+			if (page_mapped(page))
+				goto unlock;
+			truncate_complete_page(mapping, page);
+unlock:
+			unlock_page(page);
+		}
+		pagevec_release(&pvec);
+		cond_resched();
+	}
+}
+
+/**
+ * invalidate_inode_pages2 - remove all unmapped pages from an address_space
+ * @mapping - the address_space
+ *
+ * invalidate_inode_pages2() is like truncate_inode_pages(), except for the case
+ * where the page is seen to be mapped into process pagetables.  In that case,
+ * the page is marked clean but is left attached to its address_space.
+ *
+ * FIXME: invalidate_inode_pages2() is probably trivially livelockable.
+ */
+void invalidate_inode_pages2(struct address_space *mapping)
+{
+	struct pagevec pvec;
+	pgoff_t next = 0;
+	int i;
+
+	pagevec_init(&pvec);
+	while (pagevec_lookup(&pvec, mapping, next, PAGEVEC_SIZE)) {
+		for (i = 0; i < pagevec_count(&pvec); i++) {
+			struct page *page = pvec.pages[i];
+
+			lock_page(page);
+			if (page->mapping == mapping) {	/* truncate race? */
+				wait_on_page_writeback(page);
+				next = page->index + 1;
+				if (page_mapped(page))
+					clear_page_dirty(page);
+				else
+					truncate_complete_page(mapping, page);
+			}
+			unlock_page(page);
+		}
+		pagevec_release(&pvec);
+		cond_resched();
+	}
+}
diff -Nru a/mm/vmscan.c b/mm/vmscan.c
--- a/mm/vmscan.c	Mon Oct  7 11:25:26 2002
+++ b/mm/vmscan.c	Mon Oct  7 11:25:26 2002
@@ -101,15 +101,19 @@
 	return page_count(page) - !!PagePrivate(page) == 2;
 }
 
+
+/*
+ * shrink_list returns the number of reclaimed pages
+ */
 static /* inline */ int
-shrink_list(struct list_head *page_list, int nr_pages,
-		unsigned int gfp_mask, int *max_scan, int *nr_mapped)
+shrink_list(struct list_head *page_list, unsigned int gfp_mask,
+		int *max_scan, int *nr_mapped)
 {
 	struct address_space *mapping;
 	LIST_HEAD(ret_pages);
 	struct pagevec freed_pvec;
-	const int nr_pages_in = nr_pages;
 	int pgactivate = 0;
+	int ret = 0;
 
 	pagevec_init(&freed_pvec);
 	while (!list_empty(page_list)) {
@@ -295,7 +299,7 @@
 		__put_page(page);	/* The pagecache ref */
 free_it:
 		unlock_page(page);
-		nr_pages--;
+		ret++;
 		if (!pagevec_add(&freed_pvec, page))
 			__pagevec_release_nonlru(&freed_pvec);
 		continue;
@@ -312,9 +316,11 @@
 	list_splice(&ret_pages, page_list);
 	if (pagevec_count(&freed_pvec))
 		__pagevec_release_nonlru(&freed_pvec);
-	KERNEL_STAT_ADD(pgsteal, nr_pages_in - nr_pages);
-	KERNEL_STAT_ADD(pgactivate, pgactivate);
-	return nr_pages;
+	mod_page_state(pgsteal, ret);
+	if (current->flags & PF_KSWAPD)
+		mod_page_state(kswapd_steal, ret);
+	mod_page_state(pgactivate, pgactivate);
+	return ret;
 }
 
 /*
@@ -323,18 +329,19 @@
  * not freed will be added back to the LRU.
  *
  * shrink_cache() is passed the number of pages to try to free, and returns
- * the number which are yet-to-free.
+ * the number of pages which were reclaimed.
  *
  * For pagecache intensive workloads, the first loop here is the hottest spot
  * in the kernel (apart from the copy_*_user functions).
  */
 static /* inline */ int
-shrink_cache(int nr_pages, struct zone *zone,
+shrink_cache(const int nr_pages, struct zone *zone,
 		unsigned int gfp_mask, int max_scan, int *nr_mapped)
 {
 	LIST_HEAD(page_list);
 	struct pagevec pvec;
 	int nr_to_process;
+	int ret = 0;
 
 	/*
 	 * Try to ensure that we free `nr_pages' pages in one pass of the loop.
@@ -347,10 +354,11 @@
 
 	lru_add_drain();
 	spin_lock_irq(&zone->lru_lock);
-	while (max_scan > 0 && nr_pages > 0) {
+	while (max_scan > 0 && ret < nr_pages) {
 		struct page *page;
 		int nr_taken = 0;
 		int nr_scan = 0;
+		int nr_freed;
 
 		while (nr_scan++ < nr_to_process &&
 				!list_empty(&zone->inactive_list)) {
@@ -380,11 +388,11 @@
 			goto done;
 
 		max_scan -= nr_scan;
-		KERNEL_STAT_ADD(pgscan, nr_scan);
-		nr_pages = shrink_list(&page_list, nr_pages,
-				gfp_mask, &max_scan, nr_mapped);
-
-		if (nr_pages <= 0 && list_empty(&page_list))
+		mod_page_state(pgscan, nr_scan);
+		nr_freed = shrink_list(&page_list, gfp_mask,
+					&max_scan, nr_mapped);
+		ret += nr_freed;
+		if (nr_freed <= 0 && list_empty(&page_list))
 			goto done;
 
 		spin_lock_irq(&zone->lru_lock);
@@ -410,7 +418,7 @@
 	spin_unlock_irq(&zone->lru_lock);
 done:
 	pagevec_release(&pvec);
-	return nr_pages;	
+	return ret;
 }
 
 /*
@@ -527,13 +535,18 @@
 	spin_unlock_irq(&zone->lru_lock);
 	pagevec_release(&pvec);
 
-	KERNEL_STAT_ADD(pgscan, nr_pages_in - nr_pages);
-	KERNEL_STAT_ADD(pgdeactivate, pgdeactivate);
+	mod_page_state(pgrefill, nr_pages_in - nr_pages);
+	mod_page_state(pgdeactivate, pgdeactivate);
 }
 
+/*
+ * Try to reclaim `nr_pages' from this zone.  Returns the number of reclaimed
+ * pages.  This is a basic per-zone page freer.  Used by both kswapd and
+ * direct reclaim.
+ */
 static /* inline */ int
-shrink_zone(struct zone *zone, int max_scan,
-		unsigned int gfp_mask, int nr_pages, int *nr_mapped)
+shrink_zone(struct zone *zone, int max_scan, unsigned int gfp_mask,
+		const int nr_pages, int *nr_mapped)
 {
 	unsigned long ratio;
 
@@ -554,36 +567,60 @@
 		atomic_sub(SWAP_CLUSTER_MAX, &zone->refill_counter);
 		refill_inactive_zone(zone, SWAP_CLUSTER_MAX);
 	}
-	nr_pages = shrink_cache(nr_pages, zone, gfp_mask,
-				max_scan, nr_mapped);
-	return nr_pages;
+	return shrink_cache(nr_pages, zone, gfp_mask, max_scan, nr_mapped);
+}
+
+/*
+ * FIXME: don't do this for ZONE_HIGHMEM
+ */
+/*
+ * Here we assume it costs one seek to replace a lru page and that it also
+ * takes a seek to recreate a cache object.  With this in mind we age equal
+ * percentages of the lru and ageable caches.  This should balance the seeks
+ * generated by these structures.
+ *
+ * NOTE: for now I do this for all zones.  If we find this is too aggressive
+ * on large boxes we may want to exclude ZONE_HIGHMEM.
+ *
+ * If we're encountering mapped pages on the LRU then increase the pressure on
+ * slab to avoid swapping.
+ */
+static void shrink_slab(int total_scanned, int gfp_mask)
+{
+	int shrink_ratio;
+	int pages = nr_used_zone_pages();
+
+	shrink_ratio = (pages / (total_scanned + 1)) + 1;
+	shrink_dcache_memory(shrink_ratio, gfp_mask);
+	shrink_icache_memory(shrink_ratio, gfp_mask);
+	shrink_dqcache_memory(shrink_ratio, gfp_mask);
 }
 
+/*
+ * This is the direct reclaim path, for page-allocating processes.  We only
+ * try to reclaim pages from zones which will satisfy the caller's allocation
+ * request.
+ */
 static int
-shrink_caches(struct zone *classzone, int priority,
-		int *total_scanned, int gfp_mask, int nr_pages)
+shrink_caches(struct zone *classzone, int priority, int *total_scanned,
+		int gfp_mask, const int nr_pages, int order)
 {
 	struct zone *first_classzone;
 	struct zone *zone;
-	int ratio;
 	int nr_mapped = 0;
-	int pages = nr_used_zone_pages();
+	int ret = 0;
 
 	first_classzone = classzone->zone_pgdat->node_zones;
 	for (zone = classzone; zone >= first_classzone; zone--) {
 		int max_scan;
 		int to_reclaim;
-		int unreclaimed;
 
 		to_reclaim = zone->pages_high - zone->free_pages;
-		if (to_reclaim < 0)
+		if (order == 0 && to_reclaim < 0)
 			continue;	/* zone has enough memory */
 
-		if (to_reclaim > SWAP_CLUSTER_MAX)
-			to_reclaim = SWAP_CLUSTER_MAX;
-
-		if (to_reclaim < nr_pages)
-			to_reclaim = nr_pages;
+		to_reclaim = min(to_reclaim, SWAP_CLUSTER_MAX);
+		to_reclaim = max(to_reclaim, nr_pages);
 
 		/*
 		 * If we cannot reclaim `nr_pages' pages by scanning twice
@@ -592,33 +629,18 @@
 		max_scan = zone->nr_inactive >> priority;
 		if (max_scan < to_reclaim * 2)
 			max_scan = to_reclaim * 2;
-		unreclaimed = shrink_zone(zone, max_scan,
-				gfp_mask, to_reclaim, &nr_mapped);
-		nr_pages -= to_reclaim - unreclaimed;
+		ret += shrink_zone(zone, max_scan, gfp_mask,
+				to_reclaim, &nr_mapped);
 		*total_scanned += max_scan;
+		*total_scanned += nr_mapped;
+		if (ret >= nr_pages)
+			break;
 	}
-
-	/*
-	 * Here we assume it costs one seek to replace a lru page and that
-	 * it also takes a seek to recreate a cache object.  With this in
-	 * mind we age equal percentages of the lru and ageable caches.
-	 * This should balance the seeks generated by these structures.
-	 *
-	 * NOTE: for now I do this for all zones.  If we find this is too
-	 * aggressive on large boxes we may want to exclude ZONE_HIGHMEM
-	 *
-	 * If we're encountering mapped pages on the LRU then increase the
-	 * pressure on slab to avoid swapping.
-	 */
-	ratio = (pages / (*total_scanned + nr_mapped + 1)) + 1;
-	shrink_dcache_memory(ratio, gfp_mask);
-	shrink_icache_memory(ratio, gfp_mask);
-	shrink_dqcache_memory(ratio, gfp_mask);
-	return nr_pages;
+	return ret;
 }
 
 /*
- * This is the main entry point to page reclaim.
+ * This is the main entry point to direct page reclaim.
  *
  * If a full scan of the inactive list fails to free enough memory then we
  * are "out of memory" and something needs to be killed.
@@ -638,17 +660,19 @@
 try_to_free_pages(struct zone *classzone,
 		unsigned int gfp_mask, unsigned int order)
 {
-	int priority = DEF_PRIORITY;
-	int nr_pages = SWAP_CLUSTER_MAX;
+	int priority;
+	const int nr_pages = SWAP_CLUSTER_MAX;
+	int nr_reclaimed = 0;
 
-	KERNEL_STAT_INC(pageoutrun);
+	inc_page_state(pageoutrun);
 
 	for (priority = DEF_PRIORITY; priority; priority--) {
 		int total_scanned = 0;
 
-		nr_pages = shrink_caches(classzone, priority, &total_scanned,
-					gfp_mask, nr_pages);
-		if (nr_pages <= 0)
+		nr_reclaimed += shrink_caches(classzone, priority,
+					&total_scanned, gfp_mask,
+					nr_pages, order);
+		if (nr_reclaimed >= nr_pages)
 			return 1;
 		if (total_scanned == 0)
 			return 1;	/* All zones had enough free memory */
@@ -663,62 +687,46 @@
 
 		/* Take a nap, wait for some writeback to complete */
 		blk_congestion_wait(WRITE, HZ/4);
+		shrink_slab(total_scanned, gfp_mask);
 	}
 	if (gfp_mask & __GFP_FS)
 		out_of_memory();
 	return 0;
 }
 
-static int check_classzone_need_balance(struct zone *classzone)
+/*
+ * kswapd will work across all this node's zones until they are all at
+ * pages_high.
+ */
+static void kswapd_balance_pgdat(pg_data_t *pgdat)
 {
-	struct zone *first_classzone;
+	int priority = DEF_PRIORITY;
+	int i;
 
-	first_classzone = classzone->zone_pgdat->node_zones;
-	while (classzone >= first_classzone) {
-		if (classzone->free_pages > classzone->pages_high)
-			return 0;
-		classzone--;
-	}
-	return 1;
-}
+	for (priority = DEF_PRIORITY; priority; priority--) {
+		int success = 1;
 
-static int kswapd_balance_pgdat(pg_data_t * pgdat)
-{
-	int need_more_balance = 0, i;
-	struct zone *zone;
+		for (i = 0; i < pgdat->nr_zones; i++) {
+			struct zone *zone = pgdat->node_zones + i;
+			int nr_mapped = 0;
+			int max_scan;
+			int to_reclaim;
 
-	for (i = pgdat->nr_zones-1; i >= 0; i--) {
-		zone = pgdat->node_zones + i;
-		cond_resched();
-		if (!zone->need_balance)
-			continue;
-		if (!try_to_free_pages(zone, GFP_KSWAPD, 0)) {
-			zone->need_balance = 0;
-			__set_current_state(TASK_INTERRUPTIBLE);
-			schedule_timeout(HZ);
-			continue;
+			to_reclaim = zone->pages_high - zone->free_pages;
+			if (to_reclaim <= 0)
+				continue;
+			success = 0;
+			max_scan = zone->nr_inactive >> priority;
+			if (max_scan < to_reclaim * 2)
+				max_scan = to_reclaim * 2;
+			shrink_zone(zone, max_scan, GFP_KSWAPD,
+					to_reclaim, &nr_mapped);
+			shrink_slab(max_scan + nr_mapped, GFP_KSWAPD);
 		}
-		if (check_classzone_need_balance(zone))
-			need_more_balance = 1;
-		else
-			zone->need_balance = 0;
-	}
-
-	return need_more_balance;
-}
-
-static int kswapd_can_sleep_pgdat(pg_data_t * pgdat)
-{
-	struct zone *zone;
-	int i;
-
-	for (i = pgdat->nr_zones-1; i >= 0; i--) {
-		zone = pgdat->node_zones + i;
-		if (zone->need_balance)
-			return 0;
+		if (success)
+			break;	/* All zones are at pages_high */
+		blk_congestion_wait(WRITE, HZ/4);
 	}
-
-	return 1;
 }
 
 /*
@@ -738,7 +746,7 @@
 {
 	pg_data_t *pgdat = (pg_data_t*)p;
 	struct task_struct *tsk = current;
-	DECLARE_WAITQUEUE(wait, tsk);
+	DEFINE_WAIT(wait);
 
 	daemonize();
 	set_cpus_allowed(tsk, __node_to_cpu_mask(pgdat->node_id));
@@ -757,29 +765,14 @@
 	 * us from recursively trying to free more memory as we're
 	 * trying to free the first piece of memory in the first place).
 	 */
-	tsk->flags |= PF_MEMALLOC;
+	tsk->flags |= PF_MEMALLOC|PF_KSWAPD;
 
-	/*
-	 * Kswapd main loop.
-	 */
-	for (;;) {
+	for ( ; ; ) {
 		if (current->flags & PF_FREEZE)
 			refrigerator(PF_IOTHREAD);
-		__set_current_state(TASK_INTERRUPTIBLE);
-		add_wait_queue(&pgdat->kswapd_wait, &wait);
-
-		mb();
-		if (kswapd_can_sleep_pgdat(pgdat))
-			schedule();
-
-		__set_current_state(TASK_RUNNING);
-		remove_wait_queue(&pgdat->kswapd_wait, &wait);
-
-		/*
-		 * If we actually get into a low-memory situation,
-		 * the processes needing more memory will wake us
-		 * up on a more timely basis.
-		 */
+		prepare_to_wait(&pgdat->kswapd_wait, &wait, TASK_INTERRUPTIBLE);
+		schedule();
+		finish_wait(&pgdat->kswapd_wait, &wait);
 		kswapd_balance_pgdat(pgdat);
 		blk_run_queues();
 	}
diff -Nru a/net/8021q/vlan.c b/net/8021q/vlan.c
--- a/net/8021q/vlan.c	Mon Oct  7 11:25:37 2002
+++ b/net/8021q/vlan.c	Mon Oct  7 11:25:37 2002
@@ -207,7 +207,7 @@
 #endif
 
 	/* sanity check */
-	if ((vlan_id >= VLAN_VID_MASK) || (vlan_id <= 0))
+	if (vlan_id >= VLAN_VID_MASK)
 		return -EINVAL;
 
 	spin_lock_bh(&vlan_group_lock);
diff -Nru a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c
--- a/net/8021q/vlan_dev.c	Mon Oct  7 11:25:38 2002
+++ b/net/8021q/vlan_dev.c	Mon Oct  7 11:25:38 2002
@@ -573,6 +573,7 @@
 					dev_put(dev);
 					return 0;
 				}
+				mp = mp->next;
 			}
 
 			/* Create a new mapping then. */
diff -Nru a/net/appletalk/Makefile b/net/appletalk/Makefile
--- a/net/appletalk/Makefile	Mon Oct  7 11:25:36 2002
+++ b/net/appletalk/Makefile	Mon Oct  7 11:25:36 2002
@@ -6,7 +6,7 @@
 
 obj-$(CONFIG_ATALK) += appletalk.o
 
-appletalk-y			:= aarp.o ddp.o
+appletalk-y			:= aarp.o ddp.o atalk_proc.o
 appletalk-$(CONFIG_SYSCTL)	+= sysctl_net_atalk.o
 appletalk-objs			:= $(appletalk-y)
 
diff -Nru a/net/appletalk/aarp.c b/net/appletalk/aarp.c
--- a/net/appletalk/aarp.c	Mon Oct  7 11:25:33 2002
+++ b/net/appletalk/aarp.c	Mon Oct  7 11:25:33 2002
@@ -30,34 +30,13 @@
  */
 
 #include <linux/config.h>
-#include <asm/uaccess.h>
-#include <asm/system.h>
-#include <asm/bitops.h>
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/string.h>
-#include <linux/mm.h>
-#include <linux/socket.h>
-#include <linux/sockios.h>
-#include <linux/in.h>
-#include <linux/errno.h>
-#include <linux/interrupt.h>
-#include <linux/if_ether.h>
-#include <linux/inet.h>
-#include <linux/notifier.h>
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
 #include <linux/if_arp.h>
-#include <linux/skbuff.h>
-#include <linux/spinlock.h>
 #include <net/sock.h>
 #include <net/datalink.h>
 #include <net/psnap.h>
 #include <linux/atalk.h>
 #include <linux/init.h>
 #include <linux/proc_fs.h>
-#include <linux/module.h>
 
 int sysctl_aarp_expiry_time = AARP_EXPIRY_TIME;
 int sysctl_aarp_tick_time = AARP_TICK_TIME;
@@ -995,4 +974,3 @@
 	proc_net_remove("aarp");
 }
 #endif
-MODULE_LICENSE("GPL");
diff -Nru a/net/appletalk/atalk_proc.c b/net/appletalk/atalk_proc.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/net/appletalk/atalk_proc.c	Mon Oct  7 11:26:06 2002
@@ -0,0 +1,319 @@
+/*
+ * 	atalk_proc.c - proc support for Appletalk
+ *
+ * 	Copyright(c) Arnaldo Carvalho de Melo <acme@conectiva.com.br>
+ *
+ *	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, version 2.
+ */
+
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <net/sock.h>
+#include <linux/atalk.h>
+
+#ifdef CONFIG_PROC_FS
+static __inline__ struct atalk_iface *atalk_get_interface_idx(loff_t pos)
+{
+	struct atalk_iface *i;
+
+	for (i = atalk_interfaces; pos && i; i = i->next)
+		--pos;
+
+	return i;
+}
+
+static void *atalk_seq_interface_start(struct seq_file *seq, loff_t *pos)
+{
+	loff_t l = *pos;
+
+	spin_lock_bh(&atalk_interfaces_lock);
+	return l ? atalk_get_interface_idx(--l) : (void *)1;
+}
+
+static void *atalk_seq_interface_next(struct seq_file *seq, void *v, loff_t *pos)
+{
+	struct atalk_iface *i;
+
+	++*pos;
+	if (v == (void *)1) {
+		i = NULL;
+		if (atalk_interfaces)
+			i = atalk_interfaces;
+		goto out;
+	}
+	i = v;
+	i = i->next;
+out:
+	return i;
+}
+
+static void atalk_seq_interface_stop(struct seq_file *seq, void *v)
+{
+	spin_unlock_bh(&atalk_interfaces_lock);
+}
+
+static int atalk_seq_interface_show(struct seq_file *seq, void *v)
+{
+	struct atalk_iface *iface;
+
+	if (v == (void *)1) {
+		seq_puts(seq, "Interface	  Address   Networks   "
+			      "Status\n");
+		goto out;
+	}
+
+	iface = v;
+	seq_printf(seq, "%-16s %04X:%02X  %04X-%04X  %d\n",
+		   iface->dev->name, ntohs(iface->address.s_net),
+		   iface->address.s_node, ntohs(iface->nets.nr_firstnet),
+		   ntohs(iface->nets.nr_lastnet), iface->status);
+out:
+	return 0;
+}
+
+static __inline__ struct atalk_route *atalk_get_route_idx(loff_t pos)
+{
+	struct atalk_route *r;
+
+	for (r = atalk_routes; pos && r; r = r->next)
+		--pos;
+
+	return r;
+}
+
+static void *atalk_seq_route_start(struct seq_file *seq, loff_t *pos)
+{
+	loff_t l = *pos;
+
+	read_lock_bh(&atalk_routes_lock);
+	return l ? atalk_get_route_idx(--l) : (void *)1;
+}
+
+static void *atalk_seq_route_next(struct seq_file *seq, void *v, loff_t *pos)
+{
+	struct atalk_route *r;
+
+	++*pos;
+	if (v == (void *)1) {
+		r = NULL;
+		if (atalk_routes)
+			r = atalk_routes;
+		goto out;
+	}
+	r = v;
+	r = r->next;
+out:
+	return r;
+}
+
+static void atalk_seq_route_stop(struct seq_file *seq, void *v)
+{
+	read_unlock_bh(&atalk_routes_lock);
+}
+
+static int atalk_seq_route_show(struct seq_file *seq, void *v)
+{
+	struct atalk_route *rt;
+
+	if (v == (void *)1) {
+		seq_puts(seq, "Target        Router  Flags Dev\n");
+		goto out;
+	}
+
+	if (atrtr_default.dev) {
+		rt = &atrtr_default;
+		seq_printf(seq, "Default     %04X:%02X  %-4d  %s\n",
+			       ntohs(rt->gateway.s_net), rt->gateway.s_node,
+			       rt->flags, rt->dev->name);
+	}
+
+	rt = v;
+	seq_printf(seq, "%04X:%02X     %04X:%02X  %-4d  %s\n",
+		   ntohs(rt->target.s_net), rt->target.s_node,
+		   ntohs(rt->gateway.s_net), rt->gateway.s_node,
+		   rt->flags, rt->dev->name);
+out:
+	return 0;
+}
+
+static __inline__ struct sock *atalk_get_socket_idx(loff_t pos)
+{
+	struct sock *s;
+
+	for (s = atalk_sockets; pos && s; s = s->next)
+		--pos;
+
+	return s;
+}
+
+static void *atalk_seq_socket_start(struct seq_file *seq, loff_t *pos)
+{
+	loff_t l = *pos;
+
+	spin_lock_bh(&atalk_sockets_lock);
+	return l ? atalk_get_socket_idx(--l) : (void *)1;
+}
+
+static void *atalk_seq_socket_next(struct seq_file *seq, void *v, loff_t *pos)
+{
+	struct sock *i;
+
+	++*pos;
+	if (v == (void *)1) {
+		i = NULL;
+		if (atalk_sockets)
+			i = atalk_sockets;
+		goto out;
+	}
+	i = v;
+	i = i->next;
+out:
+	return i;
+}
+
+static void atalk_seq_socket_stop(struct seq_file *seq, void *v)
+{
+	spin_unlock_bh(&atalk_sockets_lock);
+}
+
+static int atalk_seq_socket_show(struct seq_file *seq, void *v)
+{
+	struct sock *s;
+	struct atalk_sock *at;
+
+	if (v == (void *)1) {
+		seq_printf(seq, "Type Local_addr  Remote_addr Tx_queue "
+				"Rx_queue St UID\n");
+		goto out;
+	}
+
+	s = v;
+	at = at_sk(s);
+
+	seq_printf(seq, "%02X   %04X:%02X:%02X  %04X:%02X:%02X  %08X:%08X "
+			"%02X %d\n",
+		   s->type, ntohs(at->src_net), at->src_node, at->src_port,
+		   ntohs(at->dest_net), at->dest_node, at->dest_port,
+		   atomic_read(&s->wmem_alloc), atomic_read(&s->rmem_alloc),
+		   s->state, SOCK_INODE(s->socket)->i_uid);
+out:
+	return 0;
+}
+
+struct seq_operations atalk_seq_interface_ops = {
+	.start  = atalk_seq_interface_start,
+	.next   = atalk_seq_interface_next,
+	.stop   = atalk_seq_interface_stop,
+	.show   = atalk_seq_interface_show,
+};
+
+struct seq_operations atalk_seq_route_ops = {
+	.start  = atalk_seq_route_start,
+	.next   = atalk_seq_route_next,
+	.stop   = atalk_seq_route_stop,
+	.show   = atalk_seq_route_show,
+};
+
+struct seq_operations atalk_seq_socket_ops = {
+	.start  = atalk_seq_socket_start,
+	.next   = atalk_seq_socket_next,
+	.stop   = atalk_seq_socket_stop,
+	.show   = atalk_seq_socket_show,
+};
+
+static int atalk_seq_interface_open(struct inode *inode, struct file *file)
+{
+	return seq_open(file, &atalk_seq_interface_ops);
+}
+
+static int atalk_seq_route_open(struct inode *inode, struct file *file)
+{
+	return seq_open(file, &atalk_seq_route_ops);
+}
+
+static int atalk_seq_socket_open(struct inode *inode, struct file *file)
+{
+	return seq_open(file, &atalk_seq_socket_ops);
+}
+
+static struct file_operations atalk_seq_interface_fops = {
+	.open		= atalk_seq_interface_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= seq_release,
+};
+
+static struct file_operations atalk_seq_route_fops = {
+	.open		= atalk_seq_route_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= seq_release,
+};
+
+static struct file_operations atalk_seq_socket_fops = {
+	.open		= atalk_seq_socket_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= seq_release,
+};
+
+static struct proc_dir_entry *atalk_proc_dir;
+
+int __init atalk_proc_init(void)
+{
+	struct proc_dir_entry *p;
+	int rc = -ENOMEM;
+
+	atalk_proc_dir = proc_mkdir("atalk", proc_net);
+	if (!atalk_proc_dir)
+		goto out;
+
+	p = create_proc_entry("interface", S_IRUGO, atalk_proc_dir);
+	if (!p)
+		goto out_interface;
+	p->proc_fops = &atalk_seq_interface_fops;
+
+	p = create_proc_entry("route", S_IRUGO, atalk_proc_dir);
+	if (!p)
+		goto out_route;
+	p->proc_fops = &atalk_seq_route_fops;
+
+	p = create_proc_entry("socket", S_IRUGO, atalk_proc_dir);
+	if (!p)
+		goto out_socket;
+	p->proc_fops = &atalk_seq_socket_fops;
+
+	rc = 0;
+out:
+	return rc;
+out_socket:
+	remove_proc_entry("route", atalk_proc_dir);
+out_route:
+	remove_proc_entry("interface", atalk_proc_dir);
+out_interface:
+	remove_proc_entry("atalk", proc_net);
+	goto out;
+}
+
+void __exit atalk_proc_exit(void)
+{
+	remove_proc_entry("interface", atalk_proc_dir);
+	remove_proc_entry("route", atalk_proc_dir);
+	remove_proc_entry("socket", atalk_proc_dir);
+	remove_proc_entry("atalk", proc_net);
+}
+
+#else /* CONFIG_PROC_FS */
+int __init atalk_proc_init(void)
+{
+	return 0;
+}
+
+void __exit atalk_proc_exit(void)
+{
+}
+#endif /* CONFIG_PROC_FS */
diff -Nru a/net/appletalk/ddp.c b/net/appletalk/ddp.c
--- a/net/appletalk/ddp.c	Mon Oct  7 11:25:39 2002
+++ b/net/appletalk/ddp.c	Mon Oct  7 11:25:39 2002
@@ -40,6 +40,8 @@
  *                                              result.
  *		Arnaldo C. de Melo	:	Cleanup, in preparation for
  *						shared skb support 8)
+ *		Arnaldo C. de Melo	:	Move proc stuff to atalk_proc.c,
+ *						use seq_file
  *
  *		This program is free software; you can redistribute it and/or
  *		modify it under the terms of the GNU General Public License
@@ -50,41 +52,14 @@
 
 #include <linux/config.h>
 #include <linux/module.h>
-#include <asm/uaccess.h>
-#include <asm/system.h>
-#include <asm/bitops.h>
-#include <linux/types.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/string.h>
-#include <linux/mm.h>
-#include <linux/socket.h>
-#include <linux/sockios.h>
-#include <linux/in.h>
 #include <linux/tcp.h>
-#include <linux/errno.h>
-#include <linux/interrupt.h>
-#include <linux/if_ether.h>
-#include <linux/notifier.h>
-#include <linux/netdevice.h>
-#include <linux/inetdevice.h>
-#include <linux/route.h>
-#include <linux/inet.h>
-#include <linux/etherdevice.h>
 #include <linux/if_arp.h>
-#include <linux/skbuff.h>
-#include <linux/spinlock.h>
 #include <linux/termios.h>	/* For TIOCOUTQ/INQ */
 #include <net/datalink.h>
-#include <net/p8022.h>
 #include <net/psnap.h>
 #include <net/sock.h>
-#include <linux/ip.h>
 #include <net/route.h>
 #include <linux/atalk.h>
-#include <linux/proc_fs.h>
-#include <linux/stat.h>
-#include <linux/init.h>
 
 #ifdef CONFIG_PROC_FS
 extern void aarp_register_proc_fs(void);
@@ -112,8 +87,8 @@
 *                                                                          *
 \**************************************************************************/
 
-static struct sock *atalk_sockets;
-static spinlock_t atalk_sockets_lock = SPIN_LOCK_UNLOCKED;
+struct sock *atalk_sockets;
+spinlock_t atalk_sockets_lock = SPIN_LOCK_UNLOCKED;
 
 extern inline void atalk_insert_socket(struct sock *sk)
 {
@@ -244,53 +219,6 @@
 	}
 }
 
-/* Called from proc fs */
-static int atalk_get_info(char *buffer, char **start, off_t offset, int length)
-{
-	off_t pos = 0;
-	off_t begin = 0;
-	int len = sprintf(buffer, "Type local_addr  remote_addr tx_queue "
-				  "rx_queue st uid\n");
-	struct sock *s;
-	/* Output the AppleTalk data for the /proc filesystem */
-
-	spin_lock_bh(&atalk_sockets_lock);
-	for (s = atalk_sockets; s; s = s->next) {
-		struct atalk_sock *at = at_sk(s);
-
-		len += sprintf(buffer + len, "%02X   ", s->type);
-		len += sprintf(buffer + len, "%04X:%02X:%02X  ",
-			       ntohs(at->src_net), at->src_node, at->src_port);
-		len += sprintf(buffer + len, "%04X:%02X:%02X  ",
-			       ntohs(at->dest_net), at->dest_node,
-			       at->dest_port);
-		len += sprintf(buffer + len, "%08X:%08X ",
-			       atomic_read(&s->wmem_alloc),
-			       atomic_read(&s->rmem_alloc));
-		len += sprintf(buffer + len, "%02X %d\n", s->state, 
-			       SOCK_INODE(s->socket)->i_uid);
-
-		/* Are we still dumping unwanted data then discard the record */
-		pos = begin + len;
-
-		if (pos < offset) {
-			len = 0;	/* Keep dumping into the buffer start */
-			begin = pos;
-		}
-		if (pos > offset + length)	/* We have dumped enough */
-			break;
-	}
-	spin_unlock_bh(&atalk_sockets_lock);
-
-	/* The data in question runs from begin to begin + len */
-	*start = buffer + offset - begin;	/* Start of wanted data */
-	len -= offset - begin;   /* Remove unwanted header data from length */
-	if (len > length)
-		len = length;	   /* Remove unwanted tail data from length */
-
-	return len;
-}
-
 /**************************************************************************\
 *                                                                          *
 * Routing tables for the AppleTalk socket layer.                           *
@@ -298,14 +226,14 @@
 \**************************************************************************/
 
 /* Anti-deadlock ordering is router_lock --> iface_lock -DaveM */
-static struct atalk_route *atalk_router_list;
-static rwlock_t atalk_router_lock = RW_LOCK_UNLOCKED;
+struct atalk_route *atalk_routes;
+rwlock_t atalk_routes_lock = RW_LOCK_UNLOCKED;
 
-static struct atalk_iface *atalk_iface_list;
-static spinlock_t atalk_iface_lock = SPIN_LOCK_UNLOCKED;
+struct atalk_iface *atalk_interfaces;
+spinlock_t atalk_interfaces_lock = SPIN_LOCK_UNLOCKED;
 
 /* For probing devices or in a routerless network */
-static struct atalk_route atrtr_default;
+struct atalk_route atrtr_default;
 
 /* AppleTalk interface control */
 /*
@@ -314,10 +242,10 @@
  */
 static void atif_drop_device(struct net_device *dev)
 {
-	struct atalk_iface **iface = &atalk_iface_list;
+	struct atalk_iface **iface = &atalk_interfaces;
 	struct atalk_iface *tmp;
 
-	spin_lock_bh(&atalk_iface_lock);
+	spin_lock_bh(&atalk_interfaces_lock);
 	while ((tmp = *iface) != NULL) {
 		if (tmp->dev == dev) {
 			*iface = tmp->next;
@@ -327,7 +255,7 @@
 		} else
 			iface = &tmp->next;
 	}
-	spin_unlock_bh(&atalk_iface_lock);
+	spin_unlock_bh(&atalk_interfaces_lock);
 }
 
 static struct atalk_iface *atif_add_device(struct net_device *dev,
@@ -346,10 +274,10 @@
 	iface->address = *sa;
 	iface->status = 0;
 
-	spin_lock_bh(&atalk_iface_lock);
-	iface->next = atalk_iface_list;
-	atalk_iface_list = iface;
-	spin_unlock_bh(&atalk_iface_lock);
+	spin_lock_bh(&atalk_interfaces_lock);
+	iface->next = atalk_interfaces;
+	atalk_interfaces = iface;
+	spin_unlock_bh(&atalk_interfaces_lock);
 out:
 	return iface;
 out_mem:
@@ -466,8 +394,8 @@
 	 * Return a point-to-point interface only if
 	 * there is no non-ptp interface available.
 	 */
-	spin_lock_bh(&atalk_iface_lock);
-	for (iface = atalk_iface_list; iface; iface = iface->next) {
+	spin_lock_bh(&atalk_interfaces_lock);
+	for (iface = atalk_interfaces; iface; iface = iface->next) {
 		if (!fiface && !(iface->dev->flags & IFF_LOOPBACK))
 			fiface = iface;
 		if (!(iface->dev->flags & (IFF_LOOPBACK | IFF_POINTOPOINT))) {
@@ -478,12 +406,12 @@
 
 	if (fiface)
 		retval = &fiface->address;
-	else if (atalk_iface_list)
-		retval = &atalk_iface_list->address;
+	else if (atalk_interfaces)
+		retval = &atalk_interfaces->address;
 	else
 		retval = NULL;
 out:
-	spin_unlock_bh(&atalk_iface_lock);
+	spin_unlock_bh(&atalk_interfaces_lock);
 	return retval;
 }
 
@@ -514,8 +442,8 @@
 {
 	struct atalk_iface *iface;
 
-	spin_lock_bh(&atalk_iface_lock);
-	for (iface = atalk_iface_list; iface; iface = iface->next) {
+	spin_lock_bh(&atalk_interfaces_lock);
+	for (iface = atalk_interfaces; iface; iface = iface->next) {
 		if ((node == ATADDR_BCAST ||
 		     node == ATADDR_ANYNODE ||
 		     iface->address.s_node == node) &&
@@ -529,7 +457,7 @@
 		    ntohs(net) <= ntohs(iface->nets.nr_lastnet))
 		        break;
 	}
-	spin_unlock_bh(&atalk_iface_lock);
+	spin_unlock_bh(&atalk_interfaces_lock);
 	return iface;
 }
 
@@ -549,8 +477,8 @@
 	struct atalk_route *net_route = NULL;
 	struct atalk_route *r;
 	
-	read_lock_bh(&atalk_router_lock);
-	for (r = atalk_router_list; r; r = r->next) {
+	read_lock_bh(&atalk_routes_lock);
+	for (r = atalk_routes; r; r = r->next) {
 		if (!(r->flags & RTF_UP))
 			continue;
 
@@ -582,7 +510,7 @@
 	else /* No route can be found */
 		r = NULL;
 out:
-	read_unlock_bh(&atalk_router_lock);
+	read_unlock_bh(&atalk_routes_lock);
 	return r;
 }
 
@@ -630,8 +558,8 @@
 		goto out;
 
 	/* Now walk the routing table and make our decisions */
-	write_lock_bh(&atalk_router_lock);
-	for (rt = atalk_router_list; rt; rt = rt->next) {
+	write_lock_bh(&atalk_routes_lock);
+	for (rt = atalk_routes; rt; rt = rt->next) {
 		if (r->rt_flags != rt->flags)
 			continue;
 
@@ -646,8 +574,8 @@
 	if (!devhint) {
 		riface = NULL;
 
-		spin_lock_bh(&atalk_iface_lock);
-		for (iface = atalk_iface_list; iface; iface = iface->next) {
+		spin_lock_bh(&atalk_interfaces_lock);
+		for (iface = atalk_interfaces; iface; iface = iface->next) {
 			if (!riface &&
 			    ntohs(ga->sat_addr.s_net) >=
 			    		ntohs(iface->nets.nr_firstnet) &&
@@ -659,7 +587,7 @@
 			    ga->sat_addr.s_node == iface->address.s_node)
 				riface = iface;
 		}		
-		spin_unlock_bh(&atalk_iface_lock);
+		spin_unlock_bh(&atalk_interfaces_lock);
 
 		retval = -ENETUNREACH;
 		if (!riface)
@@ -675,8 +603,8 @@
 		if (!rt)
 			goto out;
 
-		rt->next = atalk_router_list;
-		atalk_router_list = rt;
+		rt->next = atalk_routes;
+		atalk_routes = rt;
 	}
 
 	/* Fill in the routing entry */
@@ -687,7 +615,7 @@
 
 	retval = 0;
 out_unlock:
-	write_unlock_bh(&atalk_router_lock);
+	write_unlock_bh(&atalk_routes_lock);
 out:
 	return retval;
 }
@@ -695,11 +623,11 @@
 /* Delete a route. Find it and discard it */
 static int atrtr_delete(struct atalk_addr * addr)
 {
-	struct atalk_route **r = &atalk_router_list;
+	struct atalk_route **r = &atalk_routes;
 	int retval = 0;
 	struct atalk_route *tmp;
 
-	write_lock_bh(&atalk_router_lock);
+	write_lock_bh(&atalk_routes_lock);
 	while ((tmp = *r) != NULL) {
 		if (tmp->target.s_net == addr->s_net &&
 		    (!(tmp->flags&RTF_GATEWAY) ||
@@ -712,7 +640,7 @@
 	}
 	retval = -ENOENT;
 out:
-	write_unlock_bh(&atalk_router_lock);
+	write_unlock_bh(&atalk_routes_lock);
 	return retval;
 }
 
@@ -722,10 +650,10 @@
  */
 void atrtr_device_down(struct net_device *dev)
 {
-	struct atalk_route **r = &atalk_router_list;
+	struct atalk_route **r = &atalk_routes;
 	struct atalk_route *tmp;
 
-	write_lock_bh(&atalk_router_lock);
+	write_lock_bh(&atalk_routes_lock);
 	while ((tmp = *r) != NULL) {
 		if (tmp->dev == dev) {
 			*r = tmp->next;
@@ -733,7 +661,7 @@
 		} else
 			r = &tmp->next;
 	}
-	write_unlock_bh(&atalk_router_lock);
+	write_unlock_bh(&atalk_routes_lock);
 
 	if (atrtr_default.dev == dev)
 		atrtr_set_default(NULL);
@@ -1013,81 +941,6 @@
 	return -EINVAL;
 }
 
-/* Called from proc fs - just make it print the ifaces neatly */
-static int atalk_if_get_info(char *buffer, char **start, off_t offset,
-			     int length)
-{
-	off_t pos = 0;
-	off_t begin = 0;
-	struct atalk_iface *iface;
-	int len = sprintf(buffer, "Interface	  Address   "
-				  "Networks   Status\n");
-
-	spin_lock_bh(&atalk_iface_lock);
-	for (iface = atalk_iface_list; iface; iface = iface->next) {
-		len += sprintf(buffer + len, "%-16s %04X:%02X  %04X-%04X  %d\n",
-			       iface->dev->name, ntohs(iface->address.s_net),
-			       iface->address.s_node,
-			       ntohs(iface->nets.nr_firstnet),
-			       ntohs(iface->nets.nr_lastnet), iface->status);
-		pos = begin + len;
-		if (pos < offset) {
-			len   = 0;
-			begin = pos;
-		}
-		if (pos > offset + length)
-			break;
-	}
-	spin_unlock_bh(&atalk_iface_lock);
-
-	*start = buffer + (offset - begin);
-	len -= (offset - begin);
-	if (len > length)
-		len = length;
-	return len;
-}
-
-/* Called from proc fs - just make it print the routes neatly */
-static int atalk_rt_get_info(char *buffer, char **start, off_t offset,
-			     int length)
-{
-	off_t pos = 0;
-	off_t begin = 0;
-	int len = sprintf(buffer, "Target        Router  Flags Dev\n");
-	struct atalk_route *rt;
-
-	if (atrtr_default.dev) {
-		rt = &atrtr_default;
-		len += sprintf(buffer + len,
-			       "Default     %04X:%02X  %-4d  %s\n",
-			       ntohs(rt->gateway.s_net), rt->gateway.s_node,
-			       rt->flags, rt->dev->name);
-	}
-
-	read_lock_bh(&atalk_router_lock);
-	for (rt = atalk_router_list; rt; rt = rt->next) {
-		len += sprintf(buffer + len,
-				"%04X:%02X     %04X:%02X  %-4d  %s\n",
-			       ntohs(rt->target.s_net), rt->target.s_node,
-			       ntohs(rt->gateway.s_net), rt->gateway.s_node,
-			       rt->flags, rt->dev->name);
-		pos = begin + len;
-		if (pos < offset) {
-			len = 0;
-			begin = pos;
-		}
-		if (pos > offset + length)
-			break;
-	}
-	read_unlock_bh(&atalk_router_lock);
-
-	*start = buffer + (offset - begin);
-	len -= (offset - begin);
-	if (len > length)
-		len = length;
-	return len;
-}
-
 /**************************************************************************\
 *                                                                          *
 * Handling for system calls applied via the various interfaces to an       *
@@ -1991,17 +1844,14 @@
 
 	register_netdevice_notifier(&ddp_notifier);
 	aarp_proto_init();
-
-	proc_net_create("appletalk", 0, atalk_get_info);
-	proc_net_create("atalk_route", 0, atalk_rt_get_info);
-	proc_net_create("atalk_iface", 0, atalk_if_get_info);
+	atalk_proc_init();
 #ifdef CONFIG_PROC_FS
 	aarp_register_proc_fs();
 #endif /* CONFIG_PROC_FS */
 #ifdef CONFIG_SYSCTL
 	atalk_register_sysctl();
 #endif /* CONFIG_SYSCTL */
-	printk(KERN_INFO "NET4: AppleTalk 0.18a for Linux NET4.0\n");
+	printk(KERN_INFO "NET4: AppleTalk 0.20 for Linux NET4.0\n");
 	return 0;
 }
 module_init(atalk_init);
@@ -2024,9 +1874,7 @@
 #ifdef CONFIG_SYSCTL
 	atalk_unregister_sysctl();
 #endif /* CONFIG_SYSCTL */
-	proc_net_remove("appletalk");
-	proc_net_remove("atalk_route");
-	proc_net_remove("atalk_iface");
+	atalk_proc_exit();
 #ifdef CONFIG_PROC_FS
 	aarp_unregister_proc_fs();
 #endif /* CONFIG_PROC_FS */
@@ -2039,3 +1887,7 @@
 }
 module_exit(atalk_exit);
 #endif  /* MODULE */
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Alan Cox <Alan.Cox@linux.org>");
+MODULE_DESCRIPTION("AppleTalk 0.20 for Linux NET4.0\n");
diff -Nru a/net/appletalk/sysctl_net_atalk.c b/net/appletalk/sysctl_net_atalk.c
--- a/net/appletalk/sysctl_net_atalk.c	Mon Oct  7 11:25:26 2002
+++ b/net/appletalk/sysctl_net_atalk.c	Mon Oct  7 11:25:26 2002
@@ -7,7 +7,6 @@
  */
 
 #include <linux/config.h>
-#include <linux/mm.h>
 #include <linux/sysctl.h>
 
 extern int sysctl_aarp_expiry_time;
@@ -16,7 +15,7 @@
 extern int sysctl_aarp_resolve_time;
 
 #ifdef CONFIG_SYSCTL
-static ctl_table atalk_table[] = {
+static struct ctl_table atalk_table[] = {
 	{
 		.ctl_name	= NET_ATALK_AARP_EXPIRY_TIME,
 		.procname	= "aarp-expiry-time",
@@ -52,7 +51,7 @@
 	{ 0 },
 };
 
-static ctl_table atalk_dir_table[] = {
+static struct ctl_table atalk_dir_table[] = {
 	{
 		.ctl_name	= NET_ATALK,
 		.procname	= "appletalk",
@@ -62,7 +61,7 @@
 	{ 0 },
 };
 
-static ctl_table atalk_root_table[] = {
+static struct ctl_table atalk_root_table[] = {
 	{
 		.ctl_name	= CTL_NET,
 		.procname	= "net",
diff -Nru a/net/bluetooth/Config.help b/net/bluetooth/Config.help
--- a/net/bluetooth/Config.help	Mon Oct  7 11:25:35 2002
+++ b/net/bluetooth/Config.help	Mon Oct  7 11:25:35 2002
@@ -11,6 +11,8 @@
                HCI Device drivers (interface to the hardware)
                L2CAP Module (L2CAP protocol)
                SCO Module (SCO links)
+               RFCOMM Module (RFCOMM protocol)  
+               BNEP Module (BNEP protocol)
 
   Say Y here to enable Linux Bluetooth support and to build BlueZ Core
   layer.
diff -Nru a/net/bluetooth/Config.in b/net/bluetooth/Config.in
--- a/net/bluetooth/Config.in	Mon Oct  7 11:25:36 2002
+++ b/net/bluetooth/Config.in	Mon Oct  7 11:25:36 2002
@@ -1,8 +1,9 @@
 #
-# Bluetooth configuration
+# Bluetooth subsystem configuration
 #
 
 if [ "$CONFIG_NET" != "n" ]; then
+
    mainmenu_option next_comment
    comment 'Bluetooth support'
    dep_tristate 'Bluetooth subsystem support' CONFIG_BLUEZ $CONFIG_NET
@@ -10,8 +11,11 @@
    if [ "$CONFIG_BLUEZ" != "n" ]; then
       dep_tristate 'L2CAP protocol support' CONFIG_BLUEZ_L2CAP $CONFIG_BLUEZ
       dep_tristate 'SCO links support' CONFIG_BLUEZ_SCO $CONFIG_BLUEZ
+      source net/bluetooth/rfcomm/Config.in
+      source net/bluetooth/bnep/Config.in
       source drivers/bluetooth/Config.in
    fi
+
    endmenu
 fi
 
diff -Nru a/net/bluetooth/Makefile b/net/bluetooth/Makefile
--- a/net/bluetooth/Makefile	Mon Oct  7 11:25:26 2002
+++ b/net/bluetooth/Makefile	Mon Oct  7 11:25:26 2002
@@ -1,13 +1,15 @@
 #
-# Makefile for the Bluetooth subsystem
+# Makefile for the Linux Bluetooth subsystem.
 #
 
-export-objs	:= syms.o
+export-objs := syms.o
 
-obj-$(CONFIG_BLUEZ) += bluez.o
-obj-$(CONFIG_BLUEZ_L2CAP) += l2cap.o
-obj-$(CONFIG_BLUEZ_SCO) += sco.o
+obj-$(CONFIG_BLUEZ)		+= bluez.o
+obj-$(CONFIG_BLUEZ_L2CAP)	+= l2cap.o
+obj-$(CONFIG_BLUEZ_SCO)		+= sco.o
+obj-$(CONFIG_BLUEZ_RFCOMM)	+= rfcomm/
+obj-$(CONFIG_BLUEZ_BNEP)	+= bnep/
 
-bluez-objs	:= af_bluetooth.o hci_core.o hci_conn.o hci_event.o hci_sock.o lib.o syms.o
+bluez-objs := af_bluetooth.o hci_core.o hci_conn.o hci_event.o hci_sock.o lib.o syms.o
 
 include $(TOPDIR)/Rules.make
diff -Nru a/net/bluetooth/af_bluetooth.c b/net/bluetooth/af_bluetooth.c
--- a/net/bluetooth/af_bluetooth.c	Mon Oct  7 11:25:26 2002
+++ b/net/bluetooth/af_bluetooth.c	Mon Oct  7 11:25:26 2002
@@ -27,7 +27,7 @@
  *
  * $Id: af_bluetooth.c,v 1.3 2002/04/17 17:37:15 maxk Exp $
  */
-#define VERSION "2.0"
+#define VERSION "2.2"
 
 #include <linux/config.h>
 #include <linux/module.h>
@@ -57,7 +57,7 @@
 #endif
 
 /* Bluetooth sockets */
-#define BLUEZ_MAX_PROTO	4
+#define BLUEZ_MAX_PROTO	5
 static struct net_proto_family *bluez_proto[BLUEZ_MAX_PROTO];
 
 static kmem_cache_t *bluez_sock_cache;
@@ -136,18 +136,18 @@
 
 void bluez_sock_link(struct bluez_sock_list *l, struct sock *sk)
 {
-	write_lock(&l->lock);
+	write_lock_bh(&l->lock);
 	sk->next = l->head;
 	l->head = sk;
 	sock_hold(sk);
-	write_unlock(&l->lock);
+	write_unlock_bh(&l->lock);
 }
 
 void bluez_sock_unlink(struct bluez_sock_list *l, struct sock *sk)
 {
 	struct sock **skp;
 
-	write_lock(&l->lock);
+	write_lock_bh(&l->lock);
 	for (skp = &l->head; *skp; skp = &((*skp)->next)) {
 		if (*skp == sk) {
 			*skp = sk->next;
@@ -155,7 +155,7 @@
 			break;
 		}
 	}
-	write_unlock(&l->lock);
+	write_unlock_bh(&l->lock);
 }
 
 void bluez_accept_enqueue(struct sock *parent, struct sock *sk)
@@ -265,6 +265,9 @@
 	if (sk->state == BT_CLOSED)
 		mask |= POLLHUP;
 
+	if (sk->state == BT_CONNECT || sk->state == BT_CONNECT2)
+		return mask;
+	
 	if (sock_writeable(sk))
 		mask |= POLLOUT | POLLWRNORM | POLLWRBAND;
 	else
diff -Nru a/net/bluetooth/bnep/Config.help b/net/bluetooth/bnep/Config.help
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/net/bluetooth/bnep/Config.help	Mon Oct  7 11:26:06 2002
@@ -0,0 +1,21 @@
+BNEP protocol support
+CONFIG_BLUEZ_BNEP
+  BNEP (Bluetooth Network Encapsulation Protocol) is Ethernet
+  emulation layer on top of Bluetooth. BNEP is required for Bluetooth
+  PAN (Personal Area Network).
+
+  To use BNEP, you will need user-space utilities provided in the 
+  BlueZ-PAN package.
+  For more information, see <http://bluez.sourceforge.net>.
+
+  Say Y here to compile BNEP support into the kernel or say M to
+  compile it as module (bnep.o).
+
+BNEP multicast filter support
+CONFIG_BLUEZ_BNEP_MC_FILTER
+  This option enables the multicast filter support for BNEP.
+
+BNEP protocol filter support
+CONFIG_BLUEZ_BNEP_PROTO_FILTER
+  This option enables the protocol filter support for BNEP.
+
diff -Nru a/net/bluetooth/bnep/Config.in b/net/bluetooth/bnep/Config.in
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/net/bluetooth/bnep/Config.in	Mon Oct  7 11:26:06 2002
@@ -0,0 +1,8 @@
+
+dep_tristate 'BNEP protocol support' CONFIG_BLUEZ_BNEP $CONFIG_BLUEZ_L2CAP
+
+if [ "$CONFIG_BLUEZ_BNEP" != "n" ]; then
+   bool '  Multicast filter support' CONFIG_BLUEZ_BNEP_MC_FILTER
+   bool '  Protocol filter support'  CONFIG_BLUEZ_BNEP_PROTO_FILTER
+fi
+
diff -Nru a/net/bluetooth/bnep/Makefile b/net/bluetooth/bnep/Makefile
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/net/bluetooth/bnep/Makefile	Mon Oct  7 11:26:06 2002
@@ -0,0 +1,9 @@
+#
+# Makefile for the Linux Bluetooth BNEP layer.
+#
+
+obj-$(CONFIG_BLUEZ_BNEP) += bnep.o
+
+bnep-objs := core.o sock.o netdev.o crc32.o
+
+include $(TOPDIR)/Rules.make
diff -Nru a/net/bluetooth/bnep/bnep.h b/net/bluetooth/bnep/bnep.h
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/net/bluetooth/bnep/bnep.h	Mon Oct  7 11:26:06 2002
@@ -0,0 +1,185 @@
+/*
+  BNEP protocol definition for Linux Bluetooth stack (BlueZ).
+  Copyright (C) 2002 Maxim Krasnyansky <maxk@qualcomm.com>
+	
+  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
+*/
+
+/*
+ * $Id: bnep.h,v 1.5 2002/08/04 21:23:58 maxk Exp $
+ */
+
+#ifndef _BNEP_H
+#define _BNEP_H
+
+#include <linux/types.h>
+#include <net/bluetooth/bluetooth.h>
+
+#include "crc32.h"
+
+// Limits
+#define BNEP_MAX_PROTO_FILTERS     5
+#define BNEP_MAX_MULTICAST_FILTERS 20
+
+// UUIDs
+#define BNEP_BASE_UUID 0x0000000000001000800000805F9B34FB
+#define BNEP_UUID16    0x02
+#define BNEP_UUID32    0x04
+#define BNEP_UUID128   0x16
+
+#define BNEP_SVC_PANU  0x1115
+#define BNEP_SVC_NAP   0x1116
+#define BNEP_SVC_GN    0x1117
+
+// Packet types
+#define BNEP_GENERAL               0x00
+#define BNEP_CONTROL               0x01
+#define BNEP_COMPRESSED            0x02
+#define BNEP_COMPRESSED_SRC_ONLY   0x03
+#define BNEP_COMPRESSED_DST_ONLY   0x04
+
+// Control types
+#define BNEP_CMD_NOT_UNDERSTOOD    0x00
+#define BNEP_SETUP_CONN_REQ        0x01
+#define BNEP_SETUP_CONN_RSP        0x02
+#define BNEP_FILTER_NET_TYPE_SET   0x03
+#define BNEP_FILTER_NET_TYPE_RSP   0x04
+#define BNEP_FILTER_MULTI_ADDR_SET 0x05
+#define BNEP_FILTER_MULTI_ADDR_RSP 0x06
+
+// Extension types
+#define BNEP_EXT_CONTROL           0x00
+
+// Response messages 
+#define BNEP_SUCCESS               0x00
+
+#define BNEP_CONN_INVALID_DST      0x01
+#define BNEP_CONN_INVALID_SRC      0x02
+#define BNEP_CONN_INVALID_SVC      0x03
+#define BNEP_CONN_NOT_ALLOWED      0x04
+
+#define BNEP_FILTER_UNSUPPORTED_REQ    0x01
+#define BNEP_FILTER_INVALID_RANGE      0x02
+#define BNEP_FILTER_INVALID_MCADDR     0x02
+#define BNEP_FILTER_LIMIT_REACHED      0x03
+#define BNEP_FILTER_DENIED_SECURITY    0x04
+
+// L2CAP settings
+#define BNEP_MTU         1691
+#define BNEP_PSM	 0x0f
+#define BNEP_FLUSH_TO    0xffff
+#define BNEP_CONNECT_TO  15
+#define BNEP_FILTER_TO   15
+
+// Headers 
+#define BNEP_TYPE_MASK	 0x7f
+#define BNEP_EXT_HEADER	 0x80
+
+struct bnep_setup_conn_req {
+	__u8  type;
+	__u8  ctrl;
+	__u8  uuid_size;
+	__u8  service[0];
+} __attribute__((packed));
+
+struct bnep_set_filter_req {
+	__u8  type;
+	__u8  ctrl;
+	__u16 len;
+	__u8  list[0];
+} __attribute__((packed));
+
+struct bnep_control_rsp {
+	__u8  type;
+	__u8  ctrl;
+	__u16 resp;
+} __attribute__((packed));
+
+struct bnep_ext_hdr {
+	__u8  type;
+	__u8  len;
+	__u8  data[0];
+} __attribute__((packed));
+
+// Ioctl interface
+#define BNEPCONADD      1
+#define BNEPCONDEL      2
+#define BNEPGETCONLIST  3
+#define BNEPGETCONINFO  4
+
+struct bnep_conadd_req {
+	int   sock;       // Connected socket
+	__u32 flags;
+	__u16 role;
+	char  device[16]; // Name of the Ethernet device
+};
+
+struct bnep_condel_req {
+	__u32 flags;
+	__u8  dst[ETH_ALEN];
+};
+
+struct bnep_coninfo {
+	__u32 flags;
+	__u16 role;
+	__u16 state;	
+	__u8  dst[ETH_ALEN];
+	char  device[16];
+};
+
+struct bnep_conlist_req {
+	__u32  cnum;
+	struct bnep_coninfo *ci;
+};
+
+struct bnep_proto_filter {
+	__u16 start;
+	__u16 end;
+};
+
+int bnep_add_connection(struct bnep_conadd_req *req, struct socket *sock);
+int bnep_del_connection(struct bnep_condel_req *req);
+int bnep_get_conlist(struct bnep_conlist_req *req);
+int bnep_get_coninfo(struct bnep_coninfo *ci);
+
+// BNEP sessions
+struct bnep_session {
+	struct list_head list;
+	
+	unsigned int  role;
+        unsigned long state;
+        unsigned long flags;
+	atomic_t      killed;
+
+	struct ethhdr eh;
+	struct msghdr msg;
+
+	struct bnep_proto_filter proto_filter[BNEP_MAX_PROTO_FILTERS];
+	u64    mc_filter;
+	
+	struct socket    *sock;
+	struct net_device dev;
+	struct net_device_stats stats;
+};
+
+int bnep_net_init(struct net_device *dev);
+int bnep_sock_init(void);
+int bnep_sock_cleanup(void);
+
+static inline int bnep_mc_hash(__u8 *addr)
+{
+        return (bnep_crc32(~0, addr, ETH_ALEN) >> 26);
+}
+
+#endif
diff -Nru a/net/bluetooth/bnep/core.c b/net/bluetooth/bnep/core.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/net/bluetooth/bnep/core.c	Mon Oct  7 11:26:06 2002
@@ -0,0 +1,705 @@
+/* 
+   BNEP implementation for Linux Bluetooth stack (BlueZ).
+   Copyright (C) 2001-2002 Inventel Systemes
+   Written 2001-2002 by
+	Clément Moreau <clement.moreau@inventel.fr>
+	David Libault  <david.libault@inventel.fr>
+
+   Copyright (C) 2002 Maxim Krasnyanskiy <maxk@qualcomm.com>
+
+   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;
+
+   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+   IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
+   CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES 
+   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 
+   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 
+   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+   ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, 
+   COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS 
+   SOFTWARE IS DISCLAIMED.
+*/
+
+/*
+ * $Id: core.c,v 1.20 2002/08/04 21:23:58 maxk Exp $
+ */ 
+
+#define __KERNEL_SYSCALLS__
+
+#include <linux/config.h>
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/signal.h>
+#include <linux/init.h>
+#include <linux/wait.h>
+#include <linux/errno.h>
+#include <linux/smp_lock.h>
+#include <linux/net.h>
+#include <net/sock.h>
+
+#include <linux/socket.h>
+#include <linux/file.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+#include <asm/unaligned.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/l2cap.h>
+
+#include "bnep.h"
+
+#ifndef CONFIG_BLUEZ_BNEP_DEBUG
+#undef  BT_DBG
+#define BT_DBG(D...)
+#endif
+
+#define VERSION "1.0"
+
+static LIST_HEAD(bnep_session_list);
+static DECLARE_RWSEM(bnep_session_sem);
+
+static struct bnep_session *__bnep_get_session(u8 *dst)
+{
+	struct bnep_session *s;
+	struct list_head *p;
+
+	BT_DBG("");
+
+	list_for_each(p, &bnep_session_list) {
+		s = list_entry(p, struct bnep_session, list);	
+		if (!memcmp(dst, s->eh.h_source, ETH_ALEN))
+			return s;
+	}
+	return NULL;
+}
+
+static void __bnep_link_session(struct bnep_session *s)
+{
+	MOD_INC_USE_COUNT;
+	list_add(&s->list, &bnep_session_list);	
+}
+
+static void __bnep_unlink_session(struct bnep_session *s)
+{
+	list_del(&s->list);
+	MOD_DEC_USE_COUNT;
+}
+
+static int bnep_send(struct bnep_session *s, void *data, size_t len)
+{
+	struct socket *sock = s->sock;
+	struct iovec iv = { data, len };
+	s->msg.msg_iov    = &iv;
+	s->msg.msg_iovlen = 1;
+	return sock->ops->sendmsg(sock, &s->msg, len, NULL);
+}
+
+static int bnep_send_rsp(struct bnep_session *s, u8 ctrl, u16 resp)
+{
+	struct bnep_control_rsp rsp;
+	rsp.type = BNEP_CONTROL;
+	rsp.ctrl = ctrl;
+	rsp.resp = htons(resp);
+	return bnep_send(s, &rsp, sizeof(rsp));
+}
+
+static int bnep_ctrl_set_netfilter(struct bnep_session *s, struct sk_buff *skb)
+{
+	u16 *data;
+	int n;
+	
+	data = (void *) skb->data;
+	if (!skb_pull(skb, 2))
+		return -EILSEQ;
+	n = ntohs(get_unaligned(data));
+
+	data = (void *) skb->data;
+	if (!skb_pull(skb, n))
+		return -EILSEQ;
+
+	BT_DBG("filter len %d", n);
+
+#ifdef CONFIG_BLUEZ_BNEP_PROTO_FILTER
+	n /= 4;
+	if (n <= BNEP_MAX_PROTO_FILTERS) {
+		struct bnep_proto_filter *f = s->proto_filter;
+		int i;
+
+		for (i = 0; i < n; i++) {
+			f[i].start = get_unaligned(data++);
+			f[i].end   = get_unaligned(data++);
+
+			BT_DBG("proto filter start %d end %d",
+				f[i].start, f[i].end);
+		}
+		if (i < BNEP_MAX_PROTO_FILTERS)
+			memset(f + i, 0, sizeof(*f));
+
+		bnep_send_rsp(s, BNEP_FILTER_NET_TYPE_RSP, BNEP_SUCCESS);
+	} else {
+		bnep_send_rsp(s, BNEP_FILTER_NET_TYPE_RSP, BNEP_FILTER_LIMIT_REACHED);
+	}
+#else
+	bnep_send_rsp(s, BNEP_FILTER_NET_TYPE_RSP, BNEP_FILTER_UNSUPPORTED_REQ);
+#endif
+	return 0;
+}
+
+static int bnep_ctrl_set_mcfilter(struct bnep_session *s, struct sk_buff *skb)
+{
+	u8 *data;
+	int n;
+	
+	data = (void *) skb->data;
+	if (!skb_pull(skb, 2))
+		return -EILSEQ;
+	n = ntohs(get_unaligned((u16 *) data));
+
+	data = (void *) skb->data;
+	if (!skb_pull(skb, n))
+		return -EILSEQ;
+
+	BT_DBG("filter len %d", n);
+
+#ifdef CONFIG_BLUEZ_BNEP_MC_FILTER
+	n /= (ETH_ALEN * 2);
+
+	if (n > 0) {
+		s->mc_filter = 0;
+
+		/* Always send broadcast */
+		set_bit(bnep_mc_hash(s->dev.broadcast), (ulong *) &s->mc_filter);
+
+		/* Add address ranges to the multicast hash */
+		for (; n > 0; n--) {
+			u8 a1[6], *a2;
+
+			memcpy(a1, data, ETH_ALEN); data += ETH_ALEN;
+			a2 = data; data += ETH_ALEN;
+	
+			BT_DBG("mc filter %s -> %s",
+				batostr((void *) a1), batostr((void *) a2));
+
+			#define INCA(a) { int i = 5; while (i >=0 && ++a[i--] == 0); }
+
+			/* Iterate from a1 to a2 */
+			set_bit(bnep_mc_hash(a1), (ulong *) &s->mc_filter);
+			while (memcmp(a1, a2, 6) < 0 && s->mc_filter != ~0LL) {
+				INCA(a1);
+				set_bit(bnep_mc_hash(a1), (ulong *) &s->mc_filter);
+			}
+		}
+	}
+
+	BT_DBG("mc filter hash 0x%llx", s->mc_filter);
+
+	bnep_send_rsp(s, BNEP_FILTER_MULTI_ADDR_RSP, BNEP_SUCCESS);
+#else
+	bnep_send_rsp(s, BNEP_FILTER_MULTI_ADDR_RSP, BNEP_FILTER_UNSUPPORTED_REQ);
+#endif
+	return 0;
+}
+
+static int bnep_rx_control(struct bnep_session *s, struct sk_buff *skb)
+{
+	int err = 0;
+	u8 cmd = *(u8 *) skb->data;
+	skb_pull(skb, 1);
+	
+	switch (cmd) {
+	case BNEP_CMD_NOT_UNDERSTOOD:
+	case BNEP_SETUP_CONN_REQ:
+	case BNEP_SETUP_CONN_RSP:
+	case BNEP_FILTER_NET_TYPE_RSP:
+	case BNEP_FILTER_MULTI_ADDR_RSP:
+		/* Ignore these for now */
+		break;
+		
+	case BNEP_FILTER_NET_TYPE_SET:
+		err = bnep_ctrl_set_netfilter(s, skb);
+		break;
+
+	case BNEP_FILTER_MULTI_ADDR_SET:
+		err = bnep_ctrl_set_mcfilter(s, skb);
+		break;
+
+	default: {
+			u8 pkt[3];
+			pkt[0] = BNEP_CONTROL;
+			pkt[1] = BNEP_CMD_NOT_UNDERSTOOD;
+			pkt[2] = cmd;
+			bnep_send(s, pkt, sizeof(pkt));
+		}
+		break;
+	}
+
+	return err;
+}
+
+static int bnep_rx_extension(struct bnep_session *s, struct sk_buff *skb)
+{
+	struct bnep_ext_hdr *h;
+	int err = 0;
+
+	do {
+		h = (void *) skb->data;
+		if (!skb_pull(skb, sizeof(*h))) {
+			err = -EILSEQ;
+			break;
+		}
+
+		BT_DBG("type 0x%x len %d", h->type, h->len);
+		
+		switch (h->type & BNEP_TYPE_MASK) {
+		case BNEP_EXT_CONTROL:
+			err = bnep_rx_control(s, skb);
+			break;
+
+		default:
+			/* Unknown extension */
+			if (!skb_pull(skb, h->len))
+				err = -EILSEQ;
+			break;
+		}
+	} while (!err && (h->type & BNEP_EXT_HEADER));
+	
+	return err;
+}
+
+static u8 __bnep_rx_hlen[] = {
+	ETH_HLEN,     /* BNEP_GENERAL */
+	0,            /* BNEP_CONTROL */
+	2,            /* BNEP_COMPRESSED */
+	ETH_ALEN + 2, /* BNEP_COMPRESSED_SRC_ONLY */
+	ETH_ALEN + 2  /* BNEP_COMPRESSED_DST_ONLY */
+};
+#define BNEP_RX_TYPES	(sizeof(__bnep_rx_hlen) - 1)
+
+static inline int bnep_rx_frame(struct bnep_session *s, struct sk_buff *skb)
+{
+	struct net_device *dev = &s->dev;
+	struct sk_buff *nskb;
+	u8 type;
+
+	dev->last_rx = jiffies;
+	s->stats.rx_bytes += skb->len;
+
+	type = *(u8 *) skb->data; skb_pull(skb, 1);
+
+	if ((type & BNEP_TYPE_MASK) > BNEP_RX_TYPES)
+		goto badframe;
+	
+	if ((type & BNEP_TYPE_MASK) == BNEP_CONTROL) {
+		bnep_rx_control(s, skb);
+		kfree_skb(skb);
+		return 0;
+	}
+
+	skb->mac.raw = skb->data;
+
+	/* Verify and pull out header */
+	if (!skb_pull(skb, __bnep_rx_hlen[type & BNEP_TYPE_MASK]))
+		goto badframe;
+
+	s->eh.h_proto = get_unaligned((u16 *) (skb->data - 2));
+
+	if (type & BNEP_EXT_HEADER) {
+		if (bnep_rx_extension(s, skb) < 0)
+			goto badframe;
+	}
+
+	/* Strip 802.1p header */
+	if (ntohs(s->eh.h_proto) == 0x8100) {
+		if (!skb_pull(skb, 4))
+			goto badframe;
+		s->eh.h_proto = get_unaligned((u16 *) (skb->data - 2));
+	}
+	
+	/* We have to alloc new skb and copy data here :(. Because original skb
+	 * may not be modified and because of the alignment requirements. */
+	nskb = alloc_skb(2 + ETH_HLEN + skb->len, GFP_KERNEL);
+	if (!nskb) {
+		s->stats.rx_dropped++;
+		kfree_skb(skb);
+		return -ENOMEM;
+	}
+	skb_reserve(nskb, 2);
+
+	/* Decompress header and construct ether frame */
+	switch (type & BNEP_TYPE_MASK) {
+	case BNEP_COMPRESSED:
+		memcpy(__skb_put(nskb, ETH_HLEN), &s->eh, ETH_HLEN);
+		break;
+	
+	case BNEP_COMPRESSED_SRC_ONLY:
+		memcpy(__skb_put(nskb, ETH_ALEN), s->eh.h_dest, ETH_ALEN);
+		memcpy(__skb_put(nskb, ETH_ALEN), skb->mac.raw, ETH_ALEN);
+		put_unaligned(s->eh.h_proto, (u16 *) __skb_put(nskb, 2));
+		break;
+
+	case BNEP_COMPRESSED_DST_ONLY:
+		memcpy(__skb_put(nskb, ETH_ALEN), skb->mac.raw, ETH_ALEN);
+		memcpy(__skb_put(nskb, ETH_ALEN + 2), s->eh.h_source, ETH_ALEN + 2);
+		break;
+
+	case BNEP_GENERAL:
+		memcpy(__skb_put(nskb, ETH_ALEN * 2), skb->mac.raw, ETH_ALEN * 2);
+		put_unaligned(s->eh.h_proto, (u16 *) __skb_put(nskb, 2));
+		break;
+	}
+
+	memcpy(__skb_put(nskb, skb->len), skb->data, skb->len);
+	kfree_skb(skb);
+	
+	s->stats.rx_packets++;
+	nskb->dev       = dev;
+	nskb->ip_summed = CHECKSUM_UNNECESSARY;
+	nskb->protocol  = eth_type_trans(nskb, dev);
+	netif_rx_ni(nskb);
+	return 0;
+
+badframe:
+	s->stats.rx_errors++;
+	kfree_skb(skb);
+	return 0;
+}
+
+static u8 __bnep_tx_types[] = {
+	BNEP_GENERAL,
+	BNEP_COMPRESSED_SRC_ONLY,
+	BNEP_COMPRESSED_DST_ONLY,
+	BNEP_COMPRESSED
+};
+
+static inline int bnep_tx_frame(struct bnep_session *s, struct sk_buff *skb)
+{
+	struct ethhdr *eh = (void *) skb->data;
+	struct socket *sock = s->sock;
+	struct iovec iv[3];
+	int len = 0, il = 0;
+	u8 type = 0;
+
+	BT_DBG("skb %p dev %p type %d", skb, skb->dev, skb->pkt_type);
+
+	if (!skb->dev) {
+		/* Control frame sent by us */
+		goto send;
+	}
+
+	iv[il++] = (struct iovec) { &type, 1 };
+	len++;
+
+	if (!memcmp(eh->h_dest, s->eh.h_source, ETH_ALEN))
+		type |= 0x01;
+
+	if (!memcmp(eh->h_source, s->eh.h_dest, ETH_ALEN))
+		type |= 0x02;
+
+	if (type)
+		skb_pull(skb, ETH_ALEN * 2);
+
+	type = __bnep_tx_types[type];
+	switch (type) {
+	case BNEP_COMPRESSED_SRC_ONLY:
+		iv[il++] = (struct iovec) { eh->h_source, ETH_ALEN };
+		len += ETH_ALEN;
+		break;
+		
+	case BNEP_COMPRESSED_DST_ONLY:
+		iv[il++] = (struct iovec) { eh->h_dest, ETH_ALEN };
+		len += ETH_ALEN;
+		break;
+	}
+
+send:
+	iv[il++] = (struct iovec) { skb->data, skb->len };
+	len += skb->len;
+	
+	/* FIXME: linearize skb */
+	
+	s->msg.msg_iov    = iv;
+	s->msg.msg_iovlen = il;
+	len = sock->ops->sendmsg(sock, &s->msg, len, NULL);
+	kfree_skb(skb);
+
+	if (len > 0) {
+		s->stats.tx_bytes += len;
+		s->stats.tx_packets++;
+		return 0;
+	}
+
+	return len;
+}
+
+static int bnep_session(void *arg)
+{
+	struct bnep_session *s = arg;
+	struct net_device *dev = &s->dev;
+	struct sock *sk = s->sock->sk;
+	struct sk_buff *skb;
+	wait_queue_t wait;
+
+	BT_DBG("");
+
+        daemonize();
+	set_user_nice(current, 19);
+	current->flags |= PF_IOTHREAD;
+        sigfillset(&current->blocked);
+	flush_signals(current);
+
+        sprintf(current->comm, "kbnepd %s", dev->name);
+
+        set_fs(KERNEL_DS);
+
+	init_waitqueue_entry(&wait, current);
+	add_wait_queue(sk->sleep, &wait);
+	while (!atomic_read(&s->killed)) {
+		set_current_state(TASK_INTERRUPTIBLE);
+
+		// RX
+		while ((skb = skb_dequeue(&sk->receive_queue))) {
+			skb_orphan(skb);
+			bnep_rx_frame(s, skb);
+		}
+
+		if (sk->state != BT_CONNECTED)
+			break;
+	
+		// TX
+		while ((skb = skb_dequeue(&sk->write_queue)))
+			if (bnep_tx_frame(s, skb))
+				break;
+		netif_wake_queue(dev);
+	
+		schedule();
+	}
+	set_current_state(TASK_RUNNING);
+	remove_wait_queue(sk->sleep, &wait);
+
+	/* Cleanup session */
+	down_write(&bnep_session_sem);
+
+	/* Delete network device */
+	unregister_netdev(dev);
+
+	/* Release the socket */
+	fput(s->sock->file);
+
+	__bnep_unlink_session(s);
+
+	up_write(&bnep_session_sem);
+	kfree(s);
+	return 0;
+}
+
+int bnep_add_connection(struct bnep_conadd_req *req, struct socket *sock)
+{
+	struct net_device *dev;
+	struct bnep_session *s, *ss;
+	u8 dst[ETH_ALEN], src[ETH_ALEN];
+	int err;
+
+	BT_DBG("");
+
+	baswap((void *) dst, &bluez_sk(sock->sk)->dst);
+	baswap((void *) src, &bluez_sk(sock->sk)->src);
+
+	s = kmalloc(sizeof(struct bnep_session), GFP_KERNEL);
+	if (!s) 
+		return -ENOMEM;
+	memset(s, 0, sizeof(struct bnep_session));
+
+	down_write(&bnep_session_sem);
+
+	ss = __bnep_get_session(dst);
+	if (ss && ss->state == BT_CONNECTED) {
+		err = -EEXIST;
+		goto failed;
+	}
+
+	dev = &s->dev;
+	
+	if (*req->device)
+		strcpy(dev->name, req->device);
+	else
+		strcpy(dev->name, "bnep%d");
+
+	/* This is rx header therefor addresses are swaped.
+	 * ie eh.h_dest is our local address. */
+	memcpy(s->eh.h_dest,   &src, ETH_ALEN);
+	memcpy(s->eh.h_source, &dst, ETH_ALEN);
+
+	s->sock  = sock;
+	s->role  = req->role;
+	s->state = BT_CONNECTED;
+	
+	s->msg.msg_flags = MSG_NOSIGNAL;
+
+#ifdef CONFIG_BLUEZ_BNEP_MC_FILTER
+	/* Set default mc filter */
+	set_bit(bnep_mc_hash(dev->broadcast), (ulong *) &s->mc_filter);
+#endif
+	
+#ifdef CONFIG_BLUEZ_BNEP_PROTO_FILTER
+	/* Set default protocol filter */
+
+	/* (IPv4, ARP)  */
+	s->proto_filter[0].start = htons(0x0800);
+	s->proto_filter[0].end   = htons(0x0806);
+	/* (RARP, AppleTalk) */
+	s->proto_filter[1].start = htons(0x8035);
+	s->proto_filter[1].end   = htons(0x80F3);
+	/* (IPX, IPv6) */
+	s->proto_filter[2].start = htons(0x8137);
+	s->proto_filter[2].end   = htons(0x86DD);
+#endif
+	
+	dev->init = bnep_net_init;
+	dev->priv = s;
+	err = register_netdev(dev);
+	if (err) {
+		goto failed;
+	}
+
+	__bnep_link_session(s);
+	
+	err = kernel_thread(bnep_session, s, CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
+	if (err < 0) {
+		/* Session thread start failed, gotta cleanup. */
+		unregister_netdev(dev);
+		__bnep_unlink_session(s);
+		goto failed;
+	}
+
+	up_write(&bnep_session_sem);
+	strcpy(req->device, dev->name);
+	return 0;
+
+failed:
+	up_write(&bnep_session_sem);
+	kfree(s);
+	return err;
+}
+
+int bnep_del_connection(struct bnep_condel_req *req)
+{
+	struct bnep_session *s;
+	int  err = 0;
+
+	BT_DBG("");
+
+	down_read(&bnep_session_sem);
+
+	s = __bnep_get_session(req->dst);
+	if (s) {
+		/* Wakeup user-space which is polling for socket errors.
+		 * This is temporary hack untill we have shutdown in L2CAP */
+		s->sock->sk->err = EUNATCH;
+		
+		/* Kill session thread */
+		atomic_inc(&s->killed);
+		wake_up_interruptible(s->sock->sk->sleep);
+	} else
+		err = -ENOENT;
+
+	up_read(&bnep_session_sem);
+	return err;
+}
+
+static void __bnep_copy_ci(struct bnep_coninfo *ci, struct bnep_session *s)
+{
+	memcpy(ci->dst, s->eh.h_source, ETH_ALEN);
+	strcpy(ci->device, s->dev.name);
+	ci->flags = s->flags;
+	ci->state = s->state;
+	ci->role  = s->role;
+}
+
+int bnep_get_conlist(struct bnep_conlist_req *req)
+{
+	struct list_head *p;
+	int err = 0, n = 0;
+
+	down_read(&bnep_session_sem);
+
+	list_for_each(p, &bnep_session_list) {
+		struct bnep_session *s;
+		struct bnep_coninfo ci;
+
+		s = list_entry(p, struct bnep_session, list);
+
+		__bnep_copy_ci(&ci, s);
+		
+		if (copy_to_user(req->ci, &ci, sizeof(ci))) {
+			err = -EFAULT;
+			break;
+		}
+
+		if (++n >= req->cnum)
+			break;
+
+		req->ci++;
+	}
+	req->cnum = n;
+
+	up_read(&bnep_session_sem);
+	return err;
+}
+
+int bnep_get_coninfo(struct bnep_coninfo *ci)
+{
+	struct bnep_session *s;
+	int err = 0;
+
+	down_read(&bnep_session_sem);
+
+	s = __bnep_get_session(ci->dst);
+	if (s)
+		__bnep_copy_ci(ci, s);
+	else
+		err = -ENOENT;
+
+	up_read(&bnep_session_sem);
+	return err;
+}
+
+static int __init bnep_init_module(void)
+{
+	BT_INFO("BNEP: BNEP2 ver %s\n"
+		"BNEP: Copyright (C) 2002 Inventel\n"
+		"BNEP: Written 2001,2002 by\n"
+		"BNEP: \tClement Moreau <clement.moreau@inventel.fr> "
+			"David Libault <david.libault@inventel.fr>\n"
+		"BNEP: Copyright (C) 2002 Maxim Krasnyanskiy <maxk@qualcomm.com>",
+			VERSION);
+
+	bnep_sock_init();
+
+	bnep_crc32_init();
+
+	return 0;
+}
+
+static void __exit bnep_cleanup_module(void)
+{
+	bnep_sock_cleanup();
+
+	bnep_crc32_cleanup();
+}
+
+module_init(bnep_init_module);
+module_exit(bnep_cleanup_module);
+
+MODULE_DESCRIPTION("BNEP ver " VERSION);
+MODULE_AUTHOR("David Libault <david.libault@inventel.fr> Maxim Krasnyanskiy <maxk@qualcomm.com>");
+MODULE_LICENSE("GPL");
diff -Nru a/net/bluetooth/bnep/crc32.c b/net/bluetooth/bnep/crc32.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/net/bluetooth/bnep/crc32.c	Mon Oct  7 11:26:06 2002
@@ -0,0 +1,59 @@
+/* 
+ * Based on linux-2.5/lib/crc32 by Matt Domsch <Matt_Domsch@dell.com>
+ *
+ * FIXME: Remove in 2.5  
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <asm/atomic.h>
+
+#include "crc32.h"
+
+#define CRCPOLY_BE 0x04c11db7
+#define CRC_BE_BITS 8
+
+static u32 *bnep_crc32_table;
+
+/*
+ * This code is in the public domain; copyright abandoned.
+ * Liability for non-performance of this code is limited to the amount
+ * you paid for it.  Since it is distributed for free, your refund will
+ * be very very small.  If it breaks, you get to keep both pieces.
+ */
+u32 bnep_crc32(u32 crc, unsigned char const *p, size_t len)
+{
+	while (len--)
+		crc = (crc << 8) ^ bnep_crc32_table[(crc >> 24) ^ *p++];
+	
+	return crc;
+}
+
+int __init bnep_crc32_init(void)
+{
+	unsigned i, j;
+	u32 crc = 0x80000000;
+
+	bnep_crc32_table = kmalloc((1 << CRC_BE_BITS) * sizeof(u32), GFP_KERNEL);
+	if (!bnep_crc32_table)
+		return -ENOMEM;
+
+	bnep_crc32_table[0] = 0;
+
+	for (i = 1; i < 1 << CRC_BE_BITS; i <<= 1) {
+		crc = (crc << 1) ^ ((crc & 0x80000000) ? CRCPOLY_BE : 0);
+		for (j = 0; j < i; j++)
+			bnep_crc32_table[i + j] = crc ^ bnep_crc32_table[j];
+	}
+	return 0;
+}
+
+void __exit bnep_crc32_cleanup(void)
+{
+	if (bnep_crc32_table)
+		kfree(bnep_crc32_table);
+	bnep_crc32_table = NULL;
+}
diff -Nru a/net/bluetooth/bnep/crc32.h b/net/bluetooth/bnep/crc32.h
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/net/bluetooth/bnep/crc32.h	Mon Oct  7 11:25:40 2002
@@ -0,0 +1,10 @@
+/*
+ * crc32.h
+ * See crc32.c for license and changes
+ *
+ * FIXME: Remove in 2.5
+ */
+
+int  bnep_crc32_init(void);
+void bnep_crc32_cleanup(void);
+u32  bnep_crc32(u32 crc, unsigned char const *p, size_t len);
diff -Nru a/net/bluetooth/bnep/netdev.c b/net/bluetooth/bnep/netdev.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/net/bluetooth/bnep/netdev.c	Mon Oct  7 11:26:06 2002
@@ -0,0 +1,250 @@
+/* 
+   BNEP implementation for Linux Bluetooth stack (BlueZ).
+   Copyright (C) 2001-2002 Inventel Systemes
+   Written 2001-2002 by
+	Clément Moreau <clement.moreau@inventel.fr>
+	David Libault  <david.libault@inventel.fr>
+
+   Copyright (C) 2002 Maxim Krasnyanskiy <maxk@qualcomm.com>
+
+   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;
+
+   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+   IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
+   CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES 
+   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 
+   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 
+   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+   ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, 
+   COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS 
+   SOFTWARE IS DISCLAIMED.
+*/
+
+/*
+ * $Id: netdev.c,v 1.8 2002/08/04 21:23:58 maxk Exp $
+ */ 
+
+#include <linux/config.h>
+#include <linux/module.h>
+
+#include <linux/socket.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/wait.h>
+
+#include <asm/unaligned.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+#include <net/bluetooth/l2cap.h>
+
+#include "bnep.h"
+
+#ifndef CONFIG_BLUEZ_BNEP_DEBUG
+#undef  BT_DBG
+#define BT_DBG( A... )
+#endif
+
+#define BNEP_TX_QUEUE_LEN 20
+
+static int bnep_net_open(struct net_device *dev)
+{
+	netif_start_queue(dev);
+	return 0;
+}
+
+static int bnep_net_close(struct net_device *dev)
+{
+	netif_stop_queue(dev);
+	return 0;
+}
+
+static struct net_device_stats *bnep_net_get_stats(struct net_device *dev)
+{
+	struct bnep_session *s = dev->priv;
+	return &s->stats;
+}
+
+static void bnep_net_set_mc_list(struct net_device *dev)
+{
+#ifdef CONFIG_BLUEZ_BNEP_MC_FILTER
+	struct bnep_session *s = dev->priv;
+	struct sock *sk = s->sock->sk;
+	struct bnep_set_filter_req *r;
+	struct sk_buff *skb;
+	int size;
+
+	BT_DBG("%s mc_count %d", dev->name, dev->mc_count);
+
+	size = sizeof(*r) + (BNEP_MAX_MULTICAST_FILTERS + 1) * ETH_ALEN * 2;
+	skb  = alloc_skb(size, GFP_ATOMIC);
+	if (!skb) {
+		BT_ERR("%s Multicast list allocation failed", dev->name);
+		return;
+	}
+
+	r = (void *) skb->data;
+	__skb_put(skb, sizeof(*r));
+
+	r->type = BNEP_CONTROL;
+	r->ctrl = BNEP_FILTER_MULTI_ADDR_SET;
+
+        if (dev->flags & (IFF_PROMISC | IFF_ALLMULTI)) {
+		u8 start[ETH_ALEN] = { 0x01 };
+
+		/* Request all addresses */
+		memcpy(__skb_put(skb, ETH_ALEN), start, ETH_ALEN);
+		memcpy(__skb_put(skb, ETH_ALEN), dev->broadcast, ETH_ALEN);
+		r->len = htons(ETH_ALEN * 2);
+	} else {
+                struct dev_mc_list *dmi = dev->mc_list;
+		int i, len = skb->len;
+
+		if (dev->flags & IFF_BROADCAST) {
+			memcpy(__skb_put(skb, ETH_ALEN), dev->broadcast, ETH_ALEN);
+			memcpy(__skb_put(skb, ETH_ALEN), dev->broadcast, ETH_ALEN);
+		}	
+		
+		/* FIXME: We should group addresses here. */
+
+		for (i = 0; i < dev->mc_count && i < BNEP_MAX_MULTICAST_FILTERS; i++) {
+			memcpy(__skb_put(skb, ETH_ALEN), dmi->dmi_addr, ETH_ALEN);
+			memcpy(__skb_put(skb, ETH_ALEN), dmi->dmi_addr, ETH_ALEN);
+			dmi = dmi->next;
+		}
+		r->len = htons(skb->len - len);
+	}
+
+	skb_queue_tail(&sk->write_queue, skb);
+	wake_up_interruptible(sk->sleep);
+#endif
+}
+
+static int bnep_net_set_mac_addr(struct net_device *dev, void *arg)
+{
+	BT_DBG("%s", dev->name);
+	return 0;
+}
+
+static void bnep_net_timeout(struct net_device *dev)
+{
+	BT_DBG("net_timeout");
+	netif_wake_queue(dev);
+}
+
+static int bnep_net_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+	return -EINVAL;
+}
+
+#ifdef CONFIG_BLUEZ_BNEP_MC_FILTER
+static inline int bnep_net_mc_filter(struct sk_buff *skb, struct bnep_session *s)
+{
+	struct ethhdr *eh = (void *) skb->data;
+
+	if ((eh->h_dest[0] & 1) && !test_bit(bnep_mc_hash(eh->h_dest), (ulong *) &s->mc_filter))
+		return 1;
+	return 0;
+}
+#endif
+
+#ifdef CONFIG_BLUEZ_BNEP_PROTO_FILTER
+/* Determine ether protocol. Based on eth_type_trans. */
+static inline u16 bnep_net_eth_proto(struct sk_buff *skb)
+{
+	struct ethhdr *eh = (void *) skb->data;
+	
+	if (ntohs(eh->h_proto) >= 1536)
+		return eh->h_proto;
+		
+	if (get_unaligned((u16 *) skb->data) == 0xFFFF)
+		return htons(ETH_P_802_3);
+		
+	return htons(ETH_P_802_2);
+}
+
+static inline int bnep_net_proto_filter(struct sk_buff *skb, struct bnep_session *s)
+{
+	u16 proto = bnep_net_eth_proto(skb);
+	struct bnep_proto_filter *f = s->proto_filter;
+	int i;
+	
+	for (i = 0; i < BNEP_MAX_PROTO_FILTERS && f[i].end; i++) {
+		if (proto >= f[i].start && proto <= f[i].end)
+			return 0;
+	}
+
+	BT_DBG("BNEP: filtered skb %p, proto 0x%.4x", skb, proto);
+	return 1;
+}
+#endif
+
+static int bnep_net_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+	struct bnep_session *s = dev->priv;
+	struct sock *sk = s->sock->sk;
+
+	BT_DBG("skb %p, dev %p", skb, dev);
+
+#ifdef CONFIG_BLUEZ_BNEP_MC_FILTER
+	if (bnep_net_mc_filter(skb, s)) {
+		kfree_skb(skb);
+		return 0;
+	}
+#endif
+	
+#ifdef CONFIG_BLUEZ_BNEP_PROTO_FILTER
+	if (bnep_net_proto_filter(skb, s)) {
+		kfree_skb(skb);
+		return 0;
+	}
+#endif
+	
+	/*
+	 * We cannot send L2CAP packets from here as we are potentially in a bh.
+	 * So we have to queue them and wake up session thread which is sleeping
+	 * on the sk->sleep.
+	 */
+	dev->trans_start = jiffies;
+	skb_queue_tail(&sk->write_queue, skb);
+	wake_up_interruptible(sk->sleep);
+
+	if (skb_queue_len(&sk->write_queue) >= BNEP_TX_QUEUE_LEN) {
+		BT_DBG("tx queue is full");
+
+		/* Stop queuing.
+		 * Session thread will do netif_wake_queue() */
+		netif_stop_queue(dev);
+	}
+
+	return 0;
+}
+
+int bnep_net_init(struct net_device *dev)
+{
+	struct bnep_session *s = dev->priv;
+
+	memcpy(dev->dev_addr, s->eh.h_dest, ETH_ALEN);
+	dev->addr_len = ETH_ALEN;
+
+	ether_setup(dev);
+
+	dev->open            = bnep_net_open;
+	dev->stop            = bnep_net_close;
+	dev->hard_start_xmit = bnep_net_xmit;
+	dev->get_stats       = bnep_net_get_stats;
+	dev->do_ioctl        = bnep_net_ioctl;
+	dev->set_mac_address = bnep_net_set_mac_addr;
+	dev->set_multicast_list = bnep_net_set_mc_list;
+
+	dev->watchdog_timeo  = HZ * 2;
+	dev->tx_timeout      = bnep_net_timeout;
+
+	return 0;
+}
diff -Nru a/net/bluetooth/bnep/sock.c b/net/bluetooth/bnep/sock.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/net/bluetooth/bnep/sock.c	Mon Oct  7 11:26:06 2002
@@ -0,0 +1,231 @@
+/* 
+   BNEP implementation for Linux Bluetooth stack (BlueZ).
+   Copyright (C) 2001-2002 Inventel Systemes
+   Written 2001-2002 by
+	David Libault  <david.libault@inventel.fr>
+
+   Copyright (C) 2002 Maxim Krasnyanskiy <maxk@qualcomm.com>
+
+   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;
+
+   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+   IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
+   CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES 
+   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 
+   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 
+   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+   ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, 
+   COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS 
+   SOFTWARE IS DISCLAIMED.
+*/
+
+/*
+ * $Id: sock.c,v 1.4 2002/08/04 21:23:58 maxk Exp $
+ */ 
+
+#include <linux/config.h>
+#include <linux/module.h>
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/poll.h>
+#include <linux/fcntl.h>
+#include <linux/skbuff.h>
+#include <linux/socket.h>
+#include <linux/ioctl.h>
+#include <linux/file.h>
+#include <net/sock.h>
+
+#include <asm/system.h>
+#include <asm/uaccess.h>
+
+#include "bnep.h"
+
+#ifndef CONFIG_BLUEZ_BNEP_DEBUG
+#undef  BT_DBG
+#define BT_DBG( A... )
+#endif
+
+static struct socket *sockfd_lookup(int fd, int *err)
+{
+	struct file *file;
+	struct inode *inode;
+	struct socket *sock;
+
+	if (!(file = fget(fd))) {
+		*err = -EBADF;
+		return NULL;
+	}
+
+	inode = file->f_dentry->d_inode;
+	if (!inode->i_sock || !(sock = SOCKET_I(inode))) {
+		*err = -ENOTSOCK;
+		fput(file);
+		return NULL;
+	}
+
+	if (sock->file != file) {
+		printk(KERN_ERR "socki_lookup: socket file changed!\n");
+		sock->file = file;
+	}
+	return sock;
+}
+ 
+static int bnep_sock_release(struct socket *sock)
+{
+	struct sock *sk = sock->sk;
+
+	BT_DBG("sock %p sk %p", sock, sk);
+
+	if (!sk)
+		return 0;
+
+	sock_orphan(sk);
+	sock_put(sk);
+
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+static int bnep_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
+{
+	struct bnep_conlist_req cl;
+	struct bnep_conadd_req  ca;
+	struct bnep_condel_req  cd;
+	struct bnep_coninfo ci;
+	struct socket *nsock;
+	int err;
+
+	BT_DBG("cmd %x arg %lx", cmd, arg);
+
+	switch (cmd) {
+	case BNEPCONADD:
+		if (!capable(CAP_NET_ADMIN))
+			return -EACCES;
+
+		if (copy_from_user(&ca, (void *) arg, sizeof(ca)))
+			return -EFAULT;
+	
+		nsock = sockfd_lookup(ca.sock, &err);
+		if (!nsock)
+			return err;
+
+		if (nsock->sk->state != BT_CONNECTED)
+			return -EBADFD;
+
+		err = bnep_add_connection(&ca, nsock);
+		if (!err) {
+    			if (copy_to_user((void *) arg, &ca, sizeof(ca)))
+				err = -EFAULT;
+		} else
+			fput(nsock->file);
+
+		return err;
+	
+	case BNEPCONDEL:
+		if (!capable(CAP_NET_ADMIN))
+			return -EACCES;
+
+		if (copy_from_user(&cd, (void *) arg, sizeof(cd)))
+			return -EFAULT;
+	
+		return bnep_del_connection(&cd);
+
+	case BNEPGETCONLIST:
+		if (copy_from_user(&cl, (void *) arg, sizeof(cl)))
+			return -EFAULT;
+
+		if (cl.cnum <= 0)
+			return -EINVAL;
+	
+		err = bnep_get_conlist(&cl);
+		if (!err && copy_to_user((void *) arg, &cl, sizeof(cl)))
+			return -EFAULT;
+
+		return err;
+
+	case BNEPGETCONINFO:
+		if (copy_from_user(&ci, (void *) arg, sizeof(ci)))
+			return -EFAULT;
+
+		err = bnep_get_coninfo(&ci);
+		if (!err && copy_to_user((void *) arg, &ci, sizeof(ci)))
+			return -EFAULT;
+
+		return err;
+
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static struct proto_ops bnep_sock_ops = {
+	.family     = PF_BLUETOOTH,
+	.release    = bnep_sock_release,
+	.ioctl      = bnep_sock_ioctl,
+	.bind       = sock_no_bind,
+	.getname    = sock_no_getname,
+	.sendmsg    = sock_no_sendmsg,
+	.recvmsg    = sock_no_recvmsg,
+	.poll       = sock_no_poll,
+	.listen     = sock_no_listen,
+	.shutdown   = sock_no_shutdown,
+	.setsockopt = sock_no_setsockopt,
+	.getsockopt = sock_no_getsockopt,
+	.connect    = sock_no_connect,
+	.socketpair = sock_no_socketpair,
+	.accept     = sock_no_accept,
+	.mmap       = sock_no_mmap
+};
+
+static int bnep_sock_create(struct socket *sock, int protocol)
+{
+	struct sock *sk;
+
+	BT_DBG("sock %p", sock);
+
+	if (sock->type != SOCK_RAW)
+		return -ESOCKTNOSUPPORT;
+
+	if (!(sk = bluez_sock_alloc(sock, PF_BLUETOOTH, 0, GFP_KERNEL)))
+		return -ENOMEM;
+	sock->ops = &bnep_sock_ops;
+
+	MOD_INC_USE_COUNT;
+
+	sock->state = SS_UNCONNECTED;
+
+	sk->destruct = NULL;
+	sk->protocol = protocol;
+
+	return 0;
+}
+
+static struct net_proto_family bnep_sock_family_ops = {
+	.family = PF_BLUETOOTH,
+	.create = bnep_sock_create
+};
+
+int bnep_sock_init(void)
+{
+	bluez_sock_register(BTPROTO_BNEP, &bnep_sock_family_ops);
+	return 0;
+}
+
+int bnep_sock_cleanup(void)
+{
+	if (bluez_sock_unregister(BTPROTO_BNEP))
+		BT_ERR("Can't unregister BNEP socket");
+	return 0;
+}
diff -Nru a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
--- a/net/bluetooth/hci_conn.c	Mon Oct  7 11:25:39 2002
+++ b/net/bluetooth/hci_conn.c	Mon Oct  7 11:25:39 2002
@@ -73,7 +73,7 @@
 	bacpy(&cp.bdaddr, &conn->dst);
 
 	if ((ie = inquiry_cache_lookup(hdev, &conn->dst)) &&
-			inquiry_entry_age(ie) > INQUIRY_ENTRY_AGE_MAX) {
+			inquiry_entry_age(ie) <= INQUIRY_ENTRY_AGE_MAX) {
 		cp.pscan_rep_mode = ie->info.pscan_rep_mode;
 		cp.pscan_mode     = ie->info.pscan_mode;
 		cp.clock_offset   = ie->info.clock_offset | __cpu_to_le16(0x8000);
@@ -188,9 +188,6 @@
 			acl->link = NULL;
 			hci_conn_put(acl);
 		}
-
-		/* Unacked frames */
-		hdev->sco_cnt += conn->sent;
 	} else {
 		struct hci_conn *sco = conn->link;
 		if (sco)
@@ -220,7 +217,7 @@
 
 	BT_DBG("%s -> %s", batostr(src), batostr(dst));
 
-	spin_lock_bh(&hdev_list_lock);
+	read_lock_bh(&hdev_list_lock);
 
 	list_for_each(p, &hdev_list) {
 		struct hci_dev *d;
@@ -248,7 +245,7 @@
 	if (hdev)
 		hci_dev_hold(hdev);
 
-	spin_unlock_bh(&hdev_list_lock);
+	read_unlock_bh(&hdev_list_lock);
 	return hdev;
 }
 
diff -Nru a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
--- a/net/bluetooth/hci_core.c	Mon Oct  7 11:25:28 2002
+++ b/net/bluetooth/hci_core.c	Mon Oct  7 11:25:28 2002
@@ -30,6 +30,7 @@
 
 #include <linux/config.h>
 #include <linux/module.h>
+#include <linux/kmod.h>
 
 #include <linux/types.h>
 #include <linux/errno.h>
@@ -66,7 +67,7 @@
 
 /* HCI device list */
 LIST_HEAD(hdev_list);
-spinlock_t hdev_list_lock;
+rwlock_t hdev_list_lock = RW_LOCK_UNLOCKED;
 
 /* HCI protocols */
 #define HCI_MAX_PROTO	2
@@ -75,7 +76,6 @@
 /* HCI notifiers list */
 static struct notifier_block *hci_notifier;
 
-
 /* ---- HCI notifications ---- */
 
 int hci_register_notifier(struct notifier_block *nb)
@@ -93,6 +93,32 @@
 	notifier_call_chain(&hci_notifier, event, hdev);
 }
 
+/* ---- HCI hotplug support ---- */
+
+#ifdef CONFIG_HOTPLUG
+
+static int hci_run_hotplug(char *dev, char *action)
+{
+	char *argv[3], *envp[5], dstr[20], astr[32];
+
+	sprintf(dstr, "DEVICE=%s", dev);
+	sprintf(astr, "ACTION=%s", action);
+
+        argv[0] = hotplug_path;
+        argv[1] = "bluetooth";
+        argv[2] = NULL;
+
+	envp[0] = "HOME=/";
+	envp[1] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin";
+	envp[2] = dstr;
+	envp[3] = astr;
+	envp[4] = NULL;
+	
+	return call_usermodehelper(argv[0], argv, envp);
+}
+#else
+#define hci_run_hotplug(A...)
+#endif
 
 /* ---- HCI requests ---- */
 
@@ -270,7 +296,7 @@
 	if (index < 0)
 		return NULL;
 
-	spin_lock(&hdev_list_lock);
+	read_lock(&hdev_list_lock);
 	list_for_each(p, &hdev_list) {
 		hdev = list_entry(p, struct hci_dev, list);
 		if (hdev->id == index) {
@@ -280,7 +306,7 @@
 	}
 	hdev = NULL;
 done:
-	spin_unlock(&hdev_list_lock);
+	read_unlock(&hdev_list_lock);
 	return hdev;
 }
 
@@ -699,7 +725,7 @@
 		return -ENOMEM;
 	dr = dl->dev_req;
 
-	spin_lock_bh(&hdev_list_lock);
+	read_lock_bh(&hdev_list_lock);
 	list_for_each(p, &hdev_list) {
 		struct hci_dev *hdev;
 		hdev = list_entry(p, struct hci_dev, list);
@@ -708,7 +734,7 @@
 		if (++n >= dev_num)
 			break;
 	}
-	spin_unlock_bh(&hdev_list_lock);
+	read_unlock_bh(&hdev_list_lock);
 
 	dl->dev_num = n;
 	size = n * sizeof(struct hci_dev_req) + sizeof(__u16);
@@ -768,7 +794,7 @@
 	if (!hdev->open || !hdev->close || !hdev->destruct)
 		return -EINVAL;
 
-	spin_lock_bh(&hdev_list_lock);
+	write_lock_bh(&hdev_list_lock);
 
 	/* Find first available device id */
 	list_for_each(p, &hdev_list) {
@@ -807,11 +833,12 @@
 
 	atomic_set(&hdev->promisc, 0);
 		
-	hci_notify(hdev, HCI_DEV_REG);
-
 	MOD_INC_USE_COUNT;
 
-	spin_unlock_bh(&hdev_list_lock);
+	write_unlock_bh(&hdev_list_lock);
+
+	hci_notify(hdev, HCI_DEV_REG);
+	hci_run_hotplug(hdev->name, "register");
 
 	return id;
 }
@@ -821,13 +848,15 @@
 {
 	BT_DBG("%p name %s type %d", hdev, hdev->name, hdev->type);
 
-	spin_lock_bh(&hdev_list_lock);
+	write_lock_bh(&hdev_list_lock);
 	list_del(&hdev->list);
-	spin_unlock_bh(&hdev_list_lock);
+	write_unlock_bh(&hdev_list_lock);
 
 	hci_dev_do_close(hdev);
 
 	hci_notify(hdev, HCI_DEV_UNREG);
+	hci_run_hotplug(hdev->name, "unregister");
+	
 	hci_dev_put(hdev);
 
 	MOD_DEC_USE_COUNT;
@@ -1103,14 +1132,13 @@
 {
 	struct conn_hash *h = &hdev->conn_hash;
 	struct hci_conn  *conn = NULL;
-	int num = 0, min = 0xffff;
+	int num = 0, min = ~0;
         struct list_head *p;
 
 	/* We don't have to lock device here. Connections are always 
 	 * added and removed with TX task disabled. */
 	list_for_each(p, &h->list) {
 		struct hci_conn *c;
-
 		c = list_entry(p, struct hci_conn, list);
 
 		if (c->type != type || c->state != BT_CONNECTED
@@ -1118,14 +1146,15 @@
 			continue;
 		num++;
 
-		if (c->sent < min || type == SCO_LINK) {
+		if (c->sent < min) {
 			min  = c->sent;
 			conn = c;
 		}
 	}
 
 	if (conn) {
-		int q = hdev->acl_cnt / num;
+		int cnt = (type == ACL_LINK ? hdev->acl_cnt : hdev->sco_cnt);
+		int q = cnt / num;
 		*quote = q ? q : 1;
 	} else
 		*quote = 0;
@@ -1134,6 +1163,25 @@
 	return conn;
 }
 
+static inline void hci_acl_tx_to(struct hci_dev *hdev)
+{
+	struct conn_hash *h = &hdev->conn_hash;
+        struct list_head *p;
+	struct hci_conn  *c;
+
+	BT_ERR("%s ACL tx timeout", hdev->name);
+
+	/* Kill stalled connections */
+	list_for_each(p, &h->list) {
+		c = list_entry(p, struct hci_conn, list);
+		if (c->type == ACL_LINK && c->sent) {
+			BT_ERR("%s killing stalled ACL connection %s",
+				hdev->name, batostr(&c->dst));
+			hci_acl_disconn(c, 0x13);
+		}
+	}
+}
+
 static inline void hci_sched_acl(struct hci_dev *hdev)
 {
 	struct hci_conn *conn;
@@ -1142,21 +1190,19 @@
 
 	BT_DBG("%s", hdev->name);
 
-	if (!hdev->acl_cnt && (jiffies - hdev->acl_last_tx) > HZ*5) {
-		BT_ERR("%s ACL tx timeout", hdev->name);
-		hdev->acl_cnt++;
-	}
-	
+	/* ACL tx timeout must be longer than maximum
+	 * link supervision timeout (40.9 seconds) */
+	if (!hdev->acl_cnt && (jiffies - hdev->acl_last_tx) > (HZ * 45))
+		hci_acl_tx_to(hdev);
+
 	while (hdev->acl_cnt && (conn = hci_low_sent(hdev, ACL_LINK, &quote))) {
-		while (quote && (skb = skb_dequeue(&conn->data_q))) {
+		while (quote-- && (skb = skb_dequeue(&conn->data_q))) {
 			BT_DBG("skb %p len %d", skb, skb->len);
-
 			hci_send_frame(skb);
 			hdev->acl_last_tx = jiffies;
 
-			conn->sent++;
 			hdev->acl_cnt--;
-			quote--;
+			conn->sent++;
 		}
 	}
 }
@@ -1170,15 +1216,14 @@
 
 	BT_DBG("%s", hdev->name);
 
-	while ((conn = hci_low_sent(hdev, SCO_LINK, &quote))) {
-		while (quote && (skb = skb_dequeue(&conn->data_q))) {
+	while (hdev->sco_cnt && (conn = hci_low_sent(hdev, SCO_LINK, &quote))) {
+		while (quote-- && (skb = skb_dequeue(&conn->data_q))) {
 			BT_DBG("skb %p len %d", skb, skb->len);
-
 			hci_send_frame(skb);
 
-			//conn->sent++;
-			//hdev->sco_cnt--;
-			quote--;
+			conn->sent++;
+			if (conn->sent == ~0)
+				conn->sent = 0;
 		}
 	}
 }
@@ -1205,7 +1250,6 @@
 	read_unlock(&hci_task_lock);
 }
 
-
 /* ----- HCI RX task (incomming data proccessing) ----- */
 
 /* ACL data packet */
@@ -1367,9 +1411,6 @@
 
 int hci_core_init(void)
 {
-	/* Init locks */
-	spin_lock_init(&hdev_list_lock);
-
 	return 0;
 }
 
diff -Nru a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
--- a/net/bluetooth/hci_event.c	Mon Oct  7 11:25:25 2002
+++ b/net/bluetooth/hci_event.c	Mon Oct  7 11:25:25 2002
@@ -352,15 +352,12 @@
 			hci_dev_lock(hdev);
 	
 			acl = conn_hash_lookup_handle(hdev, handle);
-			if (!acl || !(sco = acl->link)) {
-				hci_dev_unlock(hdev);
-				break;
-			}
-
-			sco->state = BT_CLOSED;
+			if (acl && (sco = acl->link)) {
+				sco->state = BT_CLOSED;
 
-			hci_proto_connect_cfm(sco, status);
-			hci_conn_del(sco);
+				hci_proto_connect_cfm(sco, status);
+				hci_conn_del(sco);
+			}
 
 			hci_dev_unlock(hdev);
 		}
diff -Nru a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c
--- a/net/bluetooth/hci_sock.c	Mon Oct  7 11:25:35 2002
+++ b/net/bluetooth/hci_sock.c	Mon Oct  7 11:25:35 2002
@@ -41,7 +41,7 @@
 #include <linux/fcntl.h>
 #include <linux/init.h>
 #include <linux/skbuff.h>
-#include <linux/tqueue.h>
+#include <linux/workqueue.h>
 #include <linux/interrupt.h>
 #include <linux/socket.h>
 #include <linux/ioctl.h>
@@ -86,7 +86,7 @@
 /* Send frame to RAW socket */
 void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb)
 {
-	struct sock * sk;
+	struct sock *sk;
 
 	BT_DBG("hdev %p len %d", hdev, skb->len);
 
@@ -105,13 +105,13 @@
 		/* Apply filter */
 		flt = &hci_pi(sk)->filter;
 
-		if (!hci_test_bit((skb->pkt_type & HCI_FLT_TYPE_BITS), &flt->type_mask))
+		if (!test_bit((skb->pkt_type & HCI_FLT_TYPE_BITS), &flt->type_mask))
 			continue;
 
 		if (skb->pkt_type == HCI_EVENT_PKT) {
 			register int evt = (*(__u8 *)skb->data & HCI_FLT_EVENT_BITS);
 			
-			if (!hci_test_bit(evt, &flt->event_mask))
+			if (!test_bit(evt, flt->event_mask))
 				continue;
 
 			if (flt->opcode && ((evt == EVT_CMD_COMPLETE && 
@@ -249,7 +249,7 @@
 		err = hci_sock_bound_ioctl(sk, cmd, arg);
 		release_sock(sk);
 		return err;
-	};
+	}
 }
 
 static int hci_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_len)
@@ -393,12 +393,12 @@
 		err = -EPERM;
 
 		if (skb->pkt_type == HCI_COMMAND_PKT) {
-			__u16 opcode = __le16_to_cpu(*(__u16 *)skb->data);
-			__u16 ogf = cmd_opcode_ogf(opcode) - 1;
-			__u16 ocf = cmd_opcode_ocf(opcode) & HCI_FLT_OCF_BITS;
+			u16 opcode = __le16_to_cpu(*(__u16 *)skb->data);
+			u16 ogf = cmd_opcode_ogf(opcode) - 1;
+			u16 ocf = cmd_opcode_ocf(opcode) & HCI_FLT_OCF_BITS;
 
 			if (ogf > HCI_SFLT_MAX_OGF ||
-					!hci_test_bit(ocf, &hci_sec_filter.ocf_mask[ogf]))
+					!test_bit(ocf, hci_sec_filter.ocf_mask[ogf]))
 				goto drop;
 		} else
 			goto drop;
@@ -420,8 +420,8 @@
 
 int hci_sock_setsockopt(struct socket *sock, int level, int optname, char *optval, int len)
 {
+	struct hci_ufilter uf = { .opcode = 0 };
 	struct sock *sk = sock->sk;
-	struct hci_filter flt = { opcode: 0 };
 	int err = 0, opt = 0;
 
 	BT_DBG("sk %p, opt %d", sk, optname);
@@ -454,32 +454,40 @@
 		break;
 
 	case HCI_FILTER:
-		len = MIN(len, sizeof(struct hci_filter));
-		if (copy_from_user(&flt, optval, len)) {
+		len = MIN(len, sizeof(uf));
+		if (copy_from_user(&uf, optval, len)) {
 			err = -EFAULT;
 			break;
 		}
 
 		if (!capable(CAP_NET_RAW)) {
-			flt.type_mask     &= hci_sec_filter.type_mask;
-			flt.event_mask[0] &= hci_sec_filter.event_mask[0];
-			flt.event_mask[1] &= hci_sec_filter.event_mask[1];
+			uf.type_mask &= hci_sec_filter.type_mask;
+			uf.event_mask[0] &= *((u32 *) hci_sec_filter.event_mask + 0);
+			uf.event_mask[1] &= *((u32 *) hci_sec_filter.event_mask + 1);
 		}
+
+		{	
+			struct hci_filter *f = &hci_pi(sk)->filter;
 		
-		memcpy(&hci_pi(sk)->filter, &flt, len);
-		break;
+			f->type_mask = uf.type_mask;
+			f->opcode    = uf.opcode;
+			*((u32 *) f->event_mask + 0) = uf.event_mask[0];
+			*((u32 *) f->event_mask + 1) = uf.event_mask[0];
+		}
+		break; 
 
 	default:
 		err = -ENOPROTOOPT;
 		break;
-	};
-
+	}
+	
 	release_sock(sk);
 	return err;
 }
 
 int hci_sock_getsockopt(struct socket *sock, int level, int optname, char *optval, int *optlen)
 {
+	struct hci_ufilter uf;
 	struct sock *sk = sock->sk;
 	int len, opt; 
 
@@ -508,15 +516,24 @@
 		break;
 
 	case HCI_FILTER:
-		len = MIN(len, sizeof(struct hci_filter));
-		if (copy_to_user(optval, &hci_pi(sk)->filter, len))
+		{
+			struct hci_filter *f = &hci_pi(sk)->filter;
+		
+			uf.type_mask = f->type_mask;
+			uf.opcode    = f->opcode;
+			uf.event_mask[0] = *((u32 *) f->event_mask + 0);
+			uf.event_mask[0] = *((u32 *) f->event_mask + 1);
+		}
+
+		len = MIN(len, sizeof(uf));
+		if (copy_to_user(optval, &uf, len))
 			return -EFAULT;
 		break;
 
 	default:
 		return -ENOPROTOOPT;
 		break;
-	};
+	}
 
 	return 0;
 }
diff -Nru a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c
--- a/net/bluetooth/l2cap.c	Mon Oct  7 11:25:36 2002
+++ b/net/bluetooth/l2cap.c	Mon Oct  7 11:25:36 2002
@@ -25,9 +25,9 @@
 /*
  * BlueZ L2CAP core and sockets.
  *
- * $Id: l2cap.c,v 1.8 2002/04/19 00:01:39 maxk Exp $
+ * $Id: l2cap.c,v 1.15 2002/09/09 01:14:52 maxk Exp $
  */
-#define VERSION "2.0"
+#define VERSION "2.1"
 
 #include <linux/config.h>
 #include <linux/module.h>
@@ -51,6 +51,7 @@
 
 #include <asm/system.h>
 #include <asm/uaccess.h>
+#include <asm/unaligned.h>
 
 #include <net/bluetooth/bluetooth.h>
 #include <net/bluetooth/hci_core.h>
@@ -69,7 +70,7 @@
 
 static int l2cap_conn_del(struct hci_conn *conn, int err);
 
-static inline void l2cap_chan_add(struct l2cap_conn *conn, struct sock *sk, struct sock *parent);
+static void __l2cap_chan_add(struct l2cap_conn *conn, struct sock *sk, struct sock *parent);
 static void l2cap_chan_del(struct sock *sk, int err);
 static int  l2cap_chan_send(struct sock *sk, struct msghdr *msg, int len);
 
@@ -177,6 +178,14 @@
 	return 0;
 }
 
+static inline void l2cap_chan_add(struct l2cap_conn *conn, struct sock *sk, struct sock *parent)
+{
+	struct l2cap_chan_list *l = &conn->chan_list;
+	write_lock(&l->lock);
+	__l2cap_chan_add(conn, sk, parent);
+	write_unlock(&l->lock);
+}
+
 int l2cap_connect(struct sock *sk)
 {
 	bdaddr_t *src = &bluez_sk(sk)->src;
@@ -234,32 +243,26 @@
 }
 
 /* -------- Socket interface ---------- */
-static struct sock *__l2cap_get_sock_by_addr(struct sockaddr_l2 *addr)
+static struct sock *__l2cap_get_sock_by_addr(__u16 psm, bdaddr_t *src)
 {
-	bdaddr_t *src = &addr->l2_bdaddr;
-	__u16 psm = addr->l2_psm;
 	struct sock *sk;
-
 	for (sk = l2cap_sk_list.head; sk; sk = sk->next) {
 		if (l2cap_pi(sk)->psm == psm &&
-		    !bacmp(&bluez_sk(sk)->src, src))
+				!bacmp(&bluez_sk(sk)->src, src))
 			break;
 	}
-
 	return sk;
 }
 
-/* Find socket listening on psm and source bdaddr.
+/* Find socket with psm and source bdaddr.
  * Returns closest match.
  */
-static struct sock *l2cap_get_sock_listen(bdaddr_t *src, __u16 psm)
+static struct sock *__l2cap_get_sock_by_psm(int state, __u16 psm, bdaddr_t *src)
 {
 	struct sock *sk, *sk1 = NULL;
 
-	read_lock(&l2cap_sk_list.lock);
-
 	for (sk = l2cap_sk_list.head; sk; sk = sk->next) {
-		if (sk->state != BT_LISTEN)
+		if (state && sk->state != state)
 			continue;
 
 		if (l2cap_pi(sk)->psm == psm) {
@@ -272,9 +275,19 @@
 				sk1 = sk;
 		}
 	}
+	return sk ? sk : sk1;
+}
 
+/* Find socket with given address (psm, src).
+ * Returns locked socket */
+static inline struct sock *l2cap_get_sock_by_psm(int state, __u16 psm, bdaddr_t *src)
+{
+	struct sock *s;
+	read_lock(&l2cap_sk_list.lock);
+	s = __l2cap_get_sock_by_psm(state, psm, src);
+	if (s) bh_lock_sock(s);
 	read_unlock(&l2cap_sk_list.lock);
-	return sk ? sk : sk1;
+	return s;
 }
 
 static void l2cap_sock_destruct(struct sock *sk)
@@ -283,7 +296,7 @@
 
 	skb_queue_purge(&sk->receive_queue);
 	skb_queue_purge(&sk->write_queue);
-	
+
 	if (sk->protinfo)
 		kfree(sk->protinfo);
 	
@@ -320,8 +333,6 @@
 	sock_put(sk);
 }
 
-/* Close socket.
- */
 static void __l2cap_sock_close(struct sock *sk, int reason)
 {
 	BT_DBG("sk %p state %d socket %p", sk, sk->state, sk->socket);
@@ -333,6 +344,7 @@
 
 	case BT_CONNECTED:
 	case BT_CONFIG:
+	case BT_CONNECT2:
 		if (sk->type == SOCK_SEQPACKET) {
 			struct l2cap_conn *conn = l2cap_pi(sk)->conn;
 			l2cap_disconn_req req;
@@ -349,7 +361,6 @@
 		break;
 
 	case BT_CONNECT:
-	case BT_CONNECT2:
 	case BT_DISCONN:
 		l2cap_chan_del(sk, reason);
 		break;
@@ -380,7 +391,6 @@
 
 	if (parent) {
 		sk->type = parent->type;
-
 		pi->imtu = l2cap_pi(parent)->imtu;
 		pi->omtu = l2cap_pi(parent)->omtu;
 		pi->link_mode = l2cap_pi(parent)->link_mode;
@@ -405,6 +415,8 @@
 
 	sk->destruct = l2cap_sock_destruct;
 	sk->sndtimeo = L2CAP_CONN_TIMEOUT;
+
+	sk->protocol = proto;
 	sk->state    = BT_OPEN;
 
 	l2cap_sock_init_timer(sk);
@@ -421,11 +433,12 @@
 
 	BT_DBG("sock %p", sock);
 
-	if (sock->type != SOCK_SEQPACKET && sock->type != SOCK_RAW)
+	sock->state = SS_UNCONNECTED;
+
+	if (sock->type != SOCK_SEQPACKET && sock->type != SOCK_DGRAM && sock->type != SOCK_RAW)
 		return -ESOCKTNOSUPPORT;
 
-	sock->state = SS_UNCONNECTED;
-	sock->ops   = &l2cap_sock_ops;
+	sock->ops = &l2cap_sock_ops;
 
 	sk = l2cap_sock_alloc(sock, protocol, GFP_KERNEL);
 	if (!sk)
@@ -453,20 +466,16 @@
 		goto done;
 	}
 
-	write_lock(&l2cap_sk_list.lock);
-
-	if (la->l2_psm && __l2cap_get_sock_by_addr(la)) {
+	write_lock_bh(&l2cap_sk_list.lock);
+	if (la->l2_psm && __l2cap_get_sock_by_addr(la->l2_psm, &la->l2_bdaddr)) {
 		err = -EADDRINUSE;
-		goto unlock;
+	} else {
+		/* Save source address */
+		bacpy(&bluez_sk(sk)->src, &la->l2_bdaddr);
+		l2cap_pi(sk)->psm = la->l2_psm;
+		sk->state = BT_BOUND;
 	}
-
-	/* Save source address */
-	bacpy(&bluez_sk(sk)->src, &la->l2_bdaddr);
-	l2cap_pi(sk)->psm = la->l2_psm;
-	sk->state = BT_BOUND;
-
-unlock:
-	write_unlock(&l2cap_sk_list.lock);
+	write_unlock_bh(&l2cap_sk_list.lock);
 
 done:
 	release_sock(sk);
@@ -629,7 +638,6 @@
 		bacpy(&la->l2_bdaddr, &bluez_sk(sk)->src);
 
 	la->l2_psm = l2cap_pi(sk)->psm;
-
 	return 0;
 }
 
@@ -646,6 +654,10 @@
 	if (msg->msg_flags & MSG_OOB)
 		return -EOPNOTSUPP;
 
+	/* Check outgoing MTU */
+	if (len > l2cap_pi(sk)->omtu)
+		return -EINVAL;
+
 	lock_sock(sk);
 
 	if (sk->state == BT_CONNECTED)
@@ -691,7 +703,7 @@
 	default:
 		err = -ENOPROTOOPT;
 		break;
-	};
+	}
 
 	release_sock(sk);
 	return err;
@@ -743,20 +755,37 @@
 	default:
 		err = -ENOPROTOOPT;
 		break;
-	};
+	}
 
 	release_sock(sk);
 	return err;
 }
 
+static int l2cap_sock_shutdown(struct socket *sock, int how)
+{
+	struct sock *sk = sock->sk;
+
+	BT_DBG("sock %p, sk %p", sock, sk);
+
+	if (!sk) return 0;
+
+	l2cap_sock_clear_timer(sk);
+
+	lock_sock(sk);
+	sk->shutdown = SHUTDOWN_MASK;
+	__l2cap_sock_close(sk, ECONNRESET);
+	release_sock(sk);
+
+	return 0;
+}
+
 static int l2cap_sock_release(struct socket *sock)
 {
 	struct sock *sk = sock->sk;
 
 	BT_DBG("sock %p, sk %p", sock, sk);
 
-	if (!sk)
-		return 0;
+	if (!sk) return 0;
 
 	sock_orphan(sk);
 	l2cap_sock_close(sk);
@@ -767,24 +796,20 @@
 static struct sock * __l2cap_get_chan_by_dcid(struct l2cap_chan_list *l, __u16 cid)
 {
 	struct sock *s;
-
 	for (s = l->head; s; s = l2cap_pi(s)->next_c) {
 		if (l2cap_pi(s)->dcid == cid)
 			break;
 	}
-
 	return s;
 }
 
 static struct sock *__l2cap_get_chan_by_scid(struct l2cap_chan_list *l, __u16 cid)
 {
 	struct sock *s;
-
 	for (s = l->head; s; s = l2cap_pi(s)->next_c) {
 		if (l2cap_pi(s)->scid == cid)
 			break;
 	}
-
 	return s;
 }
 
@@ -850,10 +875,15 @@
 	l2cap_pi(sk)->conn = conn;
 
 	if (sk->type == SOCK_SEQPACKET) {
-		/* Alloc CID for normal socket */
+		/* Alloc CID for connection-oriented socket */
 		l2cap_pi(sk)->scid = l2cap_alloc_cid(l);
+	} else if (sk->type == SOCK_DGRAM) {
+		/* Connectionless socket */
+		l2cap_pi(sk)->scid = 0x0002;
+		l2cap_pi(sk)->dcid = 0x0002;
+		l2cap_pi(sk)->omtu = L2CAP_DEFAULT_MTU;
 	} else {
-		/* Raw socket can send only signalling messages */
+		/* Raw socket can send/recv signalling messages only */
 		l2cap_pi(sk)->scid = 0x0001;
 		l2cap_pi(sk)->dcid = 0x0001;
 		l2cap_pi(sk)->omtu = L2CAP_DEFAULT_MTU;
@@ -865,14 +895,6 @@
 		bluez_accept_enqueue(parent, sk);
 }
 
-static inline void l2cap_chan_add(struct l2cap_conn *conn, struct sock *sk, struct sock *parent)
-{
-	struct l2cap_chan_list *l = &conn->chan_list;
-	write_lock(&l->lock);
-	__l2cap_chan_add(conn, sk, parent);
-	write_unlock(&l->lock);
-}
-
 /* Delete channel. 
  * Must be called on the locked socket. */
 static void l2cap_chan_del(struct sock *sk, int err)
@@ -984,25 +1006,31 @@
 {
 	struct l2cap_conn *conn = l2cap_pi(sk)->conn;
 	struct sk_buff *skb, **frag;
-	int err, size, count, sent=0;
+	int err, hlen, count, sent=0;
 	l2cap_hdr *lh;
 
-	/* Check outgoing MTU */
-	if (len > l2cap_pi(sk)->omtu)
-		return -EINVAL;
-
 	BT_DBG("sk %p len %d", sk, len);
 
 	/* First fragment (with L2CAP header) */
-	count = MIN(conn->mtu - L2CAP_HDR_SIZE, len);
-	size  = L2CAP_HDR_SIZE + count;
-	if (!(skb = bluez_skb_send_alloc(sk, size, msg->msg_flags & MSG_DONTWAIT, &err)))
+	if (sk->type == SOCK_DGRAM)
+		hlen = L2CAP_HDR_SIZE + 2;
+	else
+		hlen = L2CAP_HDR_SIZE;
+
+	count = MIN(conn->mtu - hlen, len);
+
+	skb = bluez_skb_send_alloc(sk, hlen + count,
+			msg->msg_flags & MSG_DONTWAIT, &err);
+	if (!skb)
 		return err;
 
 	/* Create L2CAP header */
 	lh = (l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE);
-	lh->len = __cpu_to_le16(len);
 	lh->cid = __cpu_to_le16(l2cap_pi(sk)->dcid);
+	lh->len = __cpu_to_le16(len + (hlen - L2CAP_HDR_SIZE));
+
+	if (sk->type == SOCK_DGRAM)
+		put_unaligned(l2cap_pi(sk)->psm, (__u16 *) skb_put(skb, 2));
 
 	if (memcpy_fromiovec(skb_put(skb, count), msg->msg_iov, count)) {
 		err = -EFAULT;
@@ -1315,37 +1343,41 @@
 	BT_DBG("psm 0x%2.2x scid 0x%4.4x", psm, scid);
 
 	/* Check if we have socket listening on psm */
-	if (!(parent = l2cap_get_sock_listen(conn->src, psm))) {
+	parent = l2cap_get_sock_by_psm(BT_LISTEN, psm, conn->src);
+	if (!parent) {
 		result = L2CAP_CR_BAD_PSM;
-		goto resp;
+		goto sendresp;
 	}
 
-	write_lock(&list->lock);
-	bh_lock_sock(parent);
-
 	result = L2CAP_CR_NO_MEM;
 
-	/* Check if we already have channel with that dcid */
-	if (__l2cap_get_chan_by_dcid(list, scid))
-		goto unlock;
-
 	/* Check for backlog size */
 	if (parent->ack_backlog > parent->max_ack_backlog) {
 		BT_DBG("backlog full %d", parent->ack_backlog); 
-		goto unlock;
+		goto response;
 	}
 
-	if (!(sk = l2cap_sock_alloc(NULL, BTPROTO_L2CAP, GFP_ATOMIC)))
-		goto unlock;
+	sk = l2cap_sock_alloc(NULL, BTPROTO_L2CAP, GFP_ATOMIC);
+	if (!sk)
+		goto response;
 
-	l2cap_sock_init(sk, parent);
+	write_lock(&list->lock);
+
+	/* Check if we already have channel with that dcid */
+	if (__l2cap_get_chan_by_dcid(list, scid)) {
+		write_unlock(&list->lock);
+		sk->zapped = 1;
+		l2cap_sock_kill(sk);
+		goto response;
+	}
 
+	hci_conn_hold(conn->hcon);
+
+	l2cap_sock_init(sk, parent);
 	bacpy(&bluez_sk(sk)->src, conn->src);
 	bacpy(&bluez_sk(sk)->dst, conn->dst);
 	l2cap_pi(sk)->psm  = psm;
 	l2cap_pi(sk)->dcid = scid;
-	
-	hci_conn_hold(conn->hcon);
 
 	__l2cap_chan_add(conn, sk, parent);
 	dcid = l2cap_pi(sk)->scid;
@@ -1360,20 +1392,22 @@
 	
 	if (l2cap_pi(sk)->link_mode & L2CAP_LM_ENCRYPT) {
 		if (!hci_conn_encrypt(conn->hcon))
-			goto unlock;
+			goto done;
 	} else if (l2cap_pi(sk)->link_mode & L2CAP_LM_AUTH) {
 		if (!hci_conn_auth(conn->hcon))
-			goto unlock;
+			goto done;
 	}
 
 	sk->state = BT_CONFIG;
 	result = status = 0;
 
-unlock:
-	bh_unlock_sock(parent);
+done:
 	write_unlock(&list->lock);
 
-resp:
+response:
+	bh_unlock_sock(parent);
+
+sendresp:
 	rsp.scid   = __cpu_to_le16(scid);
 	rsp.dcid   = __cpu_to_le16(dcid);
 	rsp.result = __cpu_to_le16(result);
@@ -1678,10 +1712,37 @@
 	return 0;
 }
 
+static inline int l2cap_conless_channel(struct l2cap_conn *conn, __u16 psm, struct sk_buff *skb)
+{
+	struct sock *sk;
+
+	sk = l2cap_get_sock_by_psm(0, psm, conn->src);
+	if (!sk)
+		goto drop;
+
+	BT_DBG("sk %p, len %d", sk, skb->len);
+
+	if (sk->state != BT_BOUND && sk->state != BT_CONNECTED)
+		goto drop;
+
+	if (l2cap_pi(sk)->imtu < skb->len)
+		goto drop;
+
+	if (!sock_queue_rcv_skb(sk, skb))
+		goto done;
+
+drop:
+	kfree_skb(skb);
+
+done:
+	if (sk) bh_unlock_sock(sk);
+	return 0;
+}
+
 static void l2cap_recv_frame(struct l2cap_conn *conn, struct sk_buff *skb)
 {
 	l2cap_hdr *lh = (l2cap_hdr *) skb->data;
-	__u16 cid, len;
+	__u16 cid, psm, len;
 
 	skb_pull(skb, L2CAP_HDR_SIZE);
 	cid = __le16_to_cpu(lh->cid);
@@ -1689,10 +1750,21 @@
 
 	BT_DBG("len %d, cid 0x%4.4x", len, cid);
 
-	if (cid == 0x0001)
+	switch (cid) {
+	case 0x0001:
 		l2cap_sig_channel(conn, skb);
-	else	
+		break;
+
+	case 0x0002:
+		psm = get_unaligned((__u16 *) skb->data);
+		skb_pull(skb, 2);
+		l2cap_conless_channel(conn, psm, skb);
+		break;
+		
+	default:
 		l2cap_data_channel(conn, cid, skb);
+		break;
+	}
 }
 
 /* ------------ L2CAP interface with lower layer (HCI) ------------- */
@@ -1859,8 +1931,8 @@
 	BT_DBG("conn %p len %d flags 0x%x", conn, skb->len, flags);
 
 	if (flags & ACL_START) {
-		int flen, tlen, size;
-		l2cap_hdr *lh;
+		l2cap_hdr *hdr;
+		int len;
 
 		if (conn->rx_len) {
 			BT_ERR("Unexpected start frame (len %d)", skb->len);
@@ -1869,30 +1941,28 @@
 			conn->rx_len = 0;
 		}
 
-		if (skb->len < L2CAP_HDR_SIZE) {
+		if (skb->len < 2) {
 			BT_ERR("Frame is too small (len %d)", skb->len);
 			goto drop;
 		}
 
-		lh = (l2cap_hdr *)skb->data;
-		tlen = __le16_to_cpu(lh->len);
-		flen = skb->len - L2CAP_HDR_SIZE;
+		hdr = (l2cap_hdr *) skb->data;
+		len = __le16_to_cpu(hdr->len) + L2CAP_HDR_SIZE;
 
-		BT_DBG("Start: total len %d, frag len %d", tlen, flen);
+		BT_DBG("Start: total len %d, frag len %d", len, skb->len);
 
-		if (flen == tlen) {
+		if (len == skb->len) {
 			/* Complete frame received */
 			l2cap_recv_frame(conn, skb);
 			return 0;
 		}
 
 		/* Allocate skb for the complete frame (with header) */
-		size = L2CAP_HDR_SIZE + tlen;
-		if (!(conn->rx_skb = bluez_skb_alloc(size, GFP_ATOMIC)))
+		if (!(conn->rx_skb = bluez_skb_alloc(len, GFP_ATOMIC)))
 			goto drop;
 
 		memcpy(skb_put(conn->rx_skb, skb->len), skb->data, skb->len);
-		conn->rx_len = tlen - flen;
+		conn->rx_len = len - skb->len;
 	} else {
 		BT_DBG("Cont: frag len %d (expecting %d)", skb->len, conn->rx_len);
 
@@ -1932,7 +2002,7 @@
 	struct sock *sk;
 	char *ptr = buf;
 
-	write_lock(&list->lock);
+	read_lock_bh(&list->lock);
 
 	for (sk = list->head; sk; sk = sk->next) {
 		pi = l2cap_pi(sk);
@@ -1942,7 +2012,7 @@
 				pi->link_mode);
 	}
 
-	write_unlock(&list->lock);
+	read_unlock_bh(&list->lock);
 
 	ptr += sprintf(ptr, "\n");
 	return ptr - buf;
@@ -1973,38 +2043,38 @@
 }
 
 static struct proto_ops l2cap_sock_ops = {
-	.family =	PF_BLUETOOTH,
-	.release =	l2cap_sock_release,
-	.bind =		l2cap_sock_bind,
-	.connect =	l2cap_sock_connect,
-	.listen =	l2cap_sock_listen,
-	.accept =	l2cap_sock_accept,
-	.getname =	l2cap_sock_getname,
-	.sendmsg =	l2cap_sock_sendmsg,
-	.recvmsg =	bluez_sock_recvmsg,
-	.poll =		bluez_sock_poll,
-	.socketpair =	sock_no_socketpair,
-	.ioctl =	sock_no_ioctl,
-	.shutdown =	sock_no_shutdown,
-	.setsockopt =	l2cap_sock_setsockopt,
-	.getsockopt =	l2cap_sock_getsockopt,
-	.mmap =		sock_no_mmap
+	.family  =      PF_BLUETOOTH,
+	.release =      l2cap_sock_release,
+	.bind    =      l2cap_sock_bind,
+	.connect =      l2cap_sock_connect,
+	.listen  =      l2cap_sock_listen,
+	.accept  =      l2cap_sock_accept,
+	.getname =      l2cap_sock_getname,
+	.sendmsg =      l2cap_sock_sendmsg,
+	.recvmsg =      bluez_sock_recvmsg,
+	.poll    =      bluez_sock_poll,
+	.mmap    =      sock_no_mmap,
+	.socketpair =   sock_no_socketpair,
+	.ioctl      =   sock_no_ioctl,
+	.shutdown   =   l2cap_sock_shutdown,
+	.setsockopt =   l2cap_sock_setsockopt,
+	.getsockopt =   l2cap_sock_getsockopt
 };
 
 static struct net_proto_family l2cap_sock_family_ops = {
-	.family =	PF_BLUETOOTH,
-	.create =	l2cap_sock_create
+	.family =       PF_BLUETOOTH,
+	.create	=       l2cap_sock_create
 };
 
 static struct hci_proto l2cap_hci_proto = {
-	.name =		"L2CAP",
-	.id =		HCI_PROTO_L2CAP,
-	.connect_ind =	l2cap_connect_ind,
-	.connect_cfm =	l2cap_connect_cfm,
-	.disconn_ind =	l2cap_disconn_ind,
-	.recv_acldata =	l2cap_recv_acldata,
-	.auth_cfm =	l2cap_auth_cfm,
-	.encrypt_cfm =	l2cap_encrypt_cfm
+	.name =         "L2CAP",
+	.id   =         HCI_PROTO_L2CAP,
+	.connect_ind =  l2cap_connect_ind,
+	.connect_cfm =  l2cap_connect_cfm,
+	.disconn_ind =  l2cap_disconn_ind,
+	.auth_cfm    =  l2cap_auth_cfm,
+	.encrypt_cfm =  l2cap_encrypt_cfm,
+	.recv_acldata =	l2cap_recv_acldata
 };
 
 int __init l2cap_init(void)
diff -Nru a/net/bluetooth/lib.c b/net/bluetooth/lib.c
--- a/net/bluetooth/lib.c	Mon Oct  7 11:25:25 2002
+++ b/net/bluetooth/lib.c	Mon Oct  7 11:25:25 2002
@@ -105,7 +105,7 @@
 		return EACCES;
 
 	case 0x06:
-		return EINVAL;
+		return EBADE;
 
 	case 0x07:
 		return ENOMEM;
diff -Nru a/net/bluetooth/rfcomm/Config.help b/net/bluetooth/rfcomm/Config.help
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/net/bluetooth/rfcomm/Config.help	Mon Oct  7 11:26:06 2002
@@ -0,0 +1,12 @@
+RFCOMM protocol support
+CONFIG_BLUEZ_RFCOMM
+  RFCOMM provides connection oriented stream transport. RFCOMM
+  support is required for Dialup Networking, OBEX and other Bluetooth
+  applications.
+
+  Say Y here to compile RFCOMM support into the kernel or say M to
+  compile it as module (rfcomm.o).
+
+RFCOMM TTY emulation support
+CONFIG_BLUEZ_RFCOMM_TTY
+  This option enables TTY emulation support for RFCOMM channels.
diff -Nru a/net/bluetooth/rfcomm/Config.in b/net/bluetooth/rfcomm/Config.in
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/net/bluetooth/rfcomm/Config.in	Mon Oct  7 11:26:06 2002
@@ -0,0 +1,7 @@
+
+dep_tristate 'RFCOMM protocol support' CONFIG_BLUEZ_RFCOMM $CONFIG_BLUEZ_L2CAP
+
+if [ "$CONFIG_BLUEZ_RFCOMM" != "n" ]; then
+   bool '  RFCOMM TTY support' CONFIG_BLUEZ_RFCOMM_TTY
+fi
+
diff -Nru a/net/bluetooth/rfcomm/Makefile b/net/bluetooth/rfcomm/Makefile
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/net/bluetooth/rfcomm/Makefile	Mon Oct  7 11:26:06 2002
@@ -0,0 +1,11 @@
+#
+# Makefile for the Linux Bluetooth RFCOMM layer.
+#
+
+obj-$(CONFIG_BLUEZ_RFCOMM) += rfcomm.o
+
+rfcomm-y				:= core.o sock.o crc.o
+rfcomm-$(CONFIG_BLUEZ_RFCOMM_TTY)	+= tty.o
+rfcomm-objs				:= $(rfcomm-y)
+
+include $(TOPDIR)/Rules.make
diff -Nru a/net/bluetooth/rfcomm/core.c b/net/bluetooth/rfcomm/core.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/net/bluetooth/rfcomm/core.c	Mon Oct  7 11:25:40 2002
@@ -0,0 +1,1717 @@
+/* 
+   RFCOMM implementation for Linux Bluetooth stack (BlueZ).
+   Copyright (C) 2002 Maxim Krasnyansky <maxk@qualcomm.com>
+   Copyright (C) 2002 Marcel Holtmann <marcel@holtmann.org>
+
+   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;
+
+   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+   IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
+   CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES 
+   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 
+   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 
+   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+   ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, 
+   COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS 
+   SOFTWARE IS DISCLAIMED.
+*/
+
+/* 
+   RPN support    -    Dirk Husemann <hud@zurich.ibm.com>
+*/
+
+/*
+ * RFCOMM core.
+ *
+ * $Id: core.c,v 1.42 2002/10/01 23:26:25 maxk Exp $
+ */
+
+#define __KERNEL_SYSCALLS__
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/signal.h>
+#include <linux/init.h>
+#include <linux/wait.h>
+#include <linux/net.h>
+#include <net/sock.h>
+#include <asm/uaccess.h>
+#include <asm/unaligned.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/l2cap.h>
+#include <net/bluetooth/rfcomm.h>
+
+#define VERSION "0.3"
+
+#ifndef CONFIG_BLUEZ_RFCOMM_DEBUG
+#undef  BT_DBG
+#define BT_DBG(D...)
+#endif
+
+struct task_struct *rfcomm_thread;
+DECLARE_MUTEX(rfcomm_sem);
+unsigned long rfcomm_event;
+
+static LIST_HEAD(session_list);
+static atomic_t terminate, running;
+
+static int rfcomm_send_frame(struct rfcomm_session *s, u8 *data, int len);
+static int rfcomm_send_sabm(struct rfcomm_session *s, u8 dlci);
+static int rfcomm_send_disc(struct rfcomm_session *s, u8 dlci);
+static int rfcomm_queue_disc(struct rfcomm_dlc *d);
+static int rfcomm_send_nsc(struct rfcomm_session *s, int cr, u8 type);
+static int rfcomm_send_pn(struct rfcomm_session *s, int cr, struct rfcomm_dlc *d);
+static int rfcomm_send_msc(struct rfcomm_session *s, int cr, u8 dlci, u8 v24_sig);
+static int rfcomm_send_test(struct rfcomm_session *s, int cr, u8 *pattern, int len);
+static int rfcomm_send_credits(struct rfcomm_session *s, u8 addr, u8 credits);
+static void rfcomm_make_uih(struct sk_buff *skb, u8 addr);
+
+static void rfcomm_process_connect(struct rfcomm_session *s);
+
+/* ---- RFCOMM frame parsing macros ---- */
+#define __get_dlci(b)     ((b & 0xfc) >> 2)
+#define __get_channel(b)  ((b & 0xf8) >> 3)
+#define __get_dir(b)      ((b & 0x04) >> 2)
+#define __get_type(b)     ((b & 0xef))
+
+#define __test_ea(b)      ((b & 0x01))
+#define __test_cr(b)      ((b & 0x02))
+#define __test_pf(b)      ((b & 0x10))
+
+#define __addr(cr, dlci)       (((dlci & 0x3f) << 2) | (cr << 1) | 0x01)
+#define __ctrl(type, pf)       (((type & 0xef) | (pf << 4)))
+#define __dlci(dir, chn)       (((chn & 0x1f) << 1) | dir)
+#define __srv_channel(dlci)    (dlci >> 1)
+#define __dir(dlci)            (dlci & 0x01)
+
+#define __len8(len)       (((len) << 1) | 1)
+#define __len16(len)      ((len) << 1)
+
+/* MCC macros */
+#define __mcc_type(cr, type)   (((type << 2) | (cr << 1) | 0x01))
+#define __get_mcc_type(b) ((b & 0xfc) >> 2)
+#define __get_mcc_len(b)  ((b & 0xfe) >> 1)
+
+/* RPN macros */
+#define __rpn_line_settings(data, stop, parity)  ((data & 0x3) | ((stop & 0x1) << 2) | ((parity & 0x3) << 3))
+#define __get_rpn_data_bits(line) ((line) & 0x3)
+#define __get_rpn_stop_bits(line) (((line) >> 2) & 0x1)
+#define __get_rpn_parity(line)    (((line) >> 3) & 0x3)
+
+/* ---- RFCOMM FCS computation ---- */
+
+/* CRC on 2 bytes */
+#define __crc(data) (rfcomm_crc_table[rfcomm_crc_table[0xff ^ data[0]] ^ data[1]])
+
+/* FCS on 2 bytes */ 
+static inline u8 __fcs(u8 *data)
+{
+	return (0xff - __crc(data));
+}
+
+/* FCS on 3 bytes */ 
+static inline u8 __fcs2(u8 *data)
+{
+	return (0xff - rfcomm_crc_table[__crc(data) ^ data[2]]);
+}
+
+/* Check FCS */
+static inline int __check_fcs(u8 *data, int type, u8 fcs)
+{
+	u8 f = __crc(data);
+
+	if (type != RFCOMM_UIH)
+		f = rfcomm_crc_table[f ^ data[2]];
+
+	return rfcomm_crc_table[f ^ fcs] != 0xcf;
+}
+
+/* ---- L2CAP callbacks ---- */
+static void rfcomm_l2state_change(struct sock *sk)
+{
+	BT_DBG("%p state %d", sk, sk->state);
+	rfcomm_schedule(RFCOMM_SCHED_STATE);
+}
+
+static void rfcomm_l2data_ready(struct sock *sk, int bytes)
+{
+	BT_DBG("%p bytes %d", sk, bytes);
+	rfcomm_schedule(RFCOMM_SCHED_RX);
+}
+
+static int rfcomm_l2sock_create(struct socket **sock)
+{
+	int err;
+
+	BT_DBG("");
+
+	err = sock_create(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP, sock);
+	if (!err) {
+		struct sock *sk = (*sock)->sk;
+		sk->data_ready   = rfcomm_l2data_ready;
+		sk->state_change = rfcomm_l2state_change;
+	}
+	return err;
+}
+
+/* ---- RFCOMM DLCs ---- */
+static void rfcomm_dlc_timeout(unsigned long arg)
+{
+	struct rfcomm_dlc *d = (void *) arg;
+
+	BT_DBG("dlc %p state %ld", d, d->state);
+
+	set_bit(RFCOMM_TIMED_OUT, &d->flags);
+	rfcomm_dlc_put(d);
+	rfcomm_schedule(RFCOMM_SCHED_TIMEO);
+}
+
+static void rfcomm_dlc_set_timer(struct rfcomm_dlc *d, long timeout)
+{
+	BT_DBG("dlc %p state %ld timeout %ld", d, d->state, timeout);
+
+	if (!mod_timer(&d->timer, jiffies + timeout))
+		rfcomm_dlc_hold(d);
+}
+
+static void rfcomm_dlc_clear_timer(struct rfcomm_dlc *d)
+{
+	BT_DBG("dlc %p state %ld", d, d->state);
+
+	if (timer_pending(&d->timer) && del_timer(&d->timer))
+		rfcomm_dlc_put(d);
+}
+
+static void rfcomm_dlc_clear_state(struct rfcomm_dlc *d)
+{
+	BT_DBG("%p", d);
+
+	d->state      = BT_OPEN;
+	d->flags      = 0;
+	d->mtu        = RFCOMM_DEFAULT_MTU;
+	d->v24_sig    = RFCOMM_V24_RTC | RFCOMM_V24_RTR | RFCOMM_V24_DV;
+
+	d->credits    = RFCOMM_MAX_CREDITS;
+	d->rx_credits = RFCOMM_DEFAULT_CREDITS;
+}
+
+struct rfcomm_dlc *rfcomm_dlc_alloc(int prio)
+{
+	struct rfcomm_dlc *d = kmalloc(sizeof(*d), prio);
+	if (!d)
+		return NULL;
+	memset(d, 0, sizeof(*d));
+
+	init_timer(&d->timer);
+	d->timer.function = rfcomm_dlc_timeout;
+	d->timer.data = (unsigned long) d;
+
+	skb_queue_head_init(&d->tx_queue);
+	spin_lock_init(&d->lock);
+	atomic_set(&d->refcnt, 1);
+
+	rfcomm_dlc_clear_state(d);
+	
+	BT_DBG("%p", d);
+	return d;
+}
+
+void rfcomm_dlc_free(struct rfcomm_dlc *d)
+{
+	BT_DBG("%p", d);
+
+	skb_queue_purge(&d->tx_queue);
+	kfree(d);
+}
+
+static void rfcomm_dlc_link(struct rfcomm_session *s, struct rfcomm_dlc *d)
+{
+	BT_DBG("dlc %p session %p", d, s);
+
+	rfcomm_session_hold(s);
+
+	rfcomm_dlc_hold(d);
+	list_add(&d->list, &s->dlcs);
+	d->session = s;
+}
+
+static void rfcomm_dlc_unlink(struct rfcomm_dlc *d)
+{
+	struct rfcomm_session *s = d->session;
+
+	BT_DBG("dlc %p refcnt %d session %p", d, atomic_read(&d->refcnt), s);
+
+	list_del(&d->list);
+	d->session = NULL;
+	rfcomm_dlc_put(d);
+
+	rfcomm_session_put(s);
+}
+
+static struct rfcomm_dlc *rfcomm_dlc_get(struct rfcomm_session *s, int dlci)
+{
+	struct rfcomm_dlc *d;
+	struct list_head *p;
+
+	list_for_each(p, &s->dlcs) {
+		d = list_entry(p, struct rfcomm_dlc, list);
+		if (d->dlci == dlci)
+			return d;
+	}
+	return NULL;
+}
+
+static int __rfcomm_dlc_open(struct rfcomm_dlc *d, bdaddr_t *src, bdaddr_t *dst, u8 channel)
+{
+	struct rfcomm_session *s;
+	int err = 0, dlci = __dlci(0, channel);
+
+	BT_DBG("dlc %p state %ld %s %s channel %d dlci %d", 
+			d, d->state, batostr(src), batostr(dst), channel, dlci);
+
+	if (dlci < 1 || dlci > 62)
+		return -EINVAL;
+
+	if (d->state != BT_OPEN && d->state != BT_CLOSED)
+		return 0;
+
+	s = rfcomm_session_get(src, dst);
+	if (!s) {
+		s = rfcomm_session_create(src, dst, &err);
+		if (!s)
+			return err;
+	}
+
+	/* Check if DLCI already exists */
+	if (rfcomm_dlc_get(s, dlci))
+		return -EBUSY;
+
+	rfcomm_dlc_clear_state(d);
+
+	d->dlci    = dlci;
+	d->addr    = __addr(s->initiator, dlci);
+
+	d->state   = BT_CONFIG;
+	rfcomm_dlc_link(s, d);
+
+	d->mtu     = s->mtu;
+	d->credits = s->credits;
+
+	if (s->state == BT_CONNECTED)
+		rfcomm_send_pn(s, 1, d);
+	rfcomm_dlc_set_timer(d, RFCOMM_CONN_TIMEOUT);
+	return 0;
+}
+
+int rfcomm_dlc_open(struct rfcomm_dlc *d, bdaddr_t *src, bdaddr_t *dst, u8 channel)
+{
+	mm_segment_t fs;
+	int r;
+
+	rfcomm_lock();
+
+	fs = get_fs(); set_fs(KERNEL_DS);
+	r = __rfcomm_dlc_open(d, src, dst, channel);
+	set_fs(fs);
+
+	rfcomm_unlock();
+	return r;
+}
+
+static int __rfcomm_dlc_close(struct rfcomm_dlc *d, int err)
+{
+	struct rfcomm_session *s = d->session;
+	if (!s)
+		return 0;
+
+	BT_DBG("dlc %p state %ld dlci %d err %d session %p",
+			d, d->state, d->dlci, err, s);
+
+	switch (d->state) {
+	case BT_CONNECTED:
+	case BT_CONFIG:
+	case BT_CONNECT:
+		d->state = BT_DISCONN;
+		if (skb_queue_empty(&d->tx_queue)) {
+			rfcomm_send_disc(s, d->dlci);
+			rfcomm_dlc_set_timer(d, RFCOMM_DISC_TIMEOUT);
+		} else {
+			rfcomm_queue_disc(d);
+			rfcomm_dlc_set_timer(d, RFCOMM_DISC_TIMEOUT * 2);
+		}
+		break;
+
+	default:
+		rfcomm_dlc_clear_timer(d);
+
+		rfcomm_dlc_lock(d);
+		d->state = BT_CLOSED;
+		d->state_change(d, err);
+		rfcomm_dlc_unlock(d);
+
+		skb_queue_purge(&d->tx_queue);
+		rfcomm_dlc_unlink(d);
+	}
+
+	return 0;
+}
+
+int rfcomm_dlc_close(struct rfcomm_dlc *d, int err)
+{
+	mm_segment_t fs;
+	int r;
+
+	rfcomm_lock();
+
+	fs = get_fs(); set_fs(KERNEL_DS);
+	r = __rfcomm_dlc_close(d, err);
+	set_fs(fs);
+
+	rfcomm_unlock();
+	return r;
+}
+
+int rfcomm_dlc_send(struct rfcomm_dlc *d, struct sk_buff *skb)
+{
+	int len = skb->len;
+
+	if (d->state != BT_CONNECTED)
+		return -ENOTCONN;
+
+	BT_DBG("dlc %p mtu %d len %d", d, d->mtu, len);
+
+	if (len > d->mtu)
+		return -EINVAL;
+
+	rfcomm_make_uih(skb, d->addr);
+	skb_queue_tail(&d->tx_queue, skb);
+
+	if (!test_bit(RFCOMM_TX_THROTTLED, &d->flags))
+		rfcomm_schedule(RFCOMM_SCHED_TX);
+	return len;
+}
+
+void __rfcomm_dlc_throttle(struct rfcomm_dlc *d)
+{
+	BT_DBG("dlc %p state %ld", d, d->state);
+
+	if (!d->credits) {
+		d->v24_sig |= RFCOMM_V24_FC;
+		set_bit(RFCOMM_MSC_PENDING, &d->flags);
+	}
+	rfcomm_schedule(RFCOMM_SCHED_TX);
+}
+
+void __rfcomm_dlc_unthrottle(struct rfcomm_dlc *d)
+{
+	BT_DBG("dlc %p state %ld", d, d->state);
+
+	if (!d->credits) {
+		d->v24_sig &= ~RFCOMM_V24_FC;
+		set_bit(RFCOMM_MSC_PENDING, &d->flags);
+	}
+	rfcomm_schedule(RFCOMM_SCHED_TX);
+}
+
+int rfcomm_dlc_modem_status(struct rfcomm_dlc *d, int v24_sig)
+{
+	BT_DBG("dlc %p state %ld v24_sig 0x%x", 
+			d, d->state, v24_sig);
+
+	if (test_bit(RFCOMM_RX_THROTTLED, &d->flags))
+		v24_sig |= RFCOMM_V24_FC;
+	else
+		v24_sig &= ~RFCOMM_V24_FC;
+	
+	d->v24_sig = v24_sig;
+
+	if (!test_and_set_bit(RFCOMM_MSC_PENDING, &d->flags))
+		rfcomm_schedule(RFCOMM_SCHED_TX);
+
+	return 0;
+}
+
+/* ---- RFCOMM sessions ---- */
+struct rfcomm_session *rfcomm_session_add(struct socket *sock, int state)
+{
+	struct rfcomm_session *s = kmalloc(sizeof(*s), GFP_KERNEL);
+	if (!s)
+		return NULL;
+	memset(s, 0, sizeof(*s));
+	
+	BT_DBG("session %p sock %p", s, sock);
+
+	INIT_LIST_HEAD(&s->dlcs);
+	s->state = state;
+	s->sock  = sock;
+
+	s->mtu     = RFCOMM_DEFAULT_MTU;
+	s->credits = RFCOMM_MAX_CREDITS;
+	
+	list_add(&s->list, &session_list);
+
+	/* Do not increment module usage count for listeting sessions.
+	 * Otherwise we won't be able to unload the module. */
+	if (state != BT_LISTEN)
+		MOD_INC_USE_COUNT;
+	return s;
+}
+
+void rfcomm_session_del(struct rfcomm_session *s)
+{
+	int state = s->state;
+	
+	BT_DBG("session %p state %ld", s, s->state);
+
+	list_del(&s->list);
+
+	if (state == BT_CONNECTED)
+		rfcomm_send_disc(s, 0);
+
+	sock_release(s->sock);
+	kfree(s);
+
+	if (state != BT_LISTEN)
+		MOD_DEC_USE_COUNT;
+}
+
+struct rfcomm_session *rfcomm_session_get(bdaddr_t *src, bdaddr_t *dst)
+{
+	struct rfcomm_session *s;
+	struct list_head *p, *n;
+	struct bluez_sock *sk;
+	list_for_each_safe(p, n, &session_list) {
+		s = list_entry(p, struct rfcomm_session, list);
+		sk = bluez_sk(s->sock->sk); 
+
+		if ((!bacmp(src, BDADDR_ANY) || !bacmp(&sk->src, src)) &&
+				!bacmp(&sk->dst, dst))
+			return s;
+	}
+	return NULL;
+}
+
+void rfcomm_session_close(struct rfcomm_session *s, int err)
+{
+	struct rfcomm_dlc *d;
+	struct list_head *p, *n;
+
+	BT_DBG("session %p state %ld err %d", s, s->state, err);
+
+	rfcomm_session_hold(s);
+
+	s->state = BT_CLOSED;
+
+	/* Close all dlcs */
+	list_for_each_safe(p, n, &s->dlcs) {
+		d = list_entry(p, struct rfcomm_dlc, list);
+		d->state = BT_CLOSED;
+		__rfcomm_dlc_close(d, err);
+	}
+
+	rfcomm_session_put(s);
+}
+
+struct rfcomm_session *rfcomm_session_create(bdaddr_t *src, bdaddr_t *dst, int *err)
+{
+	struct rfcomm_session *s = NULL;
+	struct sockaddr_l2 addr;
+	struct l2cap_options opts;
+	struct socket *sock;
+	int    size;
+
+	BT_DBG("%s %s", batostr(src), batostr(dst));
+
+	*err = rfcomm_l2sock_create(&sock);
+	if (*err < 0)
+		return NULL;
+
+	bacpy(&addr.l2_bdaddr, src);
+	addr.l2_family = AF_BLUETOOTH;
+	addr.l2_psm    = 0;
+	*err = sock->ops->bind(sock, (struct sockaddr *) &addr, sizeof(addr));
+	if (*err < 0)
+		goto failed;
+
+	/* Set L2CAP options */
+	size = sizeof(opts);
+	sock->ops->getsockopt(sock, SOL_L2CAP, L2CAP_OPTIONS, (void *)&opts, &size);
+	
+	opts.imtu = RFCOMM_MAX_L2CAP_MTU;
+	sock->ops->setsockopt(sock, SOL_L2CAP, L2CAP_OPTIONS, (void *)&opts, size);
+
+	s = rfcomm_session_add(sock, BT_BOUND);
+	if (!s) {
+		*err = -ENOMEM;
+		goto failed;
+	}
+
+	s->initiator = 1;
+
+	bacpy(&addr.l2_bdaddr, dst);
+	addr.l2_family = AF_BLUETOOTH;
+	addr.l2_psm    = htobs(RFCOMM_PSM);
+	*err = sock->ops->connect(sock, (struct sockaddr *) &addr, sizeof(addr), O_NONBLOCK);
+	if (*err == 0 || *err == -EAGAIN)
+		return s;
+
+	rfcomm_session_del(s);
+	return NULL;
+
+failed:
+	sock_release(sock);
+	return NULL;
+}
+
+/* ---- RFCOMM frame sending ---- */
+static int rfcomm_send_frame(struct rfcomm_session *s, u8 *data, int len)
+{
+	struct socket *sock = s->sock;
+	struct iovec iv = { data, len };
+	struct msghdr msg;
+	int err;
+
+	BT_DBG("session %p len %d", s, len);
+
+	memset(&msg, 0, sizeof(msg));
+	msg.msg_iovlen = 1;
+	msg.msg_iov = &iv;
+
+	err = sock->ops->sendmsg(sock, &msg, len, 0);
+	return err;
+}
+
+static int rfcomm_send_sabm(struct rfcomm_session *s, u8 dlci)
+{
+	struct rfcomm_cmd cmd;
+
+	BT_DBG("%p dlci %d", s, dlci);
+
+	cmd.addr = __addr(s->initiator, dlci);
+	cmd.ctrl = __ctrl(RFCOMM_SABM, 1);
+	cmd.len  = __len8(0);
+	cmd.fcs  = __fcs2((u8 *) &cmd);
+
+	return rfcomm_send_frame(s, (void *) &cmd, sizeof(cmd));
+}
+
+static int rfcomm_send_ua(struct rfcomm_session *s, u8 dlci)
+{
+	struct rfcomm_cmd cmd;
+
+	BT_DBG("%p dlci %d", s, dlci);
+
+	cmd.addr = __addr(!s->initiator, dlci);
+	cmd.ctrl = __ctrl(RFCOMM_UA, 1);
+	cmd.len  = __len8(0);
+	cmd.fcs  = __fcs2((u8 *) &cmd);
+
+	return rfcomm_send_frame(s, (void *) &cmd, sizeof(cmd));
+}
+
+static int rfcomm_send_disc(struct rfcomm_session *s, u8 dlci)
+{
+	struct rfcomm_cmd cmd;
+
+	BT_DBG("%p dlci %d", s, dlci);
+
+	cmd.addr = __addr(s->initiator, dlci);
+	cmd.ctrl = __ctrl(RFCOMM_DISC, 1);
+	cmd.len  = __len8(0);
+	cmd.fcs  = __fcs2((u8 *) &cmd);
+
+	return rfcomm_send_frame(s, (void *) &cmd, sizeof(cmd));
+}
+
+static int rfcomm_queue_disc(struct rfcomm_dlc *d)
+{
+	struct rfcomm_cmd *cmd;
+	struct sk_buff *skb;
+
+	BT_DBG("dlc %p dlci %d", d, d->dlci);
+
+	skb = alloc_skb(sizeof(*cmd), GFP_KERNEL);
+	if (!skb)
+		return -ENOMEM;
+
+	cmd = (void *) __skb_put(skb, sizeof(*cmd));
+	cmd->addr = d->addr;
+	cmd->ctrl = __ctrl(RFCOMM_DISC, 1);
+	cmd->len  = __len8(0);
+	cmd->fcs  = __fcs2((u8 *) cmd);
+
+	skb_queue_tail(&d->tx_queue, skb);
+	rfcomm_schedule(RFCOMM_SCHED_TX);
+	return 0;
+}
+
+static int rfcomm_send_dm(struct rfcomm_session *s, u8 dlci)
+{
+	struct rfcomm_cmd cmd;
+
+	BT_DBG("%p dlci %d", s, dlci);
+
+	cmd.addr = __addr(!s->initiator, dlci);
+	cmd.ctrl = __ctrl(RFCOMM_DM, 1);
+	cmd.len  = __len8(0);
+	cmd.fcs  = __fcs2((u8 *) &cmd);
+
+	return rfcomm_send_frame(s, (void *) &cmd, sizeof(cmd));
+}
+
+static int rfcomm_send_nsc(struct rfcomm_session *s, int cr, u8 type)
+{
+	struct rfcomm_hdr *hdr;
+	struct rfcomm_mcc *mcc;
+	u8 buf[16], *ptr = buf;
+
+	BT_DBG("%p cr %d type %d", s, cr, type);
+
+	hdr = (void *) ptr; ptr += sizeof(*hdr);
+	hdr->addr = __addr(s->initiator, 0);
+	hdr->ctrl = __ctrl(RFCOMM_UIH, 0);
+	hdr->len  = __len8(sizeof(*mcc) + 1);
+
+	mcc = (void *) ptr; ptr += sizeof(*mcc);
+	mcc->type = __mcc_type(s->initiator, RFCOMM_NSC);
+	mcc->len  = __len8(1);
+
+	/* Type that we didn't like */
+	*ptr = __mcc_type(cr, type); ptr++;
+
+	*ptr = __fcs(buf); ptr++;
+
+	return rfcomm_send_frame(s, buf, ptr - buf);
+}
+
+static int rfcomm_send_pn(struct rfcomm_session *s, int cr, struct rfcomm_dlc *d)
+{
+	struct rfcomm_hdr *hdr;
+	struct rfcomm_mcc *mcc;
+	struct rfcomm_pn  *pn;
+	u8 buf[16], *ptr = buf;
+
+	BT_DBG("%p cr %d dlci %d mtu %d", s, cr, d->dlci, d->mtu);
+
+	hdr = (void *) ptr; ptr += sizeof(*hdr);
+	hdr->addr = __addr(s->initiator, 0);
+	hdr->ctrl = __ctrl(RFCOMM_UIH, 0);
+	hdr->len  = __len8(sizeof(*mcc) + sizeof(*pn));
+
+	mcc = (void *) ptr; ptr += sizeof(*mcc);
+	mcc->type = __mcc_type(s->initiator, RFCOMM_PN);
+	mcc->len  = __len8(sizeof(*pn));
+
+	pn = (void *) ptr; ptr += sizeof(*pn);
+	pn->dlci        = d->dlci;
+	pn->priority    = 0;
+	pn->ack_timer   = 0;
+	pn->max_retrans = 0;
+
+	if (d->credits) {
+		pn->flow_ctrl = cr ? 0xf0 : 0xe0;
+		pn->credits = RFCOMM_DEFAULT_CREDITS;
+	} else {
+		pn->flow_ctrl = 0;
+		pn->credits   = 0;
+	}
+
+	pn->mtu = htobs(d->mtu);
+
+	*ptr = __fcs(buf); ptr++;
+
+	return rfcomm_send_frame(s, buf, ptr - buf);
+}
+
+static int rfcomm_send_rpn(struct rfcomm_session *s, int cr, u8 dlci,
+			   u8 bit_rate, u8 data_bits, u8 stop_bits,
+			   u8 parity, u8 flow_ctrl_settings, 
+			   u8 xon_char, u8 xoff_char, u16 param_mask)
+{
+	struct rfcomm_hdr *hdr;
+	struct rfcomm_mcc *mcc;
+	struct rfcomm_rpn *rpn;
+	u8 buf[16], *ptr = buf;
+
+	BT_DBG("%p cr %d dlci %d bit_r 0x%x data_b 0x%x stop_b 0x%x parity 0x%x"
+	       "flwc_s 0x%x xon_c 0x%x xoff_c 0x%x p_mask 0x%x", 
+			s, cr, dlci, bit_rate, data_bits, stop_bits, parity, 
+			flow_ctrl_settings, xon_char, xoff_char, param_mask);
+
+	hdr = (void *) ptr; ptr += sizeof(*hdr);
+	hdr->addr = __addr(s->initiator, 0);
+	hdr->ctrl = __ctrl(RFCOMM_UIH, 0);
+	hdr->len  = __len8(sizeof(*mcc) + sizeof(*rpn));
+
+	mcc = (void *) ptr; ptr += sizeof(*mcc);
+	mcc->type = __mcc_type(cr, RFCOMM_RPN);
+	mcc->len  = __len8(sizeof(*rpn));
+
+	rpn = (void *) ptr; ptr += sizeof(*rpn);
+	rpn->dlci          = __addr(1, dlci);
+	rpn->bit_rate      = bit_rate;
+	rpn->line_settings = __rpn_line_settings(data_bits, stop_bits, parity);
+	rpn->flow_ctrl     = flow_ctrl_settings;
+	rpn->xon_char      = xon_char;
+	rpn->xoff_char     = xoff_char;
+	rpn->param_mask    = param_mask;
+
+	*ptr = __fcs(buf); ptr++;
+
+	return rfcomm_send_frame(s, buf, ptr - buf);
+}
+
+static int rfcomm_send_msc(struct rfcomm_session *s, int cr, u8 dlci, u8 v24_sig)
+{
+	struct rfcomm_hdr *hdr;
+	struct rfcomm_mcc *mcc;
+	struct rfcomm_msc *msc;
+	u8 buf[16], *ptr = buf;
+
+	BT_DBG("%p cr %d v24 0x%x", s, cr, v24_sig);
+
+	hdr = (void *) ptr; ptr += sizeof(*hdr);
+	hdr->addr = __addr(s->initiator, 0);
+	hdr->ctrl = __ctrl(RFCOMM_UIH, 0);
+	hdr->len  = __len8(sizeof(*mcc) + sizeof(*msc));
+
+	mcc = (void *) ptr; ptr += sizeof(*mcc);
+	mcc->type = __mcc_type(cr, RFCOMM_MSC);
+	mcc->len  = __len8(sizeof(*msc));
+
+	msc = (void *) ptr; ptr += sizeof(*msc);
+	msc->dlci    = __addr(1, dlci);
+	msc->v24_sig = v24_sig;
+
+	*ptr = __fcs(buf); ptr++;
+
+	return rfcomm_send_frame(s, buf, ptr - buf);
+}
+
+static int rfcomm_send_test(struct rfcomm_session *s, int cr, u8 *pattern, int len)
+{
+	struct socket *sock = s->sock;
+	struct iovec iv[3];
+	struct msghdr msg;
+	unsigned char hdr[5], crc[1];
+
+	if (len > 125)
+		return -EINVAL;
+
+	BT_DBG("%p cr %d", s, cr);
+
+	hdr[0] = __addr(s->initiator, 0);
+	hdr[1] = __ctrl(RFCOMM_UIH, 0);
+	hdr[2] = 0x01 | ((len + 2) << 1);
+	hdr[3] = 0x01 | ((cr & 0x01) << 1) | (RFCOMM_TEST << 2);
+	hdr[4] = 0x01 | (len << 1);
+
+	crc[0] = __fcs(hdr);
+
+	iv[0].iov_base = hdr;
+	iv[0].iov_len  = 5;
+	iv[1].iov_base = pattern;
+	iv[1].iov_len  = len;
+	iv[2].iov_base = crc;
+	iv[2].iov_len  = 1;
+
+	memset(&msg, 0, sizeof(msg));
+	msg.msg_iovlen = 3;
+	msg.msg_iov = iv;
+	return sock->ops->sendmsg(sock, &msg, 6 + len, 0);
+}
+
+static int rfcomm_send_credits(struct rfcomm_session *s, u8 addr, u8 credits)
+{
+	struct rfcomm_hdr *hdr;
+	u8 buf[16], *ptr = buf;
+
+	BT_DBG("%p addr %d credits %d", s, addr, credits);
+
+	hdr = (void *) ptr; ptr += sizeof(*hdr);
+	hdr->addr = addr;
+	hdr->ctrl = __ctrl(RFCOMM_UIH, 1);
+	hdr->len  = __len8(0);
+
+	*ptr = credits; ptr++;
+
+	*ptr = __fcs(buf); ptr++;
+
+	return rfcomm_send_frame(s, buf, ptr - buf);
+}
+
+static void rfcomm_make_uih(struct sk_buff *skb, u8 addr)
+{
+	struct rfcomm_hdr *hdr;
+	int len = skb->len;
+	u8 *crc;
+
+	if (len > 127) {
+		hdr = (void *) skb_push(skb, 4);
+		put_unaligned(htobs(__len16(len)), (u16 *) &hdr->len);
+	} else {
+		hdr = (void *) skb_push(skb, 3);
+		hdr->len = __len8(len);
+	}
+	hdr->addr = addr;
+	hdr->ctrl = __ctrl(RFCOMM_UIH, 0);
+
+	crc = skb_put(skb, 1);
+	*crc = __fcs((void *) hdr);
+}
+
+/* ---- RFCOMM frame reception ---- */
+static int rfcomm_recv_ua(struct rfcomm_session *s, int dlci)
+{
+	BT_DBG("session %p state %ld dlci %d", s, s->state, dlci);
+
+	if (dlci) {
+		/* Data channel */
+		struct rfcomm_dlc *d = rfcomm_dlc_get(s, dlci);
+		if (!d) {
+			rfcomm_send_dm(s, dlci);
+			return 0;
+		}
+
+		switch (d->state) {
+		case BT_CONNECT:
+			rfcomm_dlc_clear_timer(d);
+
+			rfcomm_dlc_lock(d);
+			d->state = BT_CONNECTED;
+			d->state_change(d, 0);
+			rfcomm_dlc_unlock(d);
+
+			rfcomm_send_msc(s, 1, dlci, d->v24_sig);
+			break;
+
+		case BT_DISCONN:
+			d->state = BT_CLOSED;
+			__rfcomm_dlc_close(d, 0);
+			break;
+		}
+	} else {
+		/* Control channel */
+		switch (s->state) {
+		case BT_CONNECT:
+			s->state = BT_CONNECTED;
+			rfcomm_process_connect(s);
+			break;
+		}
+	}
+	return 0;
+}
+
+static int rfcomm_recv_dm(struct rfcomm_session *s, int dlci)
+{
+	int err = 0;
+
+	BT_DBG("session %p state %ld dlci %d", s, s->state, dlci);
+
+	if (dlci) {
+		/* Data DLC */
+		struct rfcomm_dlc *d = rfcomm_dlc_get(s, dlci);
+		if (d) {
+			if (d->state == BT_CONNECT || d->state == BT_CONFIG)
+				err = ECONNREFUSED;
+			else
+				err = ECONNRESET;
+
+			d->state = BT_CLOSED;
+			__rfcomm_dlc_close(d, err);
+		}
+	} else {
+		if (s->state == BT_CONNECT)
+			err = ECONNREFUSED;
+		else
+			err = ECONNRESET;
+
+		s->state = BT_CLOSED;
+		rfcomm_session_close(s, err);
+	}
+	return 0;
+}
+
+static int rfcomm_recv_disc(struct rfcomm_session *s, int dlci)
+{
+	int err = 0;
+
+	BT_DBG("session %p state %ld dlci %d", s, s->state, dlci);
+
+	if (dlci) {
+		struct rfcomm_dlc *d = rfcomm_dlc_get(s, dlci);
+		if (d) {
+			rfcomm_send_ua(s, dlci);
+
+			if (d->state == BT_CONNECT || d->state == BT_CONFIG)
+				err = ECONNREFUSED;
+			else
+				err = ECONNRESET;
+
+			d->state = BT_CLOSED;
+			__rfcomm_dlc_close(d, err);
+		} else 
+			rfcomm_send_dm(s, dlci);
+			
+	} else {
+		rfcomm_send_ua(s, 0);
+
+		if (s->state == BT_CONNECT)
+			err = ECONNREFUSED;
+		else
+			err = ECONNRESET;
+
+		s->state = BT_CLOSED;
+		rfcomm_session_close(s, err);
+	}
+
+	return 0;
+}
+
+static int rfcomm_recv_sabm(struct rfcomm_session *s, int dlci)
+{
+	struct rfcomm_dlc *d;
+	int channel;
+
+	BT_DBG("session %p state %ld dlci %d", s, s->state, dlci);
+
+	if (!dlci) {
+		rfcomm_send_ua(s, 0);
+
+		if (s->state == BT_OPEN) {
+			s->state = BT_CONNECTED;
+			rfcomm_process_connect(s);
+		}
+		return 0;
+	}
+
+	/* Check if DLC exists */
+	d = rfcomm_dlc_get(s, dlci);
+	if (d) {
+		if (d->state == BT_OPEN) {
+			/* DLC was previously opened by PN request */
+			rfcomm_send_ua(s, dlci);
+
+			rfcomm_dlc_lock(d);
+			d->state = BT_CONNECTED;
+			d->state_change(d, 0);
+			rfcomm_dlc_unlock(d);
+
+			rfcomm_send_msc(s, 1, dlci, d->v24_sig);
+		}
+		return 0;
+	}
+
+	/* Notify socket layer about incomming connection */
+	channel = __srv_channel(dlci);
+	if (rfcomm_connect_ind(s, channel, &d)) {
+		d->dlci = dlci;
+		d->addr = __addr(s->initiator, dlci);
+		rfcomm_dlc_link(s, d);
+
+		rfcomm_send_ua(s, dlci);
+
+		rfcomm_dlc_lock(d);
+		d->state = BT_CONNECTED;
+		d->state_change(d, 0);
+		rfcomm_dlc_unlock(d);
+	} else {
+		rfcomm_send_dm(s, dlci);
+	}
+
+	return 0;
+}
+
+static int rfcomm_apply_pn(struct rfcomm_dlc *d, int cr, struct rfcomm_pn *pn)
+{
+	BT_DBG("dlc %p state %ld dlci %d mtu %d fc 0x%x credits %d", 
+			d, d->state, d->dlci, pn->mtu, pn->flow_ctrl, pn->credits);
+
+	if (cr) {
+		if (pn->flow_ctrl == 0xf0) {
+			d->tx_credits = pn->credits;
+		} else {
+			set_bit(RFCOMM_TX_THROTTLED, &d->flags);
+			d->credits = 0;
+		}
+
+		d->mtu = btohs(pn->mtu);
+	} else {
+		if (pn->flow_ctrl == 0xe0) {
+			d->tx_credits = pn->credits;
+		} else {
+			set_bit(RFCOMM_TX_THROTTLED, &d->flags);
+			d->credits = 0;
+		}
+
+		d->mtu = btohs(pn->mtu);
+	}
+
+	return 0;
+}
+
+static int rfcomm_recv_pn(struct rfcomm_session *s, int cr, struct sk_buff *skb)
+{
+	struct rfcomm_pn *pn = (void *) skb->data;
+	struct rfcomm_dlc *d;
+	int dlci = pn->dlci;
+
+	BT_DBG("session %p state %ld dlci %d", s, s->state, dlci);
+
+	if (!dlci)
+		return 0;
+
+	d = rfcomm_dlc_get(s, dlci);
+	if (d) {
+		if (cr) {
+			/* PN request */
+			rfcomm_apply_pn(d, cr, pn);
+			rfcomm_send_pn(s, 0, d);
+		} else {
+			/* PN response */
+			switch (d->state) {
+			case BT_CONFIG:
+				rfcomm_apply_pn(d, cr, pn);
+				
+				d->state = BT_CONNECT;
+				rfcomm_send_sabm(s, d->dlci);
+				break;
+			}
+		}
+	} else {
+		int channel = __srv_channel(dlci);
+
+		if (!cr)
+			return 0;
+		
+		/* PN request for non existing DLC.
+		 * Assume incomming connection. */
+		if (rfcomm_connect_ind(s, channel, &d)) {
+			d->dlci = dlci;
+			d->addr = __addr(s->initiator, dlci);
+			rfcomm_dlc_link(s, d);
+
+			rfcomm_apply_pn(d, cr, pn);
+			
+			d->state = BT_OPEN;
+			rfcomm_send_pn(s, 0, d);
+		} else {
+			rfcomm_send_dm(s, dlci);
+		}
+	}
+	return 0;
+}
+
+static int rfcomm_recv_rpn(struct rfcomm_session *s, int cr, int len, struct sk_buff *skb)
+{
+	struct rfcomm_rpn *rpn = (void *) skb->data;
+	int dlci = __get_dlci(rpn->dlci);
+
+	u8 bit_rate  = 0;
+	u8 data_bits = 0;
+	u8 stop_bits = 0;
+	u8 parity    = 0;
+	u8 flow_ctrl = 0;
+	u8 xon_char  = 0;
+	u8 xoff_char = 0;
+	u16 rpn_mask = RFCOMM_RPN_PM_ALL;
+	
+	BT_DBG("dlci %d cr %d len 0x%x bitr 0x%x line 0x%x flow 0x%x xonc 0x%x xoffc 0x%x pm 0x%x", 
+	       dlci, cr, len, rpn->bit_rate, rpn->line_settings, rpn->flow_ctrl,
+	       rpn->xon_char, rpn->xoff_char, rpn->param_mask);
+	
+	if (!cr) 
+		return 0;
+	
+	if (len == 1) {
+		/* request: return default setting */
+		bit_rate  = RFCOMM_RPN_BR_115200;
+		data_bits = RFCOMM_RPN_DATA_8;
+		stop_bits = RFCOMM_RPN_STOP_1;
+		parity    = RFCOMM_RPN_PARITY_NONE;
+		flow_ctrl = RFCOMM_RPN_FLOW_NONE;
+		xon_char  = RFCOMM_RPN_XON_CHAR;
+		xoff_char = RFCOMM_RPN_XOFF_CHAR;
+
+		goto rpn_out;
+	}
+	/* check for sane values: ignore/accept bit_rate, 8 bits, 1 stop bit, no parity,
+	                          no flow control lines, normal XON/XOFF chars */
+	if (rpn->param_mask & RFCOMM_RPN_PM_DATA) {
+		data_bits = __get_rpn_data_bits(rpn->line_settings);
+		if (data_bits != RFCOMM_RPN_DATA_8) {
+			BT_DBG("RPN data bits mismatch 0x%x", data_bits);
+			data_bits = RFCOMM_RPN_DATA_8;
+			rpn_mask ^= RFCOMM_RPN_PM_DATA;
+		}
+	}
+	if (rpn->param_mask & RFCOMM_RPN_PM_STOP) {
+		stop_bits = __get_rpn_stop_bits(rpn->line_settings);
+		if (stop_bits != RFCOMM_RPN_STOP_1) {
+			BT_DBG("RPN stop bits mismatch 0x%x", stop_bits);
+			stop_bits = RFCOMM_RPN_STOP_1;
+			rpn_mask ^= RFCOMM_RPN_PM_STOP;
+		}
+	}
+	if (rpn->param_mask & RFCOMM_RPN_PM_PARITY) {
+		parity = __get_rpn_parity(rpn->line_settings);
+		if (parity != RFCOMM_RPN_PARITY_NONE) {
+			BT_DBG("RPN parity mismatch 0x%x", parity);
+			parity = RFCOMM_RPN_PARITY_NONE;
+			rpn_mask ^= RFCOMM_RPN_PM_PARITY;
+		}
+	}
+	if (rpn->param_mask & RFCOMM_RPN_PM_FLOW) {
+		if (rpn->flow_ctrl != RFCOMM_RPN_FLOW_NONE) {
+			BT_DBG("RPN flow ctrl mismatch 0x%x", rpn->flow_ctrl);
+			rpn->flow_ctrl = RFCOMM_RPN_FLOW_NONE;
+			rpn_mask ^= RFCOMM_RPN_PM_FLOW;
+		}
+	}
+	if (rpn->param_mask & RFCOMM_RPN_PM_XON) {
+		if (rpn->xon_char != RFCOMM_RPN_XON_CHAR) {
+			BT_DBG("RPN XON char mismatch 0x%x", rpn->xon_char);
+			rpn->xon_char = RFCOMM_RPN_XON_CHAR;
+			rpn_mask ^= RFCOMM_RPN_PM_XON;
+		}
+	}
+	if (rpn->param_mask & RFCOMM_RPN_PM_XOFF) {
+		if (rpn->xoff_char != RFCOMM_RPN_XOFF_CHAR) {
+			BT_DBG("RPN XOFF char mismatch 0x%x", rpn->xoff_char);
+			rpn->xoff_char = RFCOMM_RPN_XOFF_CHAR;
+			rpn_mask ^= RFCOMM_RPN_PM_XOFF;
+		}
+	}
+
+rpn_out:
+	rfcomm_send_rpn(s, 0, dlci, 
+			bit_rate, data_bits, stop_bits, parity, flow_ctrl,
+			xon_char, xoff_char, rpn_mask);
+
+	return 0;
+}
+
+static int rfcomm_recv_msc(struct rfcomm_session *s, int cr, struct sk_buff *skb)
+{
+	struct rfcomm_msc *msc = (void *) skb->data;
+	struct rfcomm_dlc *d;
+	int dlci = __get_dlci(msc->dlci);
+
+	BT_DBG("dlci %d cr %d v24 0x%x", dlci, cr, msc->v24_sig);
+
+	if (!cr)
+		return 0;
+
+	d = rfcomm_dlc_get(s, dlci);
+	if (d) {
+		if (msc->v24_sig & RFCOMM_V24_FC && !d->credits)
+			set_bit(RFCOMM_TX_THROTTLED, &d->flags);
+		else
+			clear_bit(RFCOMM_TX_THROTTLED, &d->flags);
+		
+		rfcomm_dlc_lock(d);
+		if (d->modem_status)
+			d->modem_status(d, msc->v24_sig);
+		rfcomm_dlc_unlock(d);
+		
+		rfcomm_send_msc(s, 0, dlci, msc->v24_sig);
+	}
+	return 0;
+}
+
+static int rfcomm_recv_mcc(struct rfcomm_session *s, struct sk_buff *skb)
+{
+	struct rfcomm_mcc *mcc = (void *) skb->data;
+	u8 type, cr, len;
+
+	cr   = __test_cr(mcc->type);
+	type = __get_mcc_type(mcc->type);
+	len  = __get_mcc_len(mcc->len);
+
+	BT_DBG("%p type 0x%x cr %d", s, type, cr);
+
+	skb_pull(skb, 2);
+
+	switch (type) {
+	case RFCOMM_PN:
+		rfcomm_recv_pn(s, cr, skb);
+		break;
+
+	case RFCOMM_RPN:
+		rfcomm_recv_rpn(s, cr, len, skb);
+		break;
+
+	case RFCOMM_MSC:
+		rfcomm_recv_msc(s, cr, skb);
+		break;
+
+	case RFCOMM_TEST:
+		if (cr)
+			rfcomm_send_test(s, 0, skb->data, skb->len);
+		break;
+
+	default:
+		BT_ERR("Unknown control type 0x%02x", type);
+		rfcomm_send_nsc(s, cr, type);
+		break;
+	}
+	return 0;
+}
+
+static int rfcomm_recv_data(struct rfcomm_session *s, int dlci, int pf, struct sk_buff *skb)
+{
+	struct rfcomm_dlc *d;
+
+	BT_DBG("session %p state %ld dlci %d pf %d", s, s->state, dlci, pf);
+
+	d = rfcomm_dlc_get(s, dlci);
+	if (!d) {
+		rfcomm_send_dm(s, dlci);
+		goto drop;
+	}
+
+	if (pf && d->credits) {
+		u8 credits = *(u8 *) skb->data; skb_pull(skb, 1);
+
+		d->tx_credits += credits;
+		if (d->tx_credits)
+			clear_bit(RFCOMM_TX_THROTTLED, &d->flags);
+	}
+
+	if (skb->len && d->state == BT_CONNECTED) {
+		rfcomm_dlc_lock(d);
+		d->rx_credits--;
+		d->data_ready(d, skb);
+		rfcomm_dlc_unlock(d);
+		return 0;
+	}
+
+drop:
+	kfree_skb(skb);
+	return 0;
+}
+
+static int rfcomm_recv_frame(struct rfcomm_session *s, struct sk_buff *skb)
+{
+	struct rfcomm_hdr *hdr = (void *) skb->data;
+	u8 type, dlci, fcs;
+
+	dlci = __get_dlci(hdr->addr);
+	type = __get_type(hdr->ctrl);
+
+	/* Trim FCS */
+	skb->len--; skb->tail--;
+	fcs = *(u8 *) skb->tail;
+	
+	if (__check_fcs(skb->data, type, fcs)) {
+		BT_ERR("bad checksum in packet");
+		kfree_skb(skb);
+		return -EILSEQ;
+	}
+
+	if (__test_ea(hdr->len))
+		skb_pull(skb, 3);
+	else
+		skb_pull(skb, 4);
+	
+	switch (type) {
+	case RFCOMM_SABM:
+		if (__test_pf(hdr->ctrl))
+			rfcomm_recv_sabm(s, dlci);
+		break;
+
+	case RFCOMM_DISC:
+		if (__test_pf(hdr->ctrl))
+			rfcomm_recv_disc(s, dlci);
+		break;
+
+	case RFCOMM_UA:
+		if (__test_pf(hdr->ctrl))
+			rfcomm_recv_ua(s, dlci);
+		break;
+
+	case RFCOMM_DM:
+		rfcomm_recv_dm(s, dlci);
+		break;
+
+	case RFCOMM_UIH:
+		if (dlci)
+			return rfcomm_recv_data(s, dlci, __test_pf(hdr->ctrl), skb);
+
+		rfcomm_recv_mcc(s, skb);
+		break;
+
+	default:
+		BT_ERR("Unknown packet type 0x%02x\n", type);
+		break;
+	}
+	kfree_skb(skb);
+	return 0;
+}
+
+/* ---- Connection and data processing ---- */
+
+static void rfcomm_process_connect(struct rfcomm_session *s)
+{
+	struct rfcomm_dlc *d;
+	struct list_head *p, *n;
+
+	BT_DBG("session %p state %ld", s, s->state);
+
+	list_for_each_safe(p, n, &s->dlcs) {
+		d = list_entry(p, struct rfcomm_dlc, list);
+		if (d->state == BT_CONFIG) {
+			d->mtu = s->mtu;
+			rfcomm_send_pn(s, 1, d);
+		}
+	}
+}
+
+/* Send data queued for the DLC.
+ * Return number of frames left in the queue.
+ */
+static inline int rfcomm_process_tx(struct rfcomm_dlc *d)
+{
+	struct sk_buff *skb;
+	int err;
+
+	BT_DBG("dlc %p state %ld credits %d rx_credits %d tx_credits %d", 
+			d, d->state, d->credits, d->rx_credits, d->tx_credits);
+
+	/* Send pending MSC */
+	if (test_and_clear_bit(RFCOMM_MSC_PENDING, &d->flags))
+		rfcomm_send_msc(d->session, d->dlci, 1, d->v24_sig); 
+	
+	if (d->credits) {
+		/* CFC enabled. 
+		 * Give them some credits */
+		if (!test_bit(RFCOMM_RX_THROTTLED, &d->flags) &&
+			       	d->rx_credits <= (d->credits >> 2)) {
+			rfcomm_send_credits(d->session, d->addr, d->credits - d->rx_credits);
+			d->rx_credits = d->credits;
+		}
+	} else {
+		/* CFC disabled. 
+		 * Give ourselves some credits */
+		d->tx_credits = RFCOMM_MAX_CREDITS;
+	}
+
+	if (test_bit(RFCOMM_TX_THROTTLED, &d->flags))
+		return skb_queue_len(&d->tx_queue);
+
+	while (d->tx_credits && (skb = skb_dequeue(&d->tx_queue))) {
+		err = rfcomm_send_frame(d->session, skb->data, skb->len);
+		if (err < 0) {
+			skb_queue_head(&d->tx_queue, skb);
+			break;
+		}
+		kfree_skb(skb);
+		d->tx_credits--;
+	}
+
+	if (d->credits && !d->tx_credits) {
+		/* We're out of TX credits.
+		 * Set TX_THROTTLED flag to avoid unnesary wakeups by dlc_send. */
+		set_bit(RFCOMM_TX_THROTTLED, &d->flags);
+	}
+
+	return skb_queue_len(&d->tx_queue);
+}
+
+static inline void rfcomm_process_dlcs(struct rfcomm_session *s)
+{
+	struct rfcomm_dlc *d;
+	struct list_head *p, *n;
+
+	BT_DBG("session %p state %ld", s, s->state);
+
+	list_for_each_safe(p, n, &s->dlcs) {
+		d = list_entry(p, struct rfcomm_dlc, list);
+		if (test_bit(RFCOMM_TIMED_OUT, &d->flags)) {
+			__rfcomm_dlc_close(d, ETIMEDOUT);
+			continue;
+		}
+
+		if (d->state == BT_CONNECTED || d->state == BT_DISCONN)
+			rfcomm_process_tx(d);
+	}
+}
+
+static inline void rfcomm_process_rx(struct rfcomm_session *s)
+{
+	struct socket *sock = s->sock;
+	struct sock *sk = sock->sk;
+	struct sk_buff *skb;
+
+	BT_DBG("session %p state %ld qlen %d", s, s->state, skb_queue_len(&sk->receive_queue));
+
+	/* Get data directly from socket receive queue without copying it. */
+	while ((skb = skb_dequeue(&sk->receive_queue))) {
+		skb_orphan(skb);
+		rfcomm_recv_frame(s, skb);
+	}
+
+	if (sk->state == BT_CLOSED) {
+		if (!s->initiator)
+			rfcomm_session_put(s);
+
+		rfcomm_session_close(s, sk->err);
+	}
+}
+
+static inline void rfcomm_accept_connection(struct rfcomm_session *s)
+{
+	struct socket *sock = s->sock, *nsock;
+	int err;
+
+	/* Fast check for a new connection.
+	 * Avoids unnesesary socket allocations. */
+	if (list_empty(&bluez_sk(sock->sk)->accept_q))
+		return;
+
+	BT_DBG("session %p", s);
+
+	nsock = sock_alloc();
+	if (!nsock)
+		return;
+
+	nsock->type = sock->type;
+	nsock->ops  = sock->ops;
+	
+	err = sock->ops->accept(sock, nsock, O_NONBLOCK);
+	if (err < 0) {
+		sock_release(nsock);
+		return;
+	}
+
+	/* Set our callbacks */
+	nsock->sk->data_ready   = rfcomm_l2data_ready;
+	nsock->sk->state_change = rfcomm_l2state_change;
+
+	s = rfcomm_session_add(nsock, BT_OPEN);
+	if (s)
+		rfcomm_session_hold(s);
+	else
+		sock_release(nsock);
+}
+
+static inline void rfcomm_check_connection(struct rfcomm_session *s)
+{
+	struct sock *sk = s->sock->sk;
+
+	BT_DBG("%p state %ld", s, s->state);
+
+	switch(sk->state) {
+	case BT_CONNECTED:
+		s->state = BT_CONNECT;
+
+		/* We can adjust MTU on outgoing sessions.
+		 * L2CAP MTU minus UIH header and FCS. */
+		s->mtu = min(l2cap_pi(sk)->omtu, l2cap_pi(sk)->imtu) - 5;
+
+		rfcomm_send_sabm(s, 0);
+		break;
+
+	case BT_CLOSED:
+		s->state = BT_CLOSED;
+		rfcomm_session_close(s, sk->err);
+		break;
+	}
+}
+
+static inline void rfcomm_process_sessions(void)
+{
+	struct list_head *p, *n;
+
+	rfcomm_lock();
+
+	list_for_each_safe(p, n, &session_list) {
+		struct rfcomm_session *s;
+		s = list_entry(p, struct rfcomm_session, list);
+
+		if (s->state == BT_LISTEN) {
+			rfcomm_accept_connection(s);
+			continue;
+		}
+
+		rfcomm_session_hold(s);
+
+		switch (s->state) {
+		case BT_BOUND:
+			rfcomm_check_connection(s);
+			break;
+
+		default:
+			rfcomm_process_rx(s);
+			break;
+		}
+
+		rfcomm_process_dlcs(s);
+
+		rfcomm_session_put(s);
+	}
+	
+	rfcomm_unlock();
+}
+
+static void rfcomm_worker(void)
+{
+	BT_DBG("");
+
+	while (!atomic_read(&terminate)) {
+		if (!test_and_clear_bit(RFCOMM_SCHED_WAKEUP, &rfcomm_event)) {
+			/* No pending events. Let's sleep.
+			 * Incomming connections and data will wake us up. */
+			set_current_state(TASK_INTERRUPTIBLE);
+			schedule();
+		}
+
+		/* Process stuff */
+		rfcomm_process_sessions();
+	}
+	set_current_state(TASK_RUNNING);
+	return;
+}
+
+static int rfcomm_add_listener(bdaddr_t *ba)
+{
+	struct sockaddr_l2 addr;
+	struct l2cap_options opts;
+	struct socket *sock;
+	struct rfcomm_session *s;
+	int    size, err = 0;
+
+	/* Create socket */
+	err = rfcomm_l2sock_create(&sock);
+	if (err < 0) { 
+		BT_ERR("Create socket failed %d", err);
+		return err;
+	}
+
+	/* Bind socket */
+	bacpy(&addr.l2_bdaddr, ba);
+	addr.l2_family = AF_BLUETOOTH;
+	addr.l2_psm    = htobs(RFCOMM_PSM);
+	err = sock->ops->bind(sock, (struct sockaddr *) &addr, sizeof(addr));
+	if (err < 0) {
+		BT_ERR("Bind failed %d", err);
+		goto failed;
+	}
+
+	/* Set L2CAP options */
+	size = sizeof(opts);
+	sock->ops->getsockopt(sock, SOL_L2CAP, L2CAP_OPTIONS, (void *)&opts, &size);
+
+	opts.imtu = RFCOMM_MAX_L2CAP_MTU;
+	sock->ops->setsockopt(sock, SOL_L2CAP, L2CAP_OPTIONS, (void *)&opts, size);
+
+	/* Start listening on the socket */
+	err = sock->ops->listen(sock, 10);
+	if (err) {
+		BT_ERR("Listen failed %d", err);
+		goto failed;
+	}
+
+	/* Add listening session */
+	s = rfcomm_session_add(sock, BT_LISTEN);
+	if (!s)
+		goto failed;
+
+	rfcomm_session_hold(s);
+	return 0;
+failed:
+	sock_release(sock);
+	return err;
+}
+
+static void rfcomm_kill_listener(void)
+{
+	struct rfcomm_session *s;
+	struct list_head *p, *n;
+
+	BT_DBG("");
+
+	list_for_each_safe(p, n, &session_list) {
+		s = list_entry(p, struct rfcomm_session, list);
+		rfcomm_session_del(s);
+	}
+}
+
+static int rfcomm_run(void *unused)
+{
+	rfcomm_thread = current;
+
+	atomic_inc(&running);
+
+	daemonize();
+	set_user_nice(current, -10);
+	current->flags |= PF_IOTHREAD;
+	sigfillset(&current->blocked);
+	flush_signals(current);
+
+	sprintf(current->comm, "krfcommd");
+
+	set_fs(KERNEL_DS);
+
+	BT_DBG("");
+
+	rfcomm_add_listener(BDADDR_ANY);
+
+	rfcomm_worker();
+
+	rfcomm_kill_listener();
+
+	atomic_dec(&running);
+	return 0;
+}
+
+int __init rfcomm_init(void)
+{
+	kernel_thread(rfcomm_run, NULL, CLONE_FS | CLONE_FILES | CLONE_SIGHAND);
+
+	rfcomm_init_sockets();
+
+#ifdef CONFIG_BLUEZ_RFCOMM_TTY
+	rfcomm_init_ttys();
+#endif
+
+	BT_INFO("BlueZ RFCOMM ver %s", VERSION);
+	BT_INFO("Copyright (C) 2002 Maxim Krasnyansky <maxk@qualcomm.com>");
+	BT_INFO("Copyright (C) 2002 Marcel Holtmann <marcel@holtmann.org>");
+	return 0;
+}
+
+void rfcomm_cleanup(void)
+{
+	/* Terminate working thread.
+	 * ie. Set terminate flag and wake it up */
+	atomic_inc(&terminate);
+	rfcomm_schedule(RFCOMM_SCHED_STATE);
+
+	/* Wait until thread is running */
+	while (atomic_read(&running))
+		schedule();
+
+#ifdef CONFIG_BLUEZ_RFCOMM_TTY
+	rfcomm_cleanup_ttys();
+#endif
+
+	rfcomm_cleanup_sockets();
+	return;
+}
+
+module_init(rfcomm_init);
+module_exit(rfcomm_cleanup);
+
+MODULE_AUTHOR("Maxim Krasnyansky <maxk@qualcomm.com>, Marcel Holtmann <marcel@holtmann.org>");
+MODULE_DESCRIPTION("BlueZ RFCOMM ver " VERSION);
+MODULE_LICENSE("GPL");
diff -Nru a/net/bluetooth/rfcomm/crc.c b/net/bluetooth/rfcomm/crc.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/net/bluetooth/rfcomm/crc.c	Mon Oct  7 11:26:06 2002
@@ -0,0 +1,71 @@
+/* 
+   RFCOMM implementation for Linux Bluetooth stack (BlueZ).
+   Copyright (C) 2002 Maxim Krasnyansky <maxk@qualcomm.com>
+   Copyright (C) 2002 Marcel Holtmann <marcel@holtmann.org>
+
+   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;
+
+   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+   IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
+   CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES 
+   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 
+   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 
+   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+   ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, 
+   COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS 
+   SOFTWARE IS DISCLAIMED.
+*/
+
+/*
+ * RFCOMM FCS calculation.
+ *
+ * $Id: crc.c,v 1.2 2002/09/21 09:54:32 holtmann Exp $
+ */
+
+/* reversed, 8-bit, poly=0x07 */
+unsigned char rfcomm_crc_table[256] = { 
+	0x00, 0x91, 0xe3, 0x72, 0x07, 0x96, 0xe4, 0x75,
+	0x0e, 0x9f, 0xed, 0x7c, 0x09, 0x98, 0xea, 0x7b,
+	0x1c, 0x8d, 0xff, 0x6e, 0x1b, 0x8a, 0xf8, 0x69,
+	0x12, 0x83, 0xf1, 0x60, 0x15, 0x84, 0xf6, 0x67,
+
+	0x38, 0xa9, 0xdb, 0x4a, 0x3f, 0xae, 0xdc, 0x4d,
+	0x36, 0xa7, 0xd5, 0x44, 0x31, 0xa0, 0xd2, 0x43,
+	0x24, 0xb5, 0xc7, 0x56, 0x23, 0xb2, 0xc0, 0x51,
+	0x2a, 0xbb, 0xc9, 0x58, 0x2d, 0xbc, 0xce, 0x5f,
+
+	0x70, 0xe1, 0x93, 0x02, 0x77, 0xe6, 0x94, 0x05,
+	0x7e, 0xef, 0x9d, 0x0c, 0x79, 0xe8, 0x9a, 0x0b,
+	0x6c, 0xfd, 0x8f, 0x1e, 0x6b, 0xfa, 0x88, 0x19,
+	0x62, 0xf3, 0x81, 0x10, 0x65, 0xf4, 0x86, 0x17,
+
+	0x48, 0xd9, 0xab, 0x3a, 0x4f, 0xde, 0xac, 0x3d,
+	0x46, 0xd7, 0xa5, 0x34, 0x41, 0xd0, 0xa2, 0x33,
+	0x54, 0xc5, 0xb7, 0x26, 0x53, 0xc2, 0xb0, 0x21,
+	0x5a, 0xcb, 0xb9, 0x28, 0x5d, 0xcc, 0xbe, 0x2f,
+
+	0xe0, 0x71, 0x03, 0x92, 0xe7, 0x76, 0x04, 0x95,
+	0xee, 0x7f, 0x0d, 0x9c, 0xe9, 0x78, 0x0a, 0x9b,
+	0xfc, 0x6d, 0x1f, 0x8e, 0xfb, 0x6a, 0x18, 0x89,
+	0xf2, 0x63, 0x11, 0x80, 0xf5, 0x64, 0x16, 0x87,
+
+	0xd8, 0x49, 0x3b, 0xaa, 0xdf, 0x4e, 0x3c, 0xad,
+	0xd6, 0x47, 0x35, 0xa4, 0xd1, 0x40, 0x32, 0xa3,
+	0xc4, 0x55, 0x27, 0xb6, 0xc3, 0x52, 0x20, 0xb1,
+	0xca, 0x5b, 0x29, 0xb8, 0xcd, 0x5c, 0x2e, 0xbf,
+
+	0x90, 0x01, 0x73, 0xe2, 0x97, 0x06, 0x74, 0xe5,
+	0x9e, 0x0f, 0x7d, 0xec, 0x99, 0x08, 0x7a, 0xeb,
+	0x8c, 0x1d, 0x6f, 0xfe, 0x8b, 0x1a, 0x68, 0xf9,
+	0x82, 0x13, 0x61, 0xf0, 0x85, 0x14, 0x66, 0xf7,
+
+	0xa8, 0x39, 0x4b, 0xda, 0xaf, 0x3e, 0x4c, 0xdd,
+	0xa6, 0x37, 0x45, 0xd4, 0xa1, 0x30, 0x42, 0xd3,
+	0xb4, 0x25, 0x57, 0xc6, 0xb3, 0x22, 0x50, 0xc1,
+	0xba, 0x2b, 0x59, 0xc8, 0xbd, 0x2c, 0x5e, 0xcf
+};
diff -Nru a/net/bluetooth/rfcomm/sock.c b/net/bluetooth/rfcomm/sock.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/net/bluetooth/rfcomm/sock.c	Mon Oct  7 11:26:06 2002
@@ -0,0 +1,869 @@
+/* 
+   RFCOMM implementation for Linux Bluetooth stack (BlueZ).
+   Copyright (C) 2002 Maxim Krasnyansky <maxk@qualcomm.com>
+   Copyright (C) 2002 Marcel Holtmann <marcel@holtmann.org>
+
+   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;
+
+   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+   IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
+   CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES 
+   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 
+   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 
+   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+   ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, 
+   COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS 
+   SOFTWARE IS DISCLAIMED.
+*/
+
+/*
+ * RFCOMM sockets.
+ *
+ * $Id: sock.c,v 1.24 2002/10/03 01:00:34 maxk Exp $
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/poll.h>
+#include <linux/fcntl.h>
+#include <linux/init.h>
+#include <linux/skbuff.h>
+#include <linux/interrupt.h>
+#include <linux/socket.h>
+#include <linux/skbuff.h>
+#include <linux/proc_fs.h>
+#include <linux/list.h>
+#include <net/sock.h>
+
+#include <asm/system.h>
+#include <asm/uaccess.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/rfcomm.h>
+
+#ifndef CONFIG_BLUEZ_RFCOMM_DEBUG
+#undef  BT_DBG
+#define BT_DBG(D...)
+#endif
+
+static struct proto_ops rfcomm_sock_ops;
+
+static struct bluez_sock_list rfcomm_sk_list = {
+	lock: RW_LOCK_UNLOCKED
+};
+
+static void rfcomm_sock_close(struct sock *sk);
+static void rfcomm_sock_kill(struct sock *sk);
+
+/* ---- DLC callbacks ----
+ *
+ * called under rfcomm_dlc_lock()
+ */
+static void rfcomm_sk_data_ready(struct rfcomm_dlc *d, struct sk_buff *skb)
+{
+	struct sock *sk = d->owner;
+	if (!sk)
+		return;
+
+	atomic_add(skb->len, &sk->rmem_alloc);
+	skb_queue_tail(&sk->receive_queue, skb);
+	sk->data_ready(sk, skb->len);
+
+	if (atomic_read(&sk->rmem_alloc) >= sk->rcvbuf)
+		rfcomm_dlc_throttle(d);
+	return;
+}
+
+static void rfcomm_sk_state_change(struct rfcomm_dlc *d, int err)
+{
+	struct sock *sk = d->owner, *parent;
+	if (!sk)
+		return;
+
+	BT_DBG("dlc %p state %ld err %d", d, d->state, err);
+
+	if (err)
+		sk->err = err;
+	sk->state = d->state;
+
+	parent = bluez_sk(sk)->parent;
+	if (!parent)
+		sk->state_change(sk);
+	else
+		parent->data_ready(parent, 0);
+	return;
+}
+
+static void rfcomm_sk_modem_status(struct rfcomm_dlc *d, int v24_sig)
+{
+	BT_DBG("dlc %p v24_sig 0x%02x", d, v24_sig);
+
+	return;
+}
+
+/* ---- Socket functions ---- */
+static struct sock *__rfcomm_get_sock_by_addr(int channel, bdaddr_t *src)
+{
+	struct sock *sk;
+
+	for (sk = rfcomm_sk_list.head; sk; sk = sk->next) {
+		if (rfcomm_pi(sk)->channel == channel && 
+				!bacmp(&bluez_sk(sk)->src, src))
+			break;
+	}
+
+	return sk;
+}
+
+/* Find socket with channel and source bdaddr.
+ * Returns closest match.
+ */
+static struct sock *__rfcomm_get_sock_by_channel(int state, __u16 channel, bdaddr_t *src)
+{
+	struct sock *sk, *sk1 = NULL;
+
+	for (sk = rfcomm_sk_list.head; sk; sk = sk->next) {
+		if (state && sk->state != state)
+			continue;
+
+		if (rfcomm_pi(sk)->channel == channel) {
+			/* Exact match. */
+			if (!bacmp(&bluez_sk(sk)->src, src))
+				break;
+
+			/* Closest match */
+			if (!bacmp(&bluez_sk(sk)->src, BDADDR_ANY))
+				sk1 = sk;
+		}
+	}
+	return sk ? sk : sk1;
+}
+
+/* Find socket with given address (channel, src).
+ * Returns locked socket */
+static inline struct sock *rfcomm_get_sock_by_channel(int state, __u16 channel, bdaddr_t *src)
+{
+	struct sock *s;
+	read_lock(&rfcomm_sk_list.lock);
+	s = __rfcomm_get_sock_by_channel(state, channel, src);
+	if (s) bh_lock_sock(s);
+	read_unlock(&rfcomm_sk_list.lock);
+	return s;
+}
+
+static void rfcomm_sock_destruct(struct sock *sk)
+{
+	struct rfcomm_dlc *d = rfcomm_pi(sk)->dlc;
+
+	BT_DBG("sk %p dlc %p", sk, d);
+
+	skb_queue_purge(&sk->receive_queue);
+	skb_queue_purge(&sk->write_queue);
+
+	rfcomm_dlc_lock(d);
+	rfcomm_pi(sk)->dlc = NULL;
+	
+	/* Detach DLC if it's owned by this socket */
+	if (d->owner == sk)
+		d->owner = NULL;
+	rfcomm_dlc_unlock(d);
+
+	rfcomm_dlc_put(d);
+
+	if (sk->protinfo)
+		kfree(sk->protinfo);
+	
+	MOD_DEC_USE_COUNT;
+}
+
+static void rfcomm_sock_cleanup_listen(struct sock *parent)
+{
+	struct sock *sk;
+
+	BT_DBG("parent %p", parent);
+
+	/* Close not yet accepted dlcs */
+	while ((sk = bluez_accept_dequeue(parent, NULL)))
+		rfcomm_sock_close(sk);
+
+	parent->state  = BT_CLOSED;
+	parent->zapped = 1;
+}
+
+/* Kill socket (only if zapped and orphan)
+ * Must be called on unlocked socket.
+ */
+static void rfcomm_sock_kill(struct sock *sk)
+{
+	if (!sk->zapped || sk->socket)
+		return;
+
+	BT_DBG("sk %p state %d refcnt %d", sk, sk->state, atomic_read(&sk->refcnt));
+
+	/* Kill poor orphan */
+	bluez_sock_unlink(&rfcomm_sk_list, sk);
+	sk->dead = 1;
+	sock_put(sk);
+}
+
+/* Close socket.
+ * Must be called on unlocked socket.
+ */
+static void rfcomm_sock_close(struct sock *sk)
+{
+	struct rfcomm_dlc *d = rfcomm_pi(sk)->dlc;
+
+	lock_sock(sk);
+
+	BT_DBG("sk %p state %d socket %p", sk, sk->state, sk->socket);
+
+	switch (sk->state) {
+	case BT_LISTEN:
+		rfcomm_sock_cleanup_listen(sk);
+		break;
+
+	case BT_CONNECT:
+	case BT_CONNECT2:
+	case BT_CONFIG:
+	case BT_CONNECTED:
+		rfcomm_dlc_close(d, 0);
+
+	default:
+		sk->zapped = 1;
+		break;
+	};
+
+	release_sock(sk);
+
+	rfcomm_sock_kill(sk);
+}
+
+static void rfcomm_sock_init(struct sock *sk, struct sock *parent)
+{
+	BT_DBG("sk %p", sk);
+
+	if (parent) 
+		sk->type = parent->type;
+}
+
+static struct sock *rfcomm_sock_alloc(struct socket *sock, int proto, int prio)
+{
+	struct rfcomm_dlc *d;
+	struct sock *sk;
+
+	sk = bluez_sock_alloc(sock, BTPROTO_RFCOMM, sizeof(struct rfcomm_pinfo), prio);
+	if (!sk)
+		return NULL;
+
+	d = rfcomm_dlc_alloc(prio);
+	if (!d) {
+		sk_free(sk);
+		return NULL;
+	}
+	d->data_ready   = rfcomm_sk_data_ready;
+	d->state_change = rfcomm_sk_state_change;
+	d->modem_status = rfcomm_sk_modem_status;
+
+	rfcomm_pi(sk)->dlc = d;
+	d->owner = sk;
+
+	sk->destruct = rfcomm_sock_destruct;
+	sk->sndtimeo = RFCOMM_CONN_TIMEOUT;
+
+	sk->sndbuf   = RFCOMM_MAX_CREDITS * RFCOMM_DEFAULT_MTU * 10;
+	sk->rcvbuf   = RFCOMM_MAX_CREDITS * RFCOMM_DEFAULT_MTU * 10;
+
+	sk->protocol = proto;
+	sk->state    = BT_OPEN;
+
+	bluez_sock_link(&rfcomm_sk_list, sk);
+
+	BT_DBG("sk %p", sk);
+
+	MOD_INC_USE_COUNT;
+	return sk;
+}
+
+static int rfcomm_sock_create(struct socket *sock, int protocol)
+{
+	struct sock *sk;
+
+	BT_DBG("sock %p", sock);
+
+	sock->state = SS_UNCONNECTED;
+
+	if (sock->type != SOCK_STREAM && sock->type != SOCK_RAW)
+		return -ESOCKTNOSUPPORT;
+
+	sock->ops = &rfcomm_sock_ops;
+
+	if (!(sk = rfcomm_sock_alloc(sock, protocol, GFP_KERNEL)))
+		return -ENOMEM;
+
+	rfcomm_sock_init(sk, NULL);
+	return 0;
+}
+
+static int rfcomm_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_len)
+{
+	struct sockaddr_rc *sa = (struct sockaddr_rc *) addr;
+	struct sock *sk = sock->sk;
+	int err = 0;
+
+	BT_DBG("sk %p %s", sk, batostr(&sa->rc_bdaddr));
+
+	if (!addr || addr->sa_family != AF_BLUETOOTH)
+		return -EINVAL;
+
+	lock_sock(sk);
+
+	if (sk->state != BT_OPEN) {
+		err = -EBADFD;
+		goto done;
+	}
+
+	write_lock_bh(&rfcomm_sk_list.lock);
+
+	if (sa->rc_channel && __rfcomm_get_sock_by_addr(sa->rc_channel, &sa->rc_bdaddr)) {
+		err = -EADDRINUSE;
+	} else {
+		/* Save source address */
+		bacpy(&bluez_sk(sk)->src, &sa->rc_bdaddr);
+		rfcomm_pi(sk)->channel = sa->rc_channel;
+		sk->state = BT_BOUND;
+	}
+
+	write_unlock_bh(&rfcomm_sk_list.lock);
+
+done:
+	release_sock(sk);
+	return err;
+}
+
+static int rfcomm_sock_connect(struct socket *sock, struct sockaddr *addr, int alen, int flags)
+{
+	struct sockaddr_rc *sa = (struct sockaddr_rc *) addr;
+	struct sock *sk = sock->sk;
+	struct rfcomm_dlc *d = rfcomm_pi(sk)->dlc;
+	int err = 0;
+
+	BT_DBG("sk %p", sk);
+
+	if (addr->sa_family != AF_BLUETOOTH || alen < sizeof(struct sockaddr_rc))
+		return -EINVAL;
+
+	if (sk->state != BT_OPEN && sk->state != BT_BOUND)
+		return -EBADFD;
+
+	if (sk->type != SOCK_STREAM)
+		return -EINVAL;
+
+	lock_sock(sk);
+
+	sk->state = BT_CONNECT;
+	bacpy(&bluez_sk(sk)->dst, &sa->rc_bdaddr);
+
+	err = rfcomm_dlc_open(d, &bluez_sk(sk)->src, &sa->rc_bdaddr, sa->rc_channel);
+	if (!err)
+		err = bluez_sock_w4_connect(sk, flags);
+
+	release_sock(sk);
+	return err;
+}
+
+int rfcomm_sock_listen(struct socket *sock, int backlog)
+{
+	struct sock *sk = sock->sk;
+	int err = 0;
+
+	BT_DBG("sk %p backlog %d", sk, backlog);
+
+	lock_sock(sk);
+
+	if (sk->state != BT_BOUND) {
+		err = -EBADFD;
+		goto done;
+	}
+
+	sk->max_ack_backlog = backlog;
+	sk->ack_backlog = 0;
+	sk->state = BT_LISTEN;
+
+done:
+	release_sock(sk);
+	return err;
+}
+
+int rfcomm_sock_accept(struct socket *sock, struct socket *newsock, int flags)
+{
+	DECLARE_WAITQUEUE(wait, current);
+	struct sock *sk = sock->sk, *nsk;
+	long timeo;
+	int err = 0;
+
+	lock_sock(sk);
+
+	if (sk->state != BT_LISTEN) {
+		err = -EBADFD;
+		goto done;
+	}
+
+	timeo = sock_rcvtimeo(sk, flags & O_NONBLOCK);
+
+	BT_DBG("sk %p timeo %ld", sk, timeo);
+
+	/* Wait for an incoming connection. (wake-one). */
+	add_wait_queue_exclusive(sk->sleep, &wait);
+	while (!(nsk = bluez_accept_dequeue(sk, newsock))) {
+		set_current_state(TASK_INTERRUPTIBLE);
+		if (!timeo) {
+			err = -EAGAIN;
+			break;
+		}
+
+		release_sock(sk);
+		timeo = schedule_timeout(timeo);
+		lock_sock(sk);
+
+		if (sk->state != BT_LISTEN) {
+			err = -EBADFD;
+			break;
+		}
+
+		if (signal_pending(current)) {
+			err = sock_intr_errno(timeo);
+			break;
+		}
+	}
+	set_current_state(TASK_RUNNING);
+	remove_wait_queue(sk->sleep, &wait);
+
+	if (err)
+		goto done;
+
+	newsock->state = SS_CONNECTED;
+
+	BT_DBG("new socket %p", nsk);
+
+done:
+	release_sock(sk);
+	return err;
+}
+
+static int rfcomm_sock_getname(struct socket *sock, struct sockaddr *addr, int *len, int peer)
+{
+	struct sockaddr_rc *sa = (struct sockaddr_rc *) addr;
+	struct sock *sk = sock->sk;
+
+	BT_DBG("sock %p, sk %p", sock, sk);
+
+	sa->rc_family  = AF_BLUETOOTH;
+	sa->rc_channel = rfcomm_pi(sk)->channel;
+	if (peer)
+		bacpy(&sa->rc_bdaddr, &bluez_sk(sk)->dst);
+	else
+		bacpy(&sa->rc_bdaddr, &bluez_sk(sk)->src);
+
+	*len = sizeof(struct sockaddr_rc);
+	return 0;
+}
+
+static int rfcomm_sock_sendmsg(struct socket *sock, struct msghdr *msg, int len,
+			       struct scm_cookie *scm)
+{
+	struct sock *sk = sock->sk;
+	struct rfcomm_dlc *d = rfcomm_pi(sk)->dlc;
+	struct sk_buff *skb;
+	int err, size;
+	int sent = 0;
+
+	if (msg->msg_flags & MSG_OOB)
+		return -EOPNOTSUPP;
+
+	if (sk->shutdown & SEND_SHUTDOWN)
+		return -EPIPE;
+
+	BT_DBG("sock %p, sk %p", sock, sk);
+
+	lock_sock(sk);
+
+	while (len) {
+		size = min_t(uint, len, d->mtu);
+		
+		skb = sock_alloc_send_skb(sk, size + RFCOMM_SKB_RESERVE,
+				msg->msg_flags & MSG_DONTWAIT, &err);
+		if (!skb)
+			break;
+		skb_reserve(skb, RFCOMM_SKB_HEAD_RESERVE);
+
+		err = memcpy_fromiovec(skb_put(skb, size), msg->msg_iov, size);
+		if (err) {
+			kfree_skb(skb);
+			sent = err;
+			break;
+		}
+
+		err = rfcomm_dlc_send(d, skb);
+		if (err < 0) {
+			kfree_skb(skb);
+			break;
+		}
+
+		sent += size;
+		len  -= size;
+	}
+
+	release_sock(sk);
+
+	return sent ? sent : err;
+}
+
+static long rfcomm_sock_data_wait(struct sock *sk, long timeo)
+{
+	DECLARE_WAITQUEUE(wait, current);
+
+	add_wait_queue(sk->sleep, &wait);
+	for (;;) {
+		set_current_state(TASK_INTERRUPTIBLE);
+
+		if (skb_queue_len(&sk->receive_queue) || sk->err || (sk->shutdown & RCV_SHUTDOWN) ||
+				signal_pending(current) || !timeo)
+			break;
+
+		set_bit(SOCK_ASYNC_WAITDATA, &sk->socket->flags);
+		release_sock(sk);
+		timeo = schedule_timeout(timeo);
+		lock_sock(sk);
+		clear_bit(SOCK_ASYNC_WAITDATA, &sk->socket->flags);
+	}
+
+	__set_current_state(TASK_RUNNING);
+	remove_wait_queue(sk->sleep, &wait);
+	return timeo;
+}
+
+static int rfcomm_sock_recvmsg(struct socket *sock, struct msghdr *msg, int size,
+			       int flags, struct scm_cookie *scm)
+{
+	struct sock *sk = sock->sk;
+	int target, err = 0, copied = 0;
+	long timeo;
+
+	if (sk->state != BT_CONNECTED)
+		return -EINVAL;
+
+	if (flags & MSG_OOB)
+		return -EOPNOTSUPP;
+
+	msg->msg_namelen = 0;
+
+	BT_DBG("sk %p size %d", sk, size);
+
+	lock_sock(sk);
+
+	target = sock_rcvlowat(sk, flags & MSG_WAITALL, size);
+	timeo  = sock_rcvtimeo(sk, flags & MSG_DONTWAIT);
+
+	do {
+		struct sk_buff *skb;
+		int chunk;
+
+		skb = skb_dequeue(&sk->receive_queue);
+		if (!skb) {
+			if (copied >= target)
+				break;
+
+			if ((err = sock_error(sk)) != 0)
+				break;
+			if (sk->shutdown & RCV_SHUTDOWN)
+				break;
+
+			err = -EAGAIN;
+			if (!timeo)
+				break;
+
+			timeo = rfcomm_sock_data_wait(sk, timeo);
+
+			if (signal_pending(current)) {
+				err = sock_intr_errno(timeo);
+				goto out;
+			}
+			continue;
+		}
+
+		chunk = min_t(unsigned int, skb->len, size);
+		if (memcpy_toiovec(msg->msg_iov, skb->data, chunk)) {
+			skb_queue_head(&sk->receive_queue, skb);
+			if (!copied)
+				copied = -EFAULT;
+			break;
+		}
+		copied += chunk;
+		size   -= chunk;
+
+		if (!(flags & MSG_PEEK)) {
+			atomic_sub(chunk, &sk->rmem_alloc);
+
+			skb_pull(skb, chunk);
+			if (skb->len) {
+				skb_queue_head(&sk->receive_queue, skb);
+				break;
+			}
+			kfree_skb(skb);
+
+		} else {
+			/* put message back and return */
+			skb_queue_head(&sk->receive_queue, skb);
+			break;
+		}
+	} while (size);
+
+out:
+	if (atomic_read(&sk->rmem_alloc) <= (sk->rcvbuf >> 2))
+		rfcomm_dlc_unthrottle(rfcomm_pi(sk)->dlc);
+
+	release_sock(sk);
+	return copied ? : err;
+}
+
+static int rfcomm_sock_shutdown(struct socket *sock, int how)
+{
+	struct sock *sk = sock->sk;
+
+	BT_DBG("sock %p, sk %p", sock, sk);
+
+	if (!sk) return 0;
+
+	lock_sock(sk);
+	sk->shutdown = SHUTDOWN_MASK;
+	if (sk->state == BT_CONNECTED)
+		rfcomm_dlc_close(rfcomm_pi(sk)->dlc, 0);
+	release_sock(sk);
+
+	return 0;
+}
+
+
+static int rfcomm_sock_setsockopt(struct socket *sock, int level, int optname, char *optval, int optlen)
+{
+	struct sock *sk = sock->sk;
+	int err = 0;
+
+	BT_DBG("sk %p", sk);
+
+	lock_sock(sk);
+
+	switch (optname) {
+	default:
+		err = -ENOPROTOOPT;
+		break;
+	}
+
+	release_sock(sk);
+	return err;
+}
+
+static int rfcomm_sock_getsockopt(struct socket *sock, int level, int optname, char *optval, int *optlen)
+{
+	struct sock *sk = sock->sk;
+	int len, err = 0; 
+
+	BT_DBG("sk %p", sk);
+
+	if (get_user(len, optlen))
+		return -EFAULT;
+
+	lock_sock(sk);
+
+	switch (optname) {
+	default:
+		err = -ENOPROTOOPT;
+		break;
+	};
+
+	release_sock(sk);
+	return err;
+}
+
+static int rfcomm_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
+{
+	struct sock *sk = sock->sk;
+	int err;
+
+	lock_sock(sk);
+
+#ifdef CONFIG_BLUEZ_RFCOMM_TTY
+	err = rfcomm_dev_ioctl(sk, cmd, arg);
+#else
+	err = -EOPNOTSUPP;
+#endif
+
+	release_sock(sk);
+	return err;
+}
+
+static int rfcomm_sock_release(struct socket *sock)
+{
+	struct sock *sk = sock->sk;
+
+	BT_DBG("sock %p, sk %p", sock, sk);
+
+	if (!sk)
+		return 0;
+
+	sock_orphan(sk);
+	rfcomm_sock_close(sk);
+
+	return 0;
+}
+
+/* ---- RFCOMM core layer callbacks ---- 
+ *
+ * called under rfcomm_lock()
+ */
+int rfcomm_connect_ind(struct rfcomm_session *s, u8 channel, struct rfcomm_dlc **d)
+{
+	struct bluez_sock *bsk = bluez_sk(s->sock->sk);
+	struct sock *sk, *parent;
+	int result = 0;
+
+	BT_DBG("session %p channel %d", s, channel);
+
+	/* Check if we have socket listening on channel */
+	parent = rfcomm_get_sock_by_channel(BT_LISTEN, channel, &bsk->src);
+	if (!parent)
+		return 0;
+
+	/* Check for backlog size */
+	if (parent->ack_backlog > parent->max_ack_backlog) {
+		BT_DBG("backlog full %d", parent->ack_backlog); 
+		goto done;
+	}
+
+	sk = rfcomm_sock_alloc(NULL, BTPROTO_RFCOMM, GFP_ATOMIC);
+	if (!sk)
+		goto done;
+
+	rfcomm_sock_init(sk, parent);
+	bacpy(&bluez_sk(sk)->src, &bsk->src);
+	bacpy(&bluez_sk(sk)->dst, &bsk->dst);
+	rfcomm_pi(sk)->channel = channel;
+
+	sk->state = BT_CONFIG;
+	bluez_accept_enqueue(parent, sk);
+
+	/* Accept connection and return socket DLC */
+	*d = rfcomm_pi(sk)->dlc;
+	result = 1;
+
+done:
+	bh_unlock_sock(parent);
+	return result;
+}
+
+/* ---- Proc fs support ---- */
+static int rfcomm_sock_dump(char *buf, struct bluez_sock_list *list)
+{
+	struct rfcomm_pinfo *pi;
+	struct sock *sk;
+	char *ptr = buf;
+
+	write_lock_bh(&list->lock);
+
+	for (sk = list->head; sk; sk = sk->next) {
+		pi = rfcomm_pi(sk);
+		ptr += sprintf(ptr, "%s %s %d %d\n",
+				batostr(&bluez_sk(sk)->src), batostr(&bluez_sk(sk)->dst),
+				sk->state, rfcomm_pi(sk)->channel); 
+	}
+
+	write_unlock_bh(&list->lock);
+
+	ptr += sprintf(ptr, "\n");
+
+	return ptr - buf;
+}
+
+static int rfcomm_read_proc(char *buf, char **start, off_t offset, int count, int *eof, void *priv)
+{
+	char *ptr = buf;
+	int len;
+
+	BT_DBG("count %d, offset %ld", count, offset);
+
+	ptr += rfcomm_sock_dump(ptr, &rfcomm_sk_list);
+	len  = ptr - buf;
+
+	if (len <= count + offset)
+		*eof = 1;
+
+	*start = buf + offset;
+	len -= offset;
+
+	if (len > count)
+		len = count;
+	if (len < 0)
+		len = 0;
+
+	return len;
+}
+
+static struct proto_ops rfcomm_sock_ops = {
+	.family		= PF_BLUETOOTH,
+	.release	= rfcomm_sock_release,
+	.bind		= rfcomm_sock_bind,
+	.connect	= rfcomm_sock_connect,
+	.listen		= rfcomm_sock_listen,
+	.accept		= rfcomm_sock_accept,
+	.getname	= rfcomm_sock_getname,
+	.sendmsg	= rfcomm_sock_sendmsg,
+	.recvmsg	= rfcomm_sock_recvmsg,
+	.shutdown	= rfcomm_sock_shutdown,
+	.setsockopt	= rfcomm_sock_setsockopt,
+	.getsockopt	= rfcomm_sock_getsockopt,
+	.ioctl		= rfcomm_sock_ioctl,
+	.poll		= bluez_sock_poll,
+	.socketpair	= sock_no_socketpair,
+	.mmap		= sock_no_mmap
+};
+
+static struct net_proto_family rfcomm_sock_family_ops = {
+	.family		= PF_BLUETOOTH,
+	.create		= rfcomm_sock_create
+};
+
+int rfcomm_init_sockets(void)
+{
+	int err;
+
+	if ((err = bluez_sock_register(BTPROTO_RFCOMM, &rfcomm_sock_family_ops))) {
+		BT_ERR("Can't register RFCOMM socket layer");
+		return err;
+	}
+
+	create_proc_read_entry("bluetooth/rfcomm", 0, 0, rfcomm_read_proc, NULL);
+	return 0;
+}
+
+void rfcomm_cleanup_sockets(void)
+{
+	int err;
+
+	remove_proc_entry("bluetooth/rfcomm", NULL);
+
+	/* Unregister socket, protocol and notifier */
+	if ((err = bluez_sock_unregister(BTPROTO_RFCOMM)))
+		BT_ERR("Can't unregister RFCOMM socket layer %d", err);
+}
diff -Nru a/net/bluetooth/rfcomm/tty.c b/net/bluetooth/rfcomm/tty.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/net/bluetooth/rfcomm/tty.c	Mon Oct  7 11:26:06 2002
@@ -0,0 +1,884 @@
+/* 
+   RFCOMM implementation for Linux Bluetooth stack (BlueZ).
+   Copyright (C) 2002 Maxim Krasnyansky <maxk@qualcomm.com>
+   Copyright (C) 2002 Marcel Holtmann <marcel@holtmann.org>
+
+   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;
+
+   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+   IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
+   CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES 
+   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 
+   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 
+   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+   ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, 
+   COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS 
+   SOFTWARE IS DISCLAIMED.
+*/
+
+/*
+ * RFCOMM TTY.
+ *
+ * $Id: tty.c,v 1.24 2002/10/03 01:54:38 holtmann Exp $
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+
+#include <linux/tty.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+
+#include <linux/slab.h>
+#include <linux/skbuff.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/rfcomm.h>
+
+#ifndef CONFIG_BLUEZ_RFCOMM_DEBUG
+#undef  BT_DBG
+#define BT_DBG(D...)
+#endif
+
+#define RFCOMM_TTY_MAGIC 0x6d02		/* magic number for rfcomm struct */
+#define RFCOMM_TTY_PORTS RFCOMM_MAX_DEV	/* whole lotta rfcomm devices */
+#define RFCOMM_TTY_MAJOR 216		/* device node major id of the usb/bluetooth.c driver */
+#define RFCOMM_TTY_MINOR 0
+
+struct rfcomm_dev {
+	struct list_head	list;
+	atomic_t		refcnt;
+
+	char			name[12];
+	int			id;
+	unsigned long		flags;
+	int			opened;
+	int			err;
+
+	bdaddr_t		src;
+	bdaddr_t		dst;
+	u8 			channel;
+
+	struct rfcomm_dlc	*dlc;
+	struct tty_struct	*tty;
+	wait_queue_head_t       wait;
+	struct tasklet_struct   wakeup_task;
+
+	atomic_t 		wmem_alloc;
+	unsigned int 		sndbuf;
+};
+
+static LIST_HEAD(rfcomm_dev_list);
+static rwlock_t rfcomm_dev_lock = RW_LOCK_UNLOCKED;
+
+static void rfcomm_dev_data_ready(struct rfcomm_dlc *dlc, struct sk_buff *skb);
+static void rfcomm_dev_state_change(struct rfcomm_dlc *dlc, int err);
+static void rfcomm_dev_modem_status(struct rfcomm_dlc *dlc, int v24_sig);
+
+static void rfcomm_tty_wakeup(unsigned long arg);
+
+/* ---- Device functions ---- */
+static void rfcomm_dev_destruct(struct rfcomm_dev *dev)
+{
+	struct rfcomm_dlc *dlc = dev->dlc;
+
+	BT_DBG("dev %p dlc %p", dev, dlc);
+
+	rfcomm_dlc_lock(dlc);
+	/* Detach DLC if it's owned by this dev */
+	if (dlc->owner == dev)
+		dlc->owner = NULL;
+	rfcomm_dlc_unlock(dlc);
+
+	rfcomm_dlc_put(dlc);
+	kfree(dev);
+
+	MOD_DEC_USE_COUNT;
+}
+
+static inline void rfcomm_dev_hold(struct rfcomm_dev *dev)
+{
+	atomic_inc(&dev->refcnt);
+}
+
+static inline void rfcomm_dev_put(struct rfcomm_dev *dev)
+{
+	if (atomic_dec_and_test(&dev->refcnt))
+		rfcomm_dev_destruct(dev);
+}
+
+static struct rfcomm_dev *__rfcomm_dev_get(int id)
+{
+	struct rfcomm_dev *dev;
+	struct list_head  *p;
+
+	list_for_each(p, &rfcomm_dev_list) {
+		dev = list_entry(p, struct rfcomm_dev, list);
+		if (dev->id == id)
+			return dev;
+	}
+
+	return NULL;
+}
+
+static inline struct rfcomm_dev *rfcomm_dev_get(int id)
+{
+	struct rfcomm_dev *dev;
+
+	read_lock(&rfcomm_dev_lock);
+	dev = __rfcomm_dev_get(id);
+	read_unlock(&rfcomm_dev_lock);
+
+	if (dev) rfcomm_dev_hold(dev);
+	return dev;
+}
+
+static int rfcomm_dev_add(struct rfcomm_dev_req *req, struct rfcomm_dlc *dlc)
+{
+	struct rfcomm_dev *dev;
+	struct list_head *head = &rfcomm_dev_list, *p;
+	int err = 0;
+
+	BT_DBG("id %d channel %d", req->dev_id, req->channel);
+	
+	dev = kmalloc(sizeof(struct rfcomm_dev), GFP_KERNEL);
+	if (!dev)
+		return -ENOMEM;
+	memset(dev, 0, sizeof(struct rfcomm_dev));
+
+	write_lock_bh(&rfcomm_dev_lock);
+
+	if (req->dev_id < 0) {
+		dev->id = 0;
+
+		list_for_each(p, &rfcomm_dev_list) {
+			if (list_entry(p, struct rfcomm_dev, list)->id != dev->id)
+				break;
+
+			dev->id++;
+			head = p;
+		}
+	} else {
+		dev->id = req->dev_id;
+
+		list_for_each(p, &rfcomm_dev_list) {
+			struct rfcomm_dev *entry = list_entry(p, struct rfcomm_dev, list);
+
+			if (entry->id == dev->id) {
+				err = -EADDRINUSE;
+				goto out;
+			}
+
+			if (entry->id > dev->id - 1)
+				break;
+
+			head = p;
+		}
+	}
+
+	if ((dev->id < 0) || (dev->id > RFCOMM_MAX_DEV - 1)) {
+		err = -ENFILE;
+		goto out;
+	}
+
+	sprintf(dev->name, "rfcomm%d", dev->id);
+
+	list_add(&dev->list, head);
+	atomic_set(&dev->refcnt, 1);
+
+	bacpy(&dev->src, &req->src);
+	bacpy(&dev->dst, &req->dst);
+	dev->channel = req->channel;
+
+	dev->flags = req->flags & 
+		((1 << RFCOMM_RELEASE_ONHUP) | (1 << RFCOMM_REUSE_DLC));
+
+	dev->sndbuf = RFCOMM_MAX_CREDITS * RFCOMM_DEFAULT_MTU * 10;
+
+	init_waitqueue_head(&dev->wait);
+	tasklet_init(&dev->wakeup_task, rfcomm_tty_wakeup, (unsigned long) dev);
+
+	rfcomm_dlc_lock(dlc);
+	dlc->data_ready   = rfcomm_dev_data_ready;
+	dlc->state_change = rfcomm_dev_state_change;
+	dlc->modem_status = rfcomm_dev_modem_status;
+
+	dlc->owner = dev;
+	dev->dlc   = dlc;
+	rfcomm_dlc_unlock(dlc);
+
+	MOD_INC_USE_COUNT;
+	
+out:
+	write_unlock_bh(&rfcomm_dev_lock);
+
+	if (err) {
+		kfree(dev);
+		return err;
+	} else
+		return dev->id;
+}
+
+static void rfcomm_dev_del(struct rfcomm_dev *dev)
+{
+	BT_DBG("dev %p", dev);
+
+	write_lock_bh(&rfcomm_dev_lock);
+	list_del_init(&dev->list);
+	write_unlock_bh(&rfcomm_dev_lock);
+
+	rfcomm_dev_put(dev);
+}
+
+/* ---- Send buffer ---- */
+static void rfcomm_wfree(struct sk_buff *skb)
+{
+	struct rfcomm_dev *dev = (void *) skb->sk;
+	atomic_sub(skb->truesize, &dev->wmem_alloc);
+	if (test_bit(RFCOMM_TTY_ATTACHED, &dev->flags))
+		tasklet_schedule(&dev->wakeup_task);
+	rfcomm_dev_put(dev);
+}
+
+static inline void rfcomm_set_owner_w(struct sk_buff *skb, struct rfcomm_dev *dev)
+{
+	rfcomm_dev_hold(dev);
+	atomic_add(skb->truesize, &dev->wmem_alloc);
+	skb->sk = (void *) dev;
+	skb->destructor = rfcomm_wfree;
+}
+
+static struct sk_buff *rfcomm_wmalloc(struct rfcomm_dev *dev, unsigned long size, int priority)
+{
+	if (size || atomic_read(&dev->wmem_alloc) < dev->sndbuf) {
+		struct sk_buff *skb = alloc_skb(size, priority);
+		if (skb) {
+			rfcomm_set_owner_w(skb, dev);
+			return skb;
+		}
+	}
+	return NULL;
+}
+
+/* ---- Device IOCTLs ---- */
+static int rfcomm_create_dev(struct sock *sk, unsigned long arg)
+{
+	struct rfcomm_dev_req req;
+	struct rfcomm_dlc *dlc;
+	int id;
+
+	if (copy_from_user(&req, (void *) arg, sizeof(req)))
+		return -EFAULT;
+
+	BT_DBG("sk %p dev_id %id flags 0x%x", sk, req.dev_id, req.flags);
+
+	if (req.flags & (1 << RFCOMM_REUSE_DLC)) {
+		dlc = rfcomm_pi(sk)->dlc;
+		rfcomm_dlc_hold(dlc);
+	} else {
+		dlc = rfcomm_dlc_alloc(GFP_KERNEL);
+		if (!dlc)
+			return -ENOMEM;
+	}
+
+	id = rfcomm_dev_add(&req, dlc);
+	if (id < 0) {
+		rfcomm_dlc_put(dlc);
+		return id;
+	}
+
+	if (req.flags & (1 << RFCOMM_REUSE_DLC)) {
+		/* DLC is now used by device.
+		 * Socket must be disconnected */
+		sk->state = BT_CLOSED;
+	}
+
+	return id;
+}
+
+static int rfcomm_release_dev(unsigned long arg)
+{
+	struct rfcomm_dev_req req;
+	struct rfcomm_dev *dev;
+
+	if (copy_from_user(&req, (void *) arg, sizeof(req)))
+		return -EFAULT;
+
+	BT_DBG("dev_id %id flags 0x%x", req.dev_id, req.flags);
+
+	if (!(dev = rfcomm_dev_get(req.dev_id)))
+		return -ENODEV;
+
+	if (req.flags & (1 << RFCOMM_HANGUP_NOW))
+		rfcomm_dlc_close(dev->dlc, 0);
+
+	rfcomm_dev_del(dev);
+	rfcomm_dev_put(dev);
+	return 0;
+}
+
+static int rfcomm_get_dev_list(unsigned long arg)
+{
+	struct rfcomm_dev_list_req *dl;
+	struct rfcomm_dev_info *di;
+	struct list_head *p;
+	int n = 0, size;
+	u16 dev_num;
+
+	BT_DBG("");
+
+	if (get_user(dev_num, (u16 *) arg))
+		return -EFAULT;
+
+	if (!dev_num)
+		return -EINVAL;
+
+	size = sizeof(*dl) + dev_num * sizeof(*di);
+
+	if (verify_area(VERIFY_WRITE, (void *)arg, size))
+		return -EFAULT;
+
+	if (!(dl = kmalloc(size, GFP_KERNEL)))
+		return -ENOMEM;
+
+	di = dl->dev_info;
+
+	read_lock_bh(&rfcomm_dev_lock);
+
+	list_for_each(p, &rfcomm_dev_list) {
+		struct rfcomm_dev *dev = list_entry(p, struct rfcomm_dev, list);
+		(di + n)->id      = dev->id;
+		(di + n)->flags   = dev->flags;
+		(di + n)->state   = dev->dlc->state;
+		(di + n)->channel = dev->channel;
+		bacpy(&(di + n)->src, &dev->src);
+		bacpy(&(di + n)->dst, &dev->dst);
+		if (++n >= dev_num)
+			break;
+	}
+
+	read_unlock_bh(&rfcomm_dev_lock);
+
+	dl->dev_num = n;
+	size = sizeof(*dl) + n * sizeof(*di);
+
+	copy_to_user((void *) arg, dl, size);
+	kfree(dl);
+	return 0;
+}
+
+static int rfcomm_get_dev_info(unsigned long arg)
+{
+	struct rfcomm_dev *dev;
+	struct rfcomm_dev_info di;
+	int err = 0;
+
+	BT_DBG("");
+
+	if (copy_from_user(&di, (void *)arg, sizeof(di)))
+		return -EFAULT;
+
+	if (!(dev = rfcomm_dev_get(di.id)))
+		return -ENODEV;
+
+	di.flags   = dev->flags;
+	di.channel = dev->channel;
+	di.state   = dev->dlc->state;
+	bacpy(&di.src, &dev->src);
+	bacpy(&di.dst, &dev->dst);
+
+	if (copy_to_user((void *)arg, &di, sizeof(di)))
+		err = -EFAULT;
+
+	rfcomm_dev_put(dev);
+	return err;
+}
+
+int rfcomm_dev_ioctl(struct sock *sk, unsigned int cmd, unsigned long arg)
+{
+	BT_DBG("cmd %d arg %ld", cmd, arg);
+
+	switch (cmd) {
+	case RFCOMMCREATEDEV:
+		return rfcomm_create_dev(sk, arg);
+
+	case RFCOMMRELEASEDEV:
+		return rfcomm_release_dev(arg);
+
+	case RFCOMMGETDEVLIST:
+		return rfcomm_get_dev_list(arg);
+
+	case RFCOMMGETDEVINFO:
+		return rfcomm_get_dev_info(arg);
+	}
+
+	return -EINVAL;
+}
+
+/* ---- DLC callbacks ---- */
+static void rfcomm_dev_data_ready(struct rfcomm_dlc *dlc, struct sk_buff *skb)
+{
+	struct rfcomm_dev *dev = dlc->owner;
+	struct tty_struct *tty;
+       
+	if (!dev || !(tty = dev->tty)) {
+		kfree(skb);
+		return;
+	}
+
+	BT_DBG("dlc %p tty %p len %d", dlc, tty, skb->len);
+
+	if (test_bit(TTY_DONT_FLIP, &tty->flags)) {
+		register int i;
+		for (i = 0; i < skb->len; i++) {
+			if (tty->flip.count >= TTY_FLIPBUF_SIZE)
+				tty_flip_buffer_push(tty);
+
+			tty_insert_flip_char(tty, skb->data[i], 0);
+		}
+		tty_flip_buffer_push(tty);
+	} else
+		tty->ldisc.receive_buf(tty, skb->data, NULL, skb->len);
+
+	kfree_skb(skb);
+}
+
+static void rfcomm_dev_state_change(struct rfcomm_dlc *dlc, int err)
+{
+	struct rfcomm_dev *dev = dlc->owner;
+	if (!dev)
+		return;
+	
+	BT_DBG("dlc %p dev %p err %d", dlc, dev, err);
+
+	dev->err = err;
+	wake_up_interruptible(&dev->wait);
+
+	if (dlc->state == BT_CLOSED) {
+		if (!dev->tty) {
+			if (test_bit(RFCOMM_RELEASE_ONHUP, &dev->flags)) {
+				rfcomm_dev_hold(dev);
+				rfcomm_dev_del(dev);
+
+				/* We have to drop DLC lock here, otherwise
+				 * rfcomm_dev_put() will dead lock if it's the last refference */
+				rfcomm_dlc_unlock(dlc);
+				rfcomm_dev_put(dev);
+				rfcomm_dlc_lock(dlc);
+			}
+		} else 
+			tty_hangup(dev->tty);
+	}
+}
+
+static void rfcomm_dev_modem_status(struct rfcomm_dlc *dlc, int v24_sig)
+{
+	BT_DBG("dlc %p v24_sig 0x%02x", dlc, v24_sig);
+}
+
+
+/* ---- TTY functions ---- */
+static void rfcomm_tty_wakeup(unsigned long arg)
+{
+	struct rfcomm_dev *dev = (void *) arg;
+	struct tty_struct *tty = dev->tty;
+	if (!tty)
+		return;
+
+	BT_DBG("dev %p tty %p", dev, tty);
+
+	if (test_bit(TTY_DO_WRITE_WAKEUP, &tty->flags) && tty->ldisc.write_wakeup)
+                (tty->ldisc.write_wakeup)(tty);
+
+	wake_up_interruptible(&tty->write_wait);
+#ifdef SERIAL_HAVE_POLL_WAIT
+	wake_up_interruptible(&tty->poll_wait);
+#endif
+}
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0)
+#define __minor MINOR
+#else
+#define __minor minor
+#endif
+
+static int rfcomm_tty_open(struct tty_struct *tty, struct file *filp)
+{
+	DECLARE_WAITQUEUE(wait, current);
+	struct rfcomm_dev *dev;
+	struct rfcomm_dlc *dlc;
+	int err, id;
+
+        id = __minor(tty->device) - tty->driver.minor_start;
+
+	BT_DBG("tty %p id %d", tty, id);
+
+	dev = rfcomm_dev_get(id);
+	if (!dev)
+		return -ENODEV;
+
+	BT_DBG("dev %p dst %s channel %d opened %d", dev, batostr(&dev->dst), dev->channel, dev->opened);
+
+	if (dev->opened++ != 0)
+		return 0;
+
+	dlc = dev->dlc;
+
+	/* Attach TTY and open DLC */
+
+	rfcomm_dlc_lock(dlc);
+	tty->driver_data = dev;
+	dev->tty = tty;
+	rfcomm_dlc_unlock(dlc);
+	set_bit(RFCOMM_TTY_ATTACHED, &dev->flags);
+
+	err = rfcomm_dlc_open(dlc, &dev->src, &dev->dst, dev->channel);
+	if (err < 0)
+		return err;
+
+	/* Wait for DLC to connect */
+	add_wait_queue(&dev->wait, &wait);
+	while (1) {
+		set_current_state(TASK_INTERRUPTIBLE);
+
+		if (dlc->state == BT_CLOSED) {
+			err = -dev->err;
+			break;
+		}
+
+		if (dlc->state == BT_CONNECTED)
+			break;
+
+		schedule();
+	}
+	set_current_state(TASK_RUNNING);
+	remove_wait_queue(&dev->wait, &wait);
+
+	return err;
+}
+
+static void rfcomm_tty_close(struct tty_struct *tty, struct file *filp)
+{
+	struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data;
+	if (!dev)
+		return;
+
+	BT_DBG("tty %p dev %p dlc %p opened %d", tty, dev, dev->dlc, dev->opened);
+
+	if (--dev->opened == 0) {
+		/* Close DLC and dettach TTY */
+		rfcomm_dlc_close(dev->dlc, 0);
+
+		clear_bit(RFCOMM_TTY_ATTACHED, &dev->flags);
+		tasklet_kill(&dev->wakeup_task);
+
+		rfcomm_dlc_lock(dev->dlc);
+		tty->driver_data = NULL;
+		dev->tty = NULL;
+		rfcomm_dlc_unlock(dev->dlc);
+	}
+
+	rfcomm_dev_put(dev);
+}
+
+static int rfcomm_tty_write(struct tty_struct *tty, int from_user, const unsigned char *buf, int count)
+{
+	struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data;
+	struct rfcomm_dlc *dlc = dev->dlc;
+	struct sk_buff *skb;
+	int err = 0, sent = 0, size;
+
+	BT_DBG("tty %p from_user %d count %d", tty, from_user, count);
+
+	while (count) {
+		size = min_t(uint, count, dlc->mtu);
+
+		if (from_user)
+			skb = rfcomm_wmalloc(dev, size + RFCOMM_SKB_RESERVE, GFP_KERNEL);
+		else
+			skb = rfcomm_wmalloc(dev, size + RFCOMM_SKB_RESERVE, GFP_ATOMIC);
+		
+		if (!skb)
+			break;
+
+		skb_reserve(skb, RFCOMM_SKB_HEAD_RESERVE);
+
+		if (from_user)
+			copy_from_user(skb_put(skb, size), buf + sent, size);
+		else
+			memcpy(skb_put(skb, size), buf + sent, size);
+
+		if ((err = rfcomm_dlc_send(dlc, skb)) < 0) {
+			kfree_skb(skb);
+			break;
+		}
+
+		sent  += size;
+		count -= size;
+	}
+
+	return sent ? sent : err;
+}
+
+static int rfcomm_tty_write_room(struct tty_struct *tty)
+{
+	struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data;
+	struct rfcomm_dlc *dlc = dev->dlc;
+
+	BT_DBG("tty %p", tty);
+
+	return dlc->mtu * (dlc->tx_credits ? : 10);
+}
+
+static int rfcomm_tty_ioctl(struct tty_struct *tty, struct file *filp, unsigned int cmd, unsigned long arg)
+{
+	struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data;
+	struct rfcomm_dlc *dlc = dev->dlc;
+	unsigned int modeminfo;
+	int err;
+
+	BT_DBG("tty %p cmd 0x%02x", tty, cmd);
+
+	switch (cmd) {
+	case TCGETS:
+		BT_DBG("TCGETS is not supported");
+		return -ENOIOCTLCMD;
+
+	case TCSETS:
+		BT_DBG("TCSETS is not supported");
+		return -ENOIOCTLCMD;
+
+	case TIOCMGET:
+		BT_DBG("TIOCMGET");
+
+		modeminfo = ((dlc->v24_sig & RFCOMM_V24_RTC) ? TIOCM_DSR | TIOCM_DTR : 0)
+			  | ((dlc->v24_sig & RFCOMM_V24_RTR) ? TIOCM_RTS | TIOCM_CTS : 0)
+			  | ((dlc->v24_sig & RFCOMM_V24_IC) ? TIOCM_RI : 0)
+			  | ((dlc->v24_sig & RFCOMM_V24_DV) ? TIOCM_CD : 0);
+
+		return put_user(modeminfo, (unsigned int *)arg);
+
+	case TIOCMBIS:
+		BT_DBG("TIOCMBIS");
+
+		if ((err = get_user(modeminfo, (unsigned int *)arg)))
+			return err;
+
+		dlc->v24_sig |= (modeminfo & TIOCM_DSR) ? RFCOMM_V24_RTC : 0;
+		dlc->v24_sig |= (modeminfo & TIOCM_DTR) ? RFCOMM_V24_RTC : 0;
+		dlc->v24_sig |= (modeminfo & TIOCM_RTS) ? RFCOMM_V24_RTR : 0;
+		dlc->v24_sig |= (modeminfo & TIOCM_CTS) ? RFCOMM_V24_RTR : 0;
+		dlc->v24_sig |= (modeminfo & TIOCM_RI) ? RFCOMM_V24_IC : 0;
+		dlc->v24_sig |= (modeminfo & TIOCM_CD) ? RFCOMM_V24_DV : 0;
+
+		// rfcomm_send_msc(dlc->session, dlc->dlci, 1, dlc->v24_sig);
+
+		return 0;
+
+	case TIOCMBIC:
+	case TIOCMSET:
+		BT_DBG("set modem info");
+		break;
+
+	case TIOCMIWAIT:
+		BT_DBG("TIOCMIWAIT");
+		break;
+
+	case TIOCGICOUNT:
+		BT_DBG("TIOCGICOUNT");
+		break;
+
+	case TIOCGSERIAL:
+		BT_ERR("TIOCGSERIAL is not supported");
+		return -ENOIOCTLCMD;
+
+	case TIOCSSERIAL:
+		BT_ERR("TIOCSSERIAL is not supported");
+		return -ENOIOCTLCMD;
+
+	case TIOCSERGSTRUCT:
+		BT_ERR("TIOCSERGSTRUCT is not supported");
+		return -ENOIOCTLCMD;
+
+	case TIOCSERGETLSR:
+		BT_ERR("TIOCSERGETLSR is not supported");
+		return -ENOIOCTLCMD;
+
+	case TIOCSERCONFIG:
+		BT_ERR("TIOCSERCONFIG is not supported");
+		return -ENOIOCTLCMD;
+
+	default:
+		return -ENOIOCTLCMD;	/* ioctls which we must ignore */
+
+	}
+
+	return -ENOIOCTLCMD;
+}
+
+#define RELEVANT_IFLAG(iflag) (iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))
+
+static void rfcomm_tty_set_termios(struct tty_struct *tty, struct termios *old)
+{
+	BT_DBG("tty %p", tty);
+
+	if ((tty->termios->c_cflag == old->c_cflag) &&
+		(RELEVANT_IFLAG(tty->termios->c_iflag) == RELEVANT_IFLAG(old->c_iflag)))
+		return;
+
+	/* handle turning off CRTSCTS */
+	if ((old->c_cflag & CRTSCTS) && !(tty->termios->c_cflag & CRTSCTS)) {
+		BT_DBG("turning off CRTSCTS");
+	}
+}
+
+static void rfcomm_tty_throttle(struct tty_struct *tty)
+{
+	struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data;
+
+	BT_DBG("tty %p dev %p", tty, dev);
+	
+	rfcomm_dlc_throttle(dev->dlc);
+}
+
+static void rfcomm_tty_unthrottle(struct tty_struct *tty)
+{
+	struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data;
+
+	BT_DBG("tty %p dev %p", tty, dev);
+	
+	rfcomm_dlc_unthrottle(dev->dlc);
+}
+
+static int rfcomm_tty_chars_in_buffer(struct tty_struct *tty)
+{
+	struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data;
+	struct rfcomm_dlc *dlc = dev->dlc;
+
+	BT_DBG("tty %p dev %p", tty, dev);
+
+	if (skb_queue_len(&dlc->tx_queue))
+		return dlc->mtu;
+
+	return 0;
+}
+
+static void rfcomm_tty_flush_buffer(struct tty_struct *tty)
+{
+	struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data;
+	if (!dev)
+		return;
+
+	BT_DBG("tty %p dev %p", tty, dev);
+
+	skb_queue_purge(&dev->dlc->tx_queue);
+
+	if (test_bit(TTY_DO_WRITE_WAKEUP, &tty->flags) && tty->ldisc.write_wakeup)
+		tty->ldisc.write_wakeup(tty);
+}
+
+static void rfcomm_tty_send_xchar(struct tty_struct *tty, char ch)
+{
+	BT_DBG("tty %p ch %c", tty, ch);
+}
+
+static void rfcomm_tty_wait_until_sent(struct tty_struct *tty, int timeout)
+{
+	BT_DBG("tty %p timeout %d", tty, timeout);
+}
+
+static void rfcomm_tty_hangup(struct tty_struct *tty)
+{
+	struct rfcomm_dev *dev = (struct rfcomm_dev *) tty->driver_data;
+	if (!dev)
+		return;
+
+	BT_DBG("tty %p dev %p", tty, dev);
+
+	rfcomm_tty_flush_buffer(tty);
+
+	if (test_bit(RFCOMM_RELEASE_ONHUP, &dev->flags))
+		rfcomm_dev_del(dev);
+}
+
+static int rfcomm_tty_read_proc(char *buf, char **start, off_t offset, int len, int *eof, void *unused)
+{
+	return 0;
+}
+
+/* ---- TTY structure ---- */
+static int    rfcomm_tty_refcount;       /* If we manage several devices */
+
+static struct tty_struct *rfcomm_tty_table[RFCOMM_TTY_PORTS];
+static struct termios *rfcomm_tty_termios[RFCOMM_TTY_PORTS];
+static struct termios *rfcomm_tty_termios_locked[RFCOMM_TTY_PORTS];
+
+static struct tty_driver rfcomm_tty_driver = {
+	.magic			= TTY_DRIVER_MAGIC,
+	.driver_name		= "rfcomm",
+#ifdef CONFIG_DEVFS_FS
+	.name			= "bluetooth/rfcomm/%d",
+#else
+	.name			= "rfcomm%d",
+#endif
+	.major			= RFCOMM_TTY_MAJOR,
+	.minor_start		= RFCOMM_TTY_MINOR,
+	.num			= RFCOMM_TTY_PORTS,
+	.type			= TTY_DRIVER_TYPE_SERIAL,
+	.subtype		= SERIAL_TYPE_NORMAL,
+	.flags			= TTY_DRIVER_REAL_RAW,
+
+	.refcount		= &rfcomm_tty_refcount,
+	.table			= rfcomm_tty_table,
+	.termios		= rfcomm_tty_termios,
+	.termios_locked		= rfcomm_tty_termios_locked,
+
+	.open			= rfcomm_tty_open,
+	.close			= rfcomm_tty_close,
+	.write			= rfcomm_tty_write,
+	.write_room		= rfcomm_tty_write_room,
+	.chars_in_buffer	= rfcomm_tty_chars_in_buffer,
+	.flush_buffer		= rfcomm_tty_flush_buffer,
+	.ioctl			= rfcomm_tty_ioctl,
+	.throttle		= rfcomm_tty_throttle,
+	.unthrottle		= rfcomm_tty_unthrottle,
+	.set_termios		= rfcomm_tty_set_termios,
+	.send_xchar		= rfcomm_tty_send_xchar,
+	.stop			= NULL,
+	.start			= NULL,
+	.hangup			= rfcomm_tty_hangup,
+	.wait_until_sent	= rfcomm_tty_wait_until_sent,
+	.read_proc		= rfcomm_tty_read_proc,
+};
+
+int rfcomm_init_ttys(void)
+{
+	int i;
+
+	/* Initalize our global data */
+	for (i = 0; i < RFCOMM_TTY_PORTS; i++)
+		rfcomm_tty_table[i] = NULL;
+
+	/* Register the TTY driver */
+	rfcomm_tty_driver.init_termios = tty_std_termios;
+	rfcomm_tty_driver.init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+	rfcomm_tty_driver.flags = TTY_DRIVER_REAL_RAW;
+
+	if (tty_register_driver(&rfcomm_tty_driver)) {
+		BT_ERR("Can't register RFCOMM TTY driver");
+		return -1;
+	}
+
+	return 0;
+}
+
+void rfcomm_cleanup_ttys(void)
+{
+	tty_unregister_driver(&rfcomm_tty_driver);
+	return;
+}
diff -Nru a/net/bluetooth/sco.c b/net/bluetooth/sco.c
--- a/net/bluetooth/sco.c	Mon Oct  7 11:25:27 2002
+++ b/net/bluetooth/sco.c	Mon Oct  7 11:25:27 2002
@@ -27,7 +27,7 @@
  *
  * $Id: sco.c,v 1.3 2002/04/17 17:37:16 maxk Exp $
  */
-#define VERSION "0.2"
+#define VERSION "0.3"
 
 #include <linux/config.h>
 #include <linux/module.h>
@@ -67,16 +67,15 @@
 	.lock = RW_LOCK_UNLOCKED
 };
 
-static inline int sco_chan_add(struct sco_conn *conn, struct sock *sk, struct sock *parent);
+static void __sco_chan_add(struct sco_conn *conn, struct sock *sk, struct sock *parent);
 static void sco_chan_del(struct sock *sk, int err);
-static inline struct sock * sco_chan_get(struct sco_conn *conn);
 
 static int  sco_conn_del(struct hci_conn *conn, int err);
 
 static void sco_sock_close(struct sock *sk);
 static void sco_sock_kill(struct sock *sk);
 
-/* ----- SCO timers ------ */
+/* ---- SCO timers ---- */
 static void sco_sock_timeout(unsigned long arg)
 {
 	struct sock *sk = (struct sock *) arg;
@@ -115,7 +114,7 @@
 	sk->timer.data = (unsigned long)sk;
 }
 
-/* -------- SCO connections --------- */
+/* ---- SCO connections ---- */
 static struct sco_conn *sco_conn_add(struct hci_conn *hcon, __u8 status)
 {
 	struct hci_dev *hdev = hcon->hdev;
@@ -150,6 +149,15 @@
 	return conn;
 }
 
+static inline struct sock *sco_chan_get(struct sco_conn *conn)
+{
+	struct sock *sk = NULL;
+	sco_conn_lock(conn);
+	sk = conn->sk;
+	sco_conn_unlock(conn);
+	return sk;
+}
+
 static int sco_conn_del(struct hci_conn *hcon, int err)
 {
 	struct sco_conn *conn;
@@ -176,6 +184,20 @@
 	return 0;
 }
 
+static inline int sco_chan_add(struct sco_conn *conn, struct sock *sk, struct sock *parent)
+{
+	int err = 0;
+
+	sco_conn_lock(conn);
+	if (conn->sk) {
+		err = -EBUSY;
+	} else {
+		__sco_chan_add(conn, sk, parent);
+	}
+	sco_conn_unlock(conn);
+	return err;
+}
+
 int sco_connect(struct sock *sk)
 {
 	bdaddr_t *src = &bluez_sk(sk)->src;
@@ -462,23 +484,20 @@
 		goto done;
 	}
 
-	write_lock(&sco_sk_list.lock);
-
+	write_lock_bh(&sco_sk_list.lock);
+	
 	if (bacmp(src, BDADDR_ANY) && __sco_get_sock_by_addr(src)) {
 		err = -EADDRINUSE;
-		goto unlock;
+	} else {
+		/* Save source address */
+		bacpy(&bluez_sk(sk)->src, &sa->sco_bdaddr);
+		sk->state = BT_BOUND;
 	}
-
-	/* Save source address */
-	bacpy(&bluez_sk(sk)->src, &sa->sco_bdaddr);
-	sk->state = BT_BOUND;
-
-unlock:
-	write_unlock(&sco_sk_list.lock);
+	
+	write_unlock_bh(&sco_sk_list.lock);
 
 done:
 	release_sock(sk);
-
 	return err;
 }
 
@@ -649,7 +668,7 @@
 	default:
 		err = -ENOPROTOOPT;
 		break;
-	};
+	}
 
 	release_sock(sk);
 	return err;
@@ -703,7 +722,7 @@
 	default:
 		err = -ENOPROTOOPT;
 		break;
-	};
+	}
 
 	release_sock(sk);
 	return err;
@@ -735,29 +754,6 @@
 		bluez_accept_enqueue(parent, sk);
 }
 
-static inline int sco_chan_add(struct sco_conn *conn, struct sock *sk, struct sock *parent)
-{
-	int err = 0;
-
-	sco_conn_lock(conn);
-	if (conn->sk) {
-		err = -EBUSY;
-	} else {
-		__sco_chan_add(conn, sk, parent);
-	}
-	sco_conn_unlock(conn);
-	return err;
-}
-
-static inline struct sock * sco_chan_get(struct sco_conn *conn)
-{
-	struct sock *sk = NULL;
-	sco_conn_lock(conn);
-	sk = conn->sk;
-	sco_conn_unlock(conn);
-	return sk;
-}
-
 /* Delete channel. 
  * Must be called on the locked socket. */
 static void sco_chan_del(struct sock *sk, int err)
@@ -895,7 +891,7 @@
 	struct sock *sk;
 	char *ptr = buf;
 
-	write_lock(&list->lock);
+	read_lock_bh(&list->lock);
 
 	for (sk = list->head; sk; sk = sk->next) {
 		pi = sco_pi(sk);
@@ -904,7 +900,7 @@
 				sk->state); 
 	}
 
-	write_unlock(&list->lock);
+	read_unlock_bh(&list->lock);
 
 	ptr += sprintf(ptr, "\n");
 
@@ -936,36 +932,36 @@
 }
 
 static struct proto_ops sco_sock_ops = {
-	.family =	PF_BLUETOOTH,
-	.release =	sco_sock_release,
-	.bind =		sco_sock_bind,
-	.connect =	sco_sock_connect,
-	.listen =	sco_sock_listen,
-	.accept =	sco_sock_accept,
-	.getname =	sco_sock_getname,
-	.sendmsg =	sco_sock_sendmsg,
-	.recvmsg =	bluez_sock_recvmsg,
-	.poll =		bluez_sock_poll,
-	.socketpair =	sock_no_socketpair,
-	.ioctl =	sock_no_ioctl,
-	.shutdown =	sock_no_shutdown,
-	.setsockopt =	sco_sock_setsockopt,
-	.getsockopt =	sco_sock_getsockopt,
-	.mmap =		sock_no_mmap
+	.family  =      PF_BLUETOOTH,
+	.release =      sco_sock_release,
+	.bind    =     	sco_sock_bind,
+	.connect =      sco_sock_connect,
+	.listen  =      sco_sock_listen,
+	.accept  =      sco_sock_accept,
+	.getname =      sco_sock_getname,
+	.sendmsg =      sco_sock_sendmsg,
+	.recvmsg =      bluez_sock_recvmsg,
+	.poll    =      bluez_sock_poll,
+	.ioctl   =      sock_no_ioctl,
+	.mmap    =      sock_no_mmap,
+	.socketpair =   sock_no_socketpair,
+	.shutdown   =   sock_no_shutdown,
+	.setsockopt =   sco_sock_setsockopt,
+	.getsockopt =   sco_sock_getsockopt
 };
 
 static struct net_proto_family sco_sock_family_ops = {
-	.family =	PF_BLUETOOTH,
-	.create =	sco_sock_create
+	.family =       PF_BLUETOOTH,
+	.create =       sco_sock_create
 };
 
 static struct hci_proto sco_hci_proto = {
-	.name =		"SCO",
-	.id =		HCI_PROTO_SCO,
-	.connect_ind =	sco_connect_ind,
-	.connect_cfm =	sco_connect_cfm,
-	.disconn_ind =	sco_disconn_ind,
-	.recv_scodata =	sco_recv_scodata,
+	.name =         "SCO",
+	.id   =         HCI_PROTO_SCO,
+	.connect_ind =  sco_connect_ind,
+	.connect_cfm =  sco_connect_cfm,
+	.disconn_ind =  sco_disconn_ind,
+	.recv_scodata =	sco_recv_scodata
 };
 
 int __init sco_init(void)
diff -Nru a/net/bridge/br.c b/net/bridge/br.c
--- a/net/bridge/br.c	Mon Oct  7 11:25:26 2002
+++ b/net/bridge/br.c	Mon Oct  7 11:25:26 2002
@@ -21,6 +21,7 @@
 #include <linux/etherdevice.h>
 #include <linux/init.h>
 #include <linux/if_bridge.h>
+#include <linux/brlock.h>
 #include <asm/uaccess.h>
 #include "br_private.h"
 
@@ -55,11 +56,6 @@
 	return 0;
 }
 
-static void __br_clear_frame_hook(void)
-{
-	br_handle_frame_hook = NULL;
-}
-
 static void __br_clear_ioctl_hook(void)
 {
 	br_ioctl_hook = NULL;
@@ -69,7 +65,11 @@
 {
 	unregister_netdevice_notifier(&br_device_notifier);
 	br_call_ioctl_atomic(__br_clear_ioctl_hook);
-	net_call_rx_atomic(__br_clear_frame_hook);
+
+	br_write_lock_bh(BR_NETPROTO_LOCK);
+	br_handle_frame_hook = NULL;
+	br_write_unlock_bh(BR_NETPROTO_LOCK);
+
 #if defined(CONFIG_ATM_LANE) || defined(CONFIG_ATM_LANE_MODULE)
 	br_fdb_get_hook = NULL;
 	br_fdb_put_hook = NULL;
diff -Nru a/net/bridge/br_if.c b/net/bridge/br_if.c
--- a/net/bridge/br_if.c	Mon Oct  7 11:25:39 2002
+++ b/net/bridge/br_if.c	Mon Oct  7 11:25:39 2002
@@ -18,6 +18,7 @@
 #include <linux/if_bridge.h>
 #include <linux/inetdevice.h>
 #include <linux/rtnetlink.h>
+#include <linux/brlock.h>
 #include <asm/uaccess.h>
 #include "br_private.h"
 
@@ -37,7 +38,7 @@
 	return 100;
 }
 
-/* called under bridge lock */
+/* called under BR_NETPROTO_LOCK and bridge lock */
 static int __br_del_if(struct net_bridge *br, struct net_device *dev)
 {
 	struct net_bridge_port *p;
@@ -86,10 +87,12 @@
 
 static void del_ifs(struct net_bridge *br)
 {
-	write_lock_bh(&br->lock);
+	br_write_lock_bh(BR_NETPROTO_LOCK);
+	write_lock(&br->lock);
 	while (br->port_list != NULL)
 		__br_del_if(br, br->port_list->dev);
-	write_unlock_bh(&br->lock);
+	write_unlock(&br->lock);
+	br_write_unlock_bh(BR_NETPROTO_LOCK);
 }
 
 static struct net_bridge *new_nb(char *name)
@@ -252,10 +255,12 @@
 {
 	int retval;
 
-	write_lock_bh(&br->lock);
+	br_write_lock_bh(BR_NETPROTO_LOCK);
+	write_lock(&br->lock);
 	retval = __br_del_if(br, dev);
 	br_stp_recalculate_bridge_id(br);
-	write_unlock_bh(&br->lock);
+	write_unlock(&br->lock);
+	br_write_unlock_bh(BR_NETPROTO_LOCK);
 
 	return retval;
 }
diff -Nru a/net/bridge/br_stp.c b/net/bridge/br_stp.c
--- a/net/bridge/br_stp.c	Mon Oct  7 11:25:27 2002
+++ b/net/bridge/br_stp.c	Mon Oct  7 11:25:27 2002
@@ -369,10 +369,19 @@
 static void br_make_forwarding(struct net_bridge_port *p)
 {
 	if (p->state == BR_STATE_BLOCKING) {
-		printk(KERN_INFO "%s: port %i(%s) entering %s state\n",
-		       p->br->dev.name, p->port_no, p->dev->name, "listening");
+		if (p->br->stp_enabled) {
+			printk(KERN_INFO "%s: port %i(%s) entering %s state\n",
+			       p->br->dev.name, p->port_no, p->dev->name,
+			       "listening");
 
-		p->state = BR_STATE_LISTENING;
+			p->state = BR_STATE_LISTENING;
+		} else {
+			printk(KERN_INFO "%s: port %i(%s) entering %s state\n",
+			       p->br->dev.name, p->port_no, p->dev->name,
+			       "learning");
+
+			p->state = BR_STATE_LEARNING;
+		}
 		br_timer_set(&p->forward_delay_timer, jiffies);
 	}
 }
diff -Nru a/net/core/dev.c b/net/core/dev.c
--- a/net/core/dev.c	Mon Oct  7 11:25:35 2002
+++ b/net/core/dev.c	Mon Oct  7 11:25:35 2002
@@ -904,8 +904,8 @@
 			if (skb2->nh.raw < skb2->data ||
 			    skb2->nh.raw > skb2->tail) {
 				if (net_ratelimit())
-					printk(KERN_DEBUG "protocol %04x is "
-							  "buggy, dev %s\n",
+					printk(KERN_CRIT "protocol %04x is "
+					       "buggy, dev %s\n",
 					       skb2->protocol, dev->name);
 				skb2->nh.raw = skb2->data;
 			}
@@ -1062,14 +1062,14 @@
 			dev->xmit_lock_owner = -1;
 			spin_unlock_bh(&dev->xmit_lock);
 			if (net_ratelimit())
-				printk(KERN_DEBUG "Virtual device %s asks to "
+				printk(KERN_CRIT "Virtual device %s asks to "
 				       "queue packet!\n", dev->name);
 			goto out_enetdown;
 		} else {
 			/* Recursion is detected! It is possible,
 			 * unfortunately */
 			if (net_ratelimit())
-				printk(KERN_DEBUG "Dead loop on virtual device "
+				printk(KERN_CRIT "Dead loop on virtual device "
 				       "%s, fix it urgently!\n", dev->name);
 		}
 	}
@@ -1372,20 +1372,6 @@
 			}
 		}
 	}
-}
-
-/**
- *	net_call_rx_atomic
- *	@fn: function to call
- *
- *	Make a function call that is atomic with respect to the protocol
- *	layers.
- */
-void net_call_rx_atomic(void (*fn)(void))
-{
-	br_write_lock_bh(BR_NETPROTO_LOCK);
-	fn();
-	br_write_unlock_bh(BR_NETPROTO_LOCK);
 }
 
 #if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
diff -Nru a/net/core/profile.c b/net/core/profile.c
--- a/net/core/profile.c	Mon Oct  7 11:25:39 2002
+++ b/net/core/profile.c	Mon Oct  7 11:25:39 2002
@@ -35,7 +35,7 @@
 static void alpha_tick(unsigned long);
 
 static struct timer_list alpha_timer =
-	{ NULL, NULL, 0, 0L, alpha_tick };
+	{ .function = alpha_tick };
 
 void alpha_tick(unsigned long dummy)
 {
@@ -158,7 +158,7 @@
 int whitehole_init(struct net_device *dev);
 
 static struct timer_list whitehole_timer =
-	{ NULL, NULL, 0, 0L, whitehole_inject };
+	{ .function = whitehole_inject };
 
 static struct net_device whitehole_dev = {
 	"whitehole", 0x0, 0x0, 0x0, 0x0, 0, 0, 0, 0, 0, NULL, whitehole_init, };
diff -Nru a/net/econet/af_econet.c b/net/econet/af_econet.c
--- a/net/econet/af_econet.c	Mon Oct  7 11:25:35 2002
+++ b/net/econet/af_econet.c	Mon Oct  7 11:25:35 2002
@@ -643,51 +643,19 @@
 static int econet_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
 {
 	struct sock *sk = sock->sk;
-	int pid;
 
-	switch(cmd) 
-	{
-		case FIOSETOWN:
-		case SIOCSPGRP:
-			if (get_user(pid, (int *) arg))
-				return -EFAULT; 
-			return f_setown(sock->file, pid, 1);
-		case FIOGETOWN:
-		case SIOCGPGRP:
-			return put_user(sock->file->f_owner.pid, (int *)arg);
+	switch(cmd) {
 		case SIOCGSTAMP:
 			if(sk->stamp.tv_sec==0)
 				return -ENOENT;
 			return copy_to_user((void *)arg, &sk->stamp, sizeof(struct timeval)) ? -EFAULT : 0;
-		case SIOCGIFFLAGS:
-		case SIOCSIFFLAGS:
-		case SIOCGIFCONF:
-		case SIOCGIFMETRIC:
-		case SIOCSIFMETRIC:
-		case SIOCGIFMEM:
-		case SIOCSIFMEM:
-		case SIOCGIFMTU:
-		case SIOCSIFMTU:
-		case SIOCSIFLINK:
-		case SIOCGIFHWADDR:
-		case SIOCSIFHWADDR:
-		case SIOCSIFMAP:
-		case SIOCGIFMAP:
-		case SIOCSIFSLAVE:
-		case SIOCGIFSLAVE:
-		case SIOCGIFINDEX:
-		case SIOCGIFNAME:
-		case SIOCGIFCOUNT:
-		case SIOCSIFHWBROADCAST:
-			return(dev_ioctl(cmd,(void *) arg));
-
 		case SIOCSIFADDR:
 		case SIOCGIFADDR:
 			return ec_dev_ioctl(sock, cmd, (void *)arg);
 			break;
 
 		default:
-			return(dev_ioctl(cmd,(void *) arg));
+			return dev_ioctl(cmd,(void *) arg);
 	}
 	/*NOTREACHED*/
 	return 0;
diff -Nru a/net/ethernet/eth.c b/net/ethernet/eth.c
--- a/net/ethernet/eth.c	Mon Oct  7 11:25:35 2002
+++ b/net/ethernet/eth.c	Mon Oct  7 11:25:35 2002
@@ -103,16 +103,16 @@
 	if (dev->flags & (IFF_LOOPBACK|IFF_NOARP)) 
 	{
 		memset(eth->h_dest, 0, dev->addr_len);
-		return(dev->hard_header_len);
+		return ETH_HLEN;
 	}
 	
 	if(daddr)
 	{
 		memcpy(eth->h_dest,daddr,dev->addr_len);
-		return dev->hard_header_len;
+		return ETH_HLEN;
 	}
 	
-	return -dev->hard_header_len;
+	return -ETH_HLEN;
 }
 
 
@@ -161,7 +161,7 @@
 	unsigned char *rawp;
 	
 	skb->mac.raw=skb->data;
-	skb_pull(skb,dev->hard_header_len);
+	skb_pull(skb,ETH_HLEN);
 	eth= skb->mac.ethernet;
 	
 	if(*eth->h_dest&1)
diff -Nru a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c
--- a/net/ipv4/af_inet.c	Mon Oct  7 11:25:26 2002
+++ b/net/ipv4/af_inet.c	Mon Oct  7 11:25:26 2002
@@ -117,9 +117,6 @@
 #ifdef CONFIG_NET_DIVERT
 #include <linux/divert.h>
 #endif /* CONFIG_NET_DIVERT */
-#if defined(CONFIG_NET_RADIO) || defined(CONFIG_NET_PCMCIA_RADIO)
-#include <linux/wireless.h>		/* Note : will define WIRELESS_EXT */
-#endif	/* CONFIG_NET_RADIO || CONFIG_NET_PCMCIA_RADIO */
 
 struct linux_mib net_statistics[NR_CPUS * 2];
 
@@ -850,20 +847,8 @@
 {
 	struct sock *sk = sock->sk;
 	int err = 0;
-	int pid;
 
 	switch (cmd) {
-		case FIOSETOWN:
-		case SIOCSPGRP:
-			if (get_user(pid, (int *)arg))
-				err = -EFAULT;
-			else
-				err = f_setown(sock->file, pid, 1);
-			break;
-		case FIOGETOWN:
-		case SIOCGPGRP:
-			err = put_user(sock->file->f_owner.pid, (int *)arg);
-			break;
 		case SIOCGSTAMP:
 			if (!sk->stamp.tv_sec)
 				err = -ENOENT;
@@ -949,15 +934,6 @@
 			err = -ENOPKG;
 			break;
 		default:
-			if (cmd >= SIOCDEVPRIVATE &&
-			    cmd <= (SIOCDEVPRIVATE + 15))
-				err = dev_ioctl(cmd, (void *)arg);
-			else
-#ifdef WIRELESS_EXT
-			if (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST)
-				err = dev_ioctl(cmd, (void *)arg);
-			else
-#endif	/* WIRELESS_EXT */
 			if (!sk->prot->ioctl ||
 			    (err = sk->prot->ioctl(sk, cmd, arg)) ==
 			    					-ENOIOCTLCMD)
diff -Nru a/net/ipv4/devinet.c b/net/ipv4/devinet.c
--- a/net/ipv4/devinet.c	Mon Oct  7 11:25:27 2002
+++ b/net/ipv4/devinet.c	Mon Oct  7 11:25:27 2002
@@ -984,16 +984,16 @@
 }
 
 static struct rtnetlink_link inet_rtnetlink_table[RTM_MAX - RTM_BASE + 1] = {
-	 [4] = { doit:	 inet_rtm_newaddr,  },
-	 [5] = { doit:	 inet_rtm_deladdr,  },
-	 [6] = { dumpit: inet_dump_ifaddr,  },
-	 [8] = { doit:	 inet_rtm_newroute, },
-	 [9] = { doit:	 inet_rtm_delroute, },
-	[10] = { doit:	 inet_rtm_getroute, dumpit: inet_dump_fib, },
+	 [4] = { .doit	 = inet_rtm_newaddr,  },
+	 [5] = { .doit	 = inet_rtm_deladdr,  },
+	 [6] = { .dumpit = inet_dump_ifaddr,  },
+	 [8] = { .doit	 = inet_rtm_newroute, },
+	 [9] = { .doit	 = inet_rtm_delroute, },
+	[10] = { .doit	 = inet_rtm_getroute, .dumpit = inet_dump_fib, },
 #ifdef CONFIG_IP_MULTIPLE_TABLES
-	[16] = { doit:	 inet_rtm_newrule, },
-	[17] = { doit:	 inet_rtm_delrule, },
-	[18] = { dumpit: inet_dump_rules,  },
+	[16] = { .doit	 = inet_rtm_newrule, },
+	[17] = { .doit	 = inet_rtm_delrule, },
+	[18] = { .dumpit = inet_dump_rules,  },
 #endif
 };
 
diff -Nru a/net/ipv4/igmp.c b/net/ipv4/igmp.c
--- a/net/ipv4/igmp.c	Mon Oct  7 11:25:39 2002
+++ b/net/ipv4/igmp.c	Mon Oct  7 11:25:39 2002
@@ -229,7 +229,7 @@
 	iph->version  = 4;
 	iph->ihl      = (sizeof(struct iphdr)+4)>>2;
 	iph->tos      = 0;
-	iph->frag_off = __constant_htons(IP_DF);
+	iph->frag_off = htons(IP_DF);
 	iph->ttl      = 1;
 	iph->daddr    = dst;
 	iph->saddr    = rt->rt_src;
diff -Nru a/net/ipv4/inetpeer.c b/net/ipv4/inetpeer.c
--- a/net/ipv4/inetpeer.c	Mon Oct  7 11:25:31 2002
+++ b/net/ipv4/inetpeer.c	Mon Oct  7 11:25:31 2002
@@ -98,7 +98,7 @@
 
 static void peer_check_expire(unsigned long dummy);
 static struct timer_list peer_periodic_timer =
-	{ { NULL, NULL }, 0, 0, &peer_check_expire };
+	{ .function = &peer_check_expire };
 
 /* Exported for sysctl_net_ipv4.  */
 int inet_peer_gc_mintime = 10 * HZ,
diff -Nru a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c
--- a/net/ipv4/ip_gre.c	Mon Oct  7 11:25:36 2002
+++ b/net/ipv4/ip_gre.c	Mon Oct  7 11:25:36 2002
@@ -126,7 +126,7 @@
 
 static struct ip_tunnel ipgre_fb_tunnel = {
 	.dev =	&ipgre_fb_tunnel_dev,
-	.parms ={ name: "gre0" }
+	.parms ={ .name = "gre0" }
 };
 
 /* Tunnel hash table */
@@ -414,7 +414,7 @@
 	struct sk_buff *skb2;
 	struct rtable *rt;
 
-	if (p[1] != __constant_htons(ETH_P_IP))
+	if (p[1] != htons(ETH_P_IP))
 		return;
 
 	flags = p[0];
@@ -537,10 +537,10 @@
 static inline void ipgre_ecn_decapsulate(struct iphdr *iph, struct sk_buff *skb)
 {
 	if (INET_ECN_is_ce(iph->tos)) {
-		if (skb->protocol == __constant_htons(ETH_P_IP)) {
+		if (skb->protocol == htons(ETH_P_IP)) {
 			if (INET_ECN_is_not_ce(skb->nh.iph->tos))
 				IP_ECN_set_ce(skb->nh.iph);
-		} else if (skb->protocol == __constant_htons(ETH_P_IPV6)) {
+		} else if (skb->protocol == htons(ETH_P_IPV6)) {
 			if (INET_ECN_is_not_ce(ip6_get_dsfield(skb->nh.ipv6h)))
 				IP6_ECN_set_ce(skb->nh.ipv6h);
 		}
@@ -551,9 +551,9 @@
 ipgre_ecn_encapsulate(u8 tos, struct iphdr *old_iph, struct sk_buff *skb)
 {
 	u8 inner = 0;
-	if (skb->protocol == __constant_htons(ETH_P_IP))
+	if (skb->protocol == htons(ETH_P_IP))
 		inner = old_iph->tos;
-	else if (skb->protocol == __constant_htons(ETH_P_IPV6))
+	else if (skb->protocol == htons(ETH_P_IPV6))
 		inner = ip6_get_dsfield((struct ipv6hdr*)old_iph);
 	return INET_ECN_encapsulate(tos, inner);
 }
@@ -710,13 +710,13 @@
 			goto tx_error;
 		}
 
-		if (skb->protocol == __constant_htons(ETH_P_IP)) {
+		if (skb->protocol == htons(ETH_P_IP)) {
 			rt = (struct rtable*)skb->dst;
 			if ((dst = rt->rt_gateway) == 0)
 				goto tx_error_icmp;
 		}
 #ifdef CONFIG_IPV6
-		else if (skb->protocol == __constant_htons(ETH_P_IPV6)) {
+		else if (skb->protocol == htons(ETH_P_IPV6)) {
 			struct in6_addr *addr6;
 			int addr_type;
 			struct neighbour *neigh = skb->dst->neighbour;
@@ -744,7 +744,7 @@
 
 	tos = tiph->tos;
 	if (tos&1) {
-		if (skb->protocol == __constant_htons(ETH_P_IP))
+		if (skb->protocol == htons(ETH_P_IP))
 			tos = old_iph->tos;
 		tos &= ~1;
 	}
@@ -767,13 +767,13 @@
 	else
 		mtu = skb->dst ? skb->dst->pmtu : dev->mtu;
 
-	if (skb->protocol == __constant_htons(ETH_P_IP)) {
+	if (skb->protocol == htons(ETH_P_IP)) {
 		if (skb->dst && mtu < skb->dst->pmtu && mtu >= 68)
 			skb->dst->pmtu = mtu;
 
-		df |= (old_iph->frag_off&__constant_htons(IP_DF));
+		df |= (old_iph->frag_off&htons(IP_DF));
 
-		if ((old_iph->frag_off&__constant_htons(IP_DF)) &&
+		if ((old_iph->frag_off&htons(IP_DF)) &&
 		    mtu < ntohs(old_iph->tot_len)) {
 			icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, htonl(mtu));
 			ip_rt_put(rt);
@@ -781,7 +781,7 @@
 		}
 	}
 #ifdef CONFIG_IPV6
-	else if (skb->protocol == __constant_htons(ETH_P_IPV6)) {
+	else if (skb->protocol == htons(ETH_P_IPV6)) {
 		struct rt6_info *rt6 = (struct rt6_info*)skb->dst;
 
 		if (rt6 && mtu < rt6->u.dst.pmtu && mtu >= IPV6_MIN_MTU) {
@@ -847,10 +847,10 @@
 	iph->saddr		=	rt->rt_src;
 
 	if ((iph->ttl = tiph->ttl) == 0) {
-		if (skb->protocol == __constant_htons(ETH_P_IP))
+		if (skb->protocol == htons(ETH_P_IP))
 			iph->ttl = old_iph->ttl;
 #ifdef CONFIG_IPV6
-		else if (skb->protocol == __constant_htons(ETH_P_IPV6))
+		else if (skb->protocol == htons(ETH_P_IPV6))
 			iph->ttl = ((struct ipv6hdr*)old_iph)->hop_limit;
 #endif
 		else
@@ -938,11 +938,11 @@
 
 		err = -EINVAL;
 		if (p.iph.version != 4 || p.iph.protocol != IPPROTO_GRE ||
-		    p.iph.ihl != 5 || (p.iph.frag_off&__constant_htons(~IP_DF)) ||
+		    p.iph.ihl != 5 || (p.iph.frag_off&htons(~IP_DF)) ||
 		    ((p.i_flags|p.o_flags)&(GRE_VERSION|GRE_ROUTING)))
 			goto done;
 		if (p.iph.ttl)
-			p.iph.frag_off |= __constant_htons(IP_DF);
+			p.iph.frag_off |= htons(IP_DF);
 
 		if (!(p.i_flags&GRE_KEY))
 			p.i_key = 0;
diff -Nru a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c
--- a/net/ipv4/ip_output.c	Mon Oct  7 11:25:39 2002
+++ b/net/ipv4/ip_output.c	Mon Oct  7 11:25:39 2002
@@ -136,7 +136,7 @@
 	iph->ihl      = 5;
 	iph->tos      = inet->tos;
 	if (ip_dont_fragment(sk, &rt->u.dst))
-		iph->frag_off = __constant_htons(IP_DF);
+		iph->frag_off = htons(IP_DF);
 	else
 		iph->frag_off = 0;
 	iph->ttl      = inet->ttl;
@@ -187,7 +187,7 @@
 	struct net_device *dev = skb->dst->dev;
 
 	skb->dev = dev;
-	skb->protocol = __constant_htons(ETH_P_IP);
+	skb->protocol = htons(ETH_P_IP);
 
 	return NF_HOOK(PF_INET, NF_IP_POST_ROUTING, skb, NULL, dev,
 		       ip_finish_output2);
@@ -209,7 +209,7 @@
 #endif
 
 	skb->dev = dev;
-	skb->protocol = __constant_htons(ETH_P_IP);
+	skb->protocol = htons(ETH_P_IP);
 
 	/*
 	 *	Multicasts are looped back for other local users
@@ -394,7 +394,7 @@
 	*((__u16 *)iph)	= htons((4 << 12) | (5 << 8) | (inet->tos & 0xff));
 	iph->tot_len = htons(skb->len);
 	if (ip_dont_fragment(sk, &rt->u.dst))
-		iph->frag_off = __constant_htons(IP_DF);
+		iph->frag_off = htons(IP_DF);
 	else
 		iph->frag_off = 0;
 	iph->ttl      = inet->ttl;
@@ -463,7 +463,7 @@
 
 	mtu = rt->u.dst.pmtu;
 	if (ip_dont_fragment(sk, &rt->u.dst))
-		df = __constant_htons(IP_DF);
+		df = htons(IP_DF);
 
 	length -= sizeof(struct iphdr);
 
@@ -594,7 +594,7 @@
 				/*
 				 *	Any further fragments will have MF set.
 				 */
-				mf = __constant_htons(IP_MF);
+				mf = htons(IP_MF);
 			}
 			if (rt->rt_type == RTN_MULTICAST)
 				iph->ttl = inet->mc_ttl;
@@ -693,7 +693,7 @@
 	 */
 	df = 0;
 	if (ip_dont_fragment(sk, &rt->u.dst))
-		df = __constant_htons(IP_DF);
+		df = htons(IP_DF);
 
 	/* 
 	 *	Fast path for unfragmented frames without options. 
@@ -797,7 +797,7 @@
 	 */
 
 	offset = (ntohs(iph->frag_off) & IP_OFFSET) << 3;
-	not_last_frag = iph->frag_off & __constant_htons(IP_MF);
+	not_last_frag = iph->frag_off & htons(IP_MF);
 
 	/*
 	 *	Keep copying data until we run out.
@@ -882,7 +882,7 @@
 		 *		   last fragment then keep MF on each bit
 		 */
 		if (left > 0 || not_last_frag)
-			iph->frag_off |= __constant_htons(IP_MF);
+			iph->frag_off |= htons(IP_MF);
 		ptr += len;
 		offset += len;
 
diff -Nru a/net/ipv4/ipconfig.c b/net/ipv4/ipconfig.c
--- a/net/ipv4/ipconfig.c	Mon Oct  7 11:25:25 2002
+++ b/net/ipv4/ipconfig.c	Mon Oct  7 11:25:25 2002
@@ -362,11 +362,11 @@
 
 	if (ic_netmask == INADDR_NONE) {
 		if (IN_CLASSA(ntohl(ic_myaddr)))
-			ic_netmask = __constant_htonl(IN_CLASSA_NET);
+			ic_netmask = htonl(IN_CLASSA_NET);
 		else if (IN_CLASSB(ntohl(ic_myaddr)))
-			ic_netmask = __constant_htonl(IN_CLASSB_NET);
+			ic_netmask = htonl(IN_CLASSB_NET);
 		else if (IN_CLASSC(ntohl(ic_myaddr)))
-			ic_netmask = __constant_htonl(IN_CLASSC_NET);
+			ic_netmask = htonl(IN_CLASSC_NET);
 		else {
 			printk(KERN_ERR "IP-Config: Unable to guess netmask for address %u.%u.%u.%u\n",
 				NIPQUAD(ic_myaddr));
@@ -432,11 +432,11 @@
 		goto drop;
 
 	/* If it's not a RARP reply, delete it. */
-	if (rarp->ar_op != __constant_htons(ARPOP_RREPLY))
+	if (rarp->ar_op != htons(ARPOP_RREPLY))
 		goto drop;
 
 	/* If it's not Ethernet, delete it. */
-	if (rarp->ar_pro != __constant_htons(ETH_P_IP))
+	if (rarp->ar_pro != htons(ETH_P_IP))
 		goto drop;
 
 	/* Extract variable-width fields */
@@ -672,15 +672,15 @@
 	h->version = 4;
 	h->ihl = 5;
 	h->tot_len = htons(sizeof(struct bootp_pkt));
-	h->frag_off = __constant_htons(IP_DF);
+	h->frag_off = htons(IP_DF);
 	h->ttl = 64;
 	h->protocol = IPPROTO_UDP;
 	h->daddr = INADDR_BROADCAST;
 	h->check = ip_fast_csum((unsigned char *) h, h->ihl);
 
 	/* Construct UDP header */
-	b->udph.source = __constant_htons(68);
-	b->udph.dest = __constant_htons(67);
+	b->udph.source = htons(68);
+	b->udph.dest = htons(67);
 	b->udph.len = htons(sizeof(struct bootp_pkt) - sizeof(struct iphdr));
 	/* UDP checksum not calculated -- explicitly allowed in BOOTP RFC */
 
@@ -711,7 +711,7 @@
 
 	/* Chain packet down the line... */
 	skb->dev = dev;
-	skb->protocol = __constant_htons(ETH_P_IP);
+	skb->protocol = htons(ETH_P_IP);
 	if ((dev->hard_header &&
 	     dev->hard_header(skb, dev, ntohs(skb->protocol), dev->broadcast, dev->dev_addr, skb->len) < 0) ||
 	    dev_queue_xmit(skb) < 0)
@@ -819,13 +819,13 @@
 	    ip_fast_csum((char *) h, h->ihl) != 0 ||
 	    skb->len < ntohs(h->tot_len) ||
 	    h->protocol != IPPROTO_UDP ||
-	    b->udph.source != __constant_htons(67) ||
-	    b->udph.dest != __constant_htons(68) ||
+	    b->udph.source != htons(67) ||
+	    b->udph.dest != htons(68) ||
 	    ntohs(h->tot_len) < ntohs(b->udph.len) + sizeof(struct iphdr))
 		goto drop;
 
 	/* Fragments are not supported */
-	if (h->frag_off & __constant_htons(IP_OFFSET | IP_MF)) {
+	if (h->frag_off & htons(IP_OFFSET | IP_MF)) {
 		printk(KERN_ERR "DHCP/BOOTP: Ignoring fragmented reply.\n");
 		goto drop;
 	}
diff -Nru a/net/ipv4/ipip.c b/net/ipv4/ipip.c
--- a/net/ipv4/ipip.c	Mon Oct  7 11:25:40 2002
+++ b/net/ipv4/ipip.c	Mon Oct  7 11:25:40 2002
@@ -129,7 +129,7 @@
 
 static struct ip_tunnel ipip_fb_tunnel = {
 	.dev =	&ipip_fb_tunnel_dev,
-	.parms ={ name:	"tunl0", }
+	.parms ={ .name	= "tunl0", }
 };
 
 static struct ip_tunnel *tunnels_r_l[HASH_SIZE];
@@ -483,7 +483,7 @@
 	skb->mac.raw = skb->nh.raw;
 	skb->nh.raw = skb->data;
 	memset(&(IPCB(skb)->opt), 0, sizeof(struct ip_options));
-	skb->protocol = __constant_htons(ETH_P_IP);
+	skb->protocol = htons(ETH_P_IP);
 	skb->pkt_type = PACKET_HOST;
 
 	read_lock(&ipip_lock);
@@ -544,7 +544,7 @@
 		goto tx_error;
 	}
 
-	if (skb->protocol != __constant_htons(ETH_P_IP))
+	if (skb->protocol != htons(ETH_P_IP))
 		goto tx_error;
 
 	if (tos&1)
@@ -585,9 +585,9 @@
 	if (skb->dst && mtu < skb->dst->pmtu)
 		skb->dst->pmtu = mtu;
 
-	df |= (old_iph->frag_off&__constant_htons(IP_DF));
+	df |= (old_iph->frag_off&htons(IP_DF));
 
-	if ((old_iph->frag_off&__constant_htons(IP_DF)) && mtu < ntohs(old_iph->tot_len)) {
+	if ((old_iph->frag_off&htons(IP_DF)) && mtu < ntohs(old_iph->tot_len)) {
 		icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, htonl(mtu));
 		ip_rt_put(rt);
 		goto tx_error;
@@ -703,10 +703,10 @@
 
 		err = -EINVAL;
 		if (p.iph.version != 4 || p.iph.protocol != IPPROTO_IPIP ||
-		    p.iph.ihl != 5 || (p.iph.frag_off&__constant_htons(~IP_DF)))
+		    p.iph.ihl != 5 || (p.iph.frag_off&htons(~IP_DF)))
 			goto done;
 		if (p.iph.ttl)
-			p.iph.frag_off |= __constant_htons(IP_DF);
+			p.iph.frag_off |= htons(IP_DF);
 
 		t = ipip_tunnel_locate(&p, cmd == SIOCADDTUNNEL);
 
diff -Nru a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c
--- a/net/ipv4/ipmr.c	Mon Oct  7 11:25:35 2002
+++ b/net/ipv4/ipmr.c	Mon Oct  7 11:25:35 2002
@@ -1434,7 +1434,7 @@
 	skb->nh.iph = (struct iphdr *)skb->data;
 	skb->dev = reg_dev;
 	memset(&(IPCB(skb)->opt), 0, sizeof(struct ip_options));
-	skb->protocol = __constant_htons(ETH_P_IP);
+	skb->protocol = htons(ETH_P_IP);
 	skb->ip_summed = 0;
 	skb->pkt_type = PACKET_HOST;
 	dst_release(skb->dst);
@@ -1501,7 +1501,7 @@
 	skb->nh.iph = (struct iphdr *)skb->data;
 	skb->dev = reg_dev;
 	memset(&(IPCB(skb)->opt), 0, sizeof(struct ip_options));
-	skb->protocol = __constant_htons(ETH_P_IP);
+	skb->protocol = htons(ETH_P_IP);
 	skb->ip_summed = 0;
 	skb->pkt_type = PACKET_HOST;
 	dst_release(skb->dst);
diff -Nru a/net/ipv4/netfilter/ip_nat_core.c b/net/ipv4/netfilter/ip_nat_core.c
--- a/net/ipv4/netfilter/ip_nat_core.c	Mon Oct  7 11:25:26 2002
+++ b/net/ipv4/netfilter/ip_nat_core.c	Mon Oct  7 11:25:26 2002
@@ -804,7 +804,7 @@
 
 		/* Always defragged for helpers */
 		IP_NF_ASSERT(!((*pskb)->nh.iph->frag_off
-			       & __constant_htons(IP_MF|IP_OFFSET)));
+			       & htons(IP_MF|IP_OFFSET)));
 
 		/* Have to grab read lock before sibling_list traversal */
 		READ_LOCK(&ip_conntrack_lock);
diff -Nru a/net/ipv4/netfilter/ip_nat_snmp_basic.c b/net/ipv4/netfilter/ip_nat_snmp_basic.c
--- a/net/ipv4/netfilter/ip_nat_snmp_basic.c	Mon Oct  7 11:25:37 2002
+++ b/net/ipv4/netfilter/ip_nat_snmp_basic.c	Mon Oct  7 11:25:37 2002
@@ -1260,9 +1260,9 @@
 	 * on post routing (SNAT).
 	 */
 	if (!((dir == IP_CT_DIR_REPLY && hooknum == NF_IP_PRE_ROUTING &&
-			udph->source == __constant_ntohs(SNMP_PORT)) ||
+			udph->source == ntohs(SNMP_PORT)) ||
 	      (dir == IP_CT_DIR_ORIGINAL && hooknum == NF_IP_POST_ROUTING &&
-	      		udph->dest == __constant_ntohs(SNMP_TRAP_PORT)))) {
+	      		udph->dest == ntohs(SNMP_TRAP_PORT)))) {
 		spin_unlock_bh(&snmp_lock);
 		return NF_ACCEPT;
 	}
diff -Nru a/net/ipv4/netfilter/ip_nat_standalone.c b/net/ipv4/netfilter/ip_nat_standalone.c
--- a/net/ipv4/netfilter/ip_nat_standalone.c	Mon Oct  7 11:25:28 2002
+++ b/net/ipv4/netfilter/ip_nat_standalone.c	Mon Oct  7 11:25:28 2002
@@ -75,7 +75,7 @@
 	/* We never see fragments: conntrack defrags on pre-routing
 	   and local-out, and ip_nat_out protects post-routing. */
 	IP_NF_ASSERT(!((*pskb)->nh.iph->frag_off
-		       & __constant_htons(IP_MF|IP_OFFSET)));
+		       & htons(IP_MF|IP_OFFSET)));
 
 	(*pskb)->nfcache |= NFC_UNKNOWN;
 
@@ -186,7 +186,7 @@
 
 	   I'm starting to have nightmares about fragments.  */
 
-	if ((*pskb)->nh.iph->frag_off & __constant_htons(IP_MF|IP_OFFSET)) {
+	if ((*pskb)->nh.iph->frag_off & htons(IP_MF|IP_OFFSET)) {
 		*pskb = ip_ct_gather_frags(*pskb);
 
 		if (!*pskb)
diff -Nru a/net/ipv4/route.c b/net/ipv4/route.c
--- a/net/ipv4/route.c	Mon Oct  7 11:25:39 2002
+++ b/net/ipv4/route.c	Mon Oct  7 11:25:39 2002
@@ -1244,7 +1244,7 @@
 		return -EINVAL;
 
 	if (MULTICAST(saddr) || BADCLASS(saddr) || LOOPBACK(saddr) ||
-	    skb->protocol != __constant_htons(ETH_P_IP))
+	    skb->protocol != htons(ETH_P_IP))
 		goto e_inval;
 
 	if (ZERONET(saddr)) {
@@ -1455,7 +1455,7 @@
 	     inet_addr_onlink(out_dev, saddr, FIB_RES_GW(res))))
 		flags |= RTCF_DOREDIRECT;
 
-	if (skb->protocol != __constant_htons(ETH_P_IP)) {
+	if (skb->protocol != htons(ETH_P_IP)) {
 		/* Not IP (i.e. ARP). Do not create route, if it is
 		 * invalid for proxy arp. DNAT routes are always valid.
 		 */
@@ -1520,7 +1520,7 @@
 out:	return err;
 
 brd_input:
-	if (skb->protocol != __constant_htons(ETH_P_IP))
+	if (skb->protocol != htons(ETH_P_IP))
 		goto e_inval;
 
 	if (ZERONET(saddr))
@@ -2154,7 +2154,7 @@
 		err = -ENODEV;
 		if (!dev)
 			goto out;
-		skb->protocol	= __constant_htons(ETH_P_IP);
+		skb->protocol	= htons(ETH_P_IP);
 		skb->dev	= dev;
 		local_bh_disable();
 		err = ip_route_input(skb, dst, src, rtm->rtm_tos, dev);
diff -Nru a/net/ipv4/tcp_diag.c b/net/ipv4/tcp_diag.c
--- a/net/ipv4/tcp_diag.c	Mon Oct  7 11:25:25 2002
+++ b/net/ipv4/tcp_diag.c	Mon Oct  7 11:25:25 2002
@@ -360,7 +360,7 @@
 				break;
 			if (sk->family == AF_INET6 && cond->family == AF_INET) {
 				if (addr[0] == 0 && addr[1] == 0 &&
-				    addr[2] == __constant_htonl(0xffff) &&
+				    addr[2] == htonl(0xffff) &&
 				    bitstring_match(addr+3, cond->addr, cond->prefix_len))
 					break;
 			}
diff -Nru a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
--- a/net/ipv4/tcp_input.c	Mon Oct  7 11:25:26 2002
+++ b/net/ipv4/tcp_input.c	Mon Oct  7 11:25:26 2002
@@ -2103,8 +2103,8 @@
 	} else if (tp->tstamp_ok &&
 		   th->doff == (sizeof(struct tcphdr)>>2)+(TCPOLEN_TSTAMP_ALIGNED>>2)) {
 		__u32 *ptr = (__u32 *)(th + 1);
-		if (*ptr == __constant_ntohl((TCPOPT_NOP << 24) | (TCPOPT_NOP << 16)
-					     | (TCPOPT_TIMESTAMP << 8) | TCPOLEN_TIMESTAMP)) {
+		if (*ptr == ntohl((TCPOPT_NOP << 24) | (TCPOPT_NOP << 16)
+				  | (TCPOPT_TIMESTAMP << 8) | TCPOLEN_TIMESTAMP)) {
 			tp->saw_tstamp = 1;
 			++ptr;
 			tp->rcv_tsval = ntohl(*ptr);
@@ -3275,8 +3275,8 @@
 			__u32 *ptr = (__u32 *)(th + 1);
 
 			/* No? Slow path! */
-			if (*ptr != __constant_ntohl((TCPOPT_NOP << 24) | (TCPOPT_NOP << 16)
-						     | (TCPOPT_TIMESTAMP << 8) | TCPOLEN_TIMESTAMP))
+			if (*ptr != ntohl((TCPOPT_NOP << 24) | (TCPOPT_NOP << 16)
+					  | (TCPOPT_TIMESTAMP << 8) | TCPOLEN_TIMESTAMP))
 				goto slow_path;
 
 			tp->saw_tstamp = 1;
diff -Nru a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
--- a/net/ipv4/tcp_ipv4.c	Mon Oct  7 11:25:29 2002
+++ b/net/ipv4/tcp_ipv4.c	Mon Oct  7 11:25:29 2002
@@ -81,8 +81,8 @@
 struct tcp_hashinfo __cacheline_aligned tcp_hashinfo = {
 	.__tcp_lhash_lock =   RW_LOCK_UNLOCKED,
 	.__tcp_lhash_users =  ATOMIC_INIT(0),
-	__tcp_lhash_wait:
-	  __WAIT_QUEUE_HEAD_INITIALIZER(tcp_hashinfo.__tcp_lhash_wait),
+	.__tcp_lhash_wait
+	  = __WAIT_QUEUE_HEAD_INITIALIZER(tcp_hashinfo.__tcp_lhash_wait),
 	.__tcp_portalloc_lock = SPIN_LOCK_UNLOCKED
 };
 
diff -Nru a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c
--- a/net/ipv4/tcp_minisocks.c	Mon Oct  7 11:25:27 2002
+++ b/net/ipv4/tcp_minisocks.c	Mon Oct  7 11:25:27 2002
@@ -428,7 +428,7 @@
 
 static struct tcp_tw_bucket *tcp_tw_death_row[TCP_TWKILL_SLOTS];
 static spinlock_t tw_death_lock = SPIN_LOCK_UNLOCKED;
-static struct timer_list tcp_tw_timer = { function: tcp_twkill };
+static struct timer_list tcp_tw_timer = { .function = tcp_twkill };
 
 static void SMP_TIMER_NAME(tcp_twkill)(unsigned long dummy)
 {
@@ -495,7 +495,7 @@
 static int tcp_twcal_hand = -1;
 static int tcp_twcal_jiffie;
 static void tcp_twcal_tick(unsigned long);
-static struct timer_list tcp_twcal_timer = {function: tcp_twcal_tick};
+static struct timer_list tcp_twcal_timer = {.function = tcp_twcal_tick};
 static struct tcp_tw_bucket *tcp_twcal_row[TCP_TW_RECYCLE_SLOTS];
 
 void tcp_tw_schedule(struct tcp_tw_bucket *tw, int timeo)
diff -Nru a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
--- a/net/ipv6/addrconf.c	Mon Oct  7 11:25:39 2002
+++ b/net/ipv6/addrconf.c	Mon Oct  7 11:25:39 2002
@@ -94,7 +94,7 @@
 
 static void addrconf_verify(unsigned long);
 
-static struct timer_list addr_chk_timer = { function: addrconf_verify };
+static struct timer_list addr_chk_timer = { .function = addrconf_verify };
 static spinlock_t addrconf_verify_lock = SPIN_LOCK_UNLOCKED;
 
 static int addrconf_ifdown(struct net_device *dev, int how);
@@ -144,47 +144,47 @@
 	/* Consider all addresses with the first three bits different of
 	   000 and 111 as unicasts.
 	 */
-	if ((st & __constant_htonl(0xE0000000)) != __constant_htonl(0x00000000) &&
-	    (st & __constant_htonl(0xE0000000)) != __constant_htonl(0xE0000000))
+	if ((st & htonl(0xE0000000)) != htonl(0x00000000) &&
+	    (st & htonl(0xE0000000)) != htonl(0xE0000000))
 		return IPV6_ADDR_UNICAST;
 
-	if ((st & __constant_htonl(0xFF000000)) == __constant_htonl(0xFF000000)) {
+	if ((st & htonl(0xFF000000)) == htonl(0xFF000000)) {
 		int type = IPV6_ADDR_MULTICAST;
 
-		switch((st & __constant_htonl(0x00FF0000))) {
-			case __constant_htonl(0x00010000):
+		switch((st & htonl(0x00FF0000))) {
+			case htonl(0x00010000):
 				type |= IPV6_ADDR_LOOPBACK;
 				break;
 
-			case __constant_htonl(0x00020000):
+			case htonl(0x00020000):
 				type |= IPV6_ADDR_LINKLOCAL;
 				break;
 
-			case __constant_htonl(0x00050000):
+			case htonl(0x00050000):
 				type |= IPV6_ADDR_SITELOCAL;
 				break;
 		};
 		return type;
 	}
 	
-	if ((st & __constant_htonl(0xFFC00000)) == __constant_htonl(0xFE800000))
+	if ((st & htonl(0xFFC00000)) == htonl(0xFE800000))
 		return (IPV6_ADDR_LINKLOCAL | IPV6_ADDR_UNICAST);
 
-	if ((st & __constant_htonl(0xFFC00000)) == __constant_htonl(0xFEC00000))
+	if ((st & htonl(0xFFC00000)) == htonl(0xFEC00000))
 		return (IPV6_ADDR_SITELOCAL | IPV6_ADDR_UNICAST);
 
 	if ((addr->s6_addr32[0] | addr->s6_addr32[1]) == 0) {
 		if (addr->s6_addr32[2] == 0) {
-			if (addr->in6_u.u6_addr32[3] == 0)
+			if (addr->s6_addr32[3] == 0)
 				return IPV6_ADDR_ANY;
 
-			if (addr->s6_addr32[3] == __constant_htonl(0x00000001))
+			if (addr->s6_addr32[3] == htonl(0x00000001))
 				return (IPV6_ADDR_LOOPBACK | IPV6_ADDR_UNICAST);
 
 			return (IPV6_ADDR_COMPATv4 | IPV6_ADDR_UNICAST);
 		}
 
-		if (addr->s6_addr32[2] == __constant_htonl(0x0000ffff))
+		if (addr->s6_addr32[2] == htonl(0x0000ffff))
 			return IPV6_ADDR_MAPPED;
 	}
 
@@ -755,7 +755,7 @@
 
 	memset(&rtmsg, 0, sizeof(rtmsg));
 	ipv6_addr_set(&rtmsg.rtmsg_dst,
-		      __constant_htonl(0xFF000000), 0, 0, 0);
+		      htonl(0xFF000000), 0, 0, 0);
 	rtmsg.rtmsg_dst_len = 8;
 	rtmsg.rtmsg_metric = IP6_RT_PRIO_ADDRCONF;
 	rtmsg.rtmsg_ifindex = dev->ifindex;
@@ -785,7 +785,7 @@
 {
 	struct in6_addr addr;
 
-	ipv6_addr_set(&addr,  __constant_htonl(0xFE800000), 0, 0, 0);
+	ipv6_addr_set(&addr,  htonl(0xFE800000), 0, 0, 0);
 	addrconf_prefix_route(&addr, 10, dev, 0, RTF_ADDRCONF);
 }
 
@@ -1123,7 +1123,7 @@
 	memcpy(&addr.s6_addr32[3], idev->dev->dev_addr, 4);
 
 	if (idev->dev->flags&IFF_POINTOPOINT) {
-		addr.s6_addr32[0] = __constant_htonl(0xfe800000);
+		addr.s6_addr32[0] = htonl(0xfe800000);
 		scope = IFA_LINK;
 	} else {
 		scope = IPV6_ADDR_COMPATv4;
@@ -1237,9 +1237,7 @@
 		return;
 
 	memset(&addr, 0, sizeof(struct in6_addr));
-
-	addr.s6_addr[0] = 0xFE;
-	addr.s6_addr[1] = 0x80;
+	addr.s6_addr32[0] = htonl(0xFE800000);
 
 	if (ipv6_generate_eui64(addr.s6_addr + 8, dev) == 0)
 		addrconf_add_linklocal(idev, &addr);
diff -Nru a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c
--- a/net/ipv6/af_inet6.c	Mon Oct  7 11:25:35 2002
+++ b/net/ipv6/af_inet6.c	Mon Oct  7 11:25:35 2002
@@ -455,18 +455,9 @@
 {
 	struct sock *sk = sock->sk;
 	int err = -EINVAL;
-	int pid;
 
 	switch(cmd) 
 	{
-	case FIOSETOWN:
-	case SIOCSPGRP:
-		if (get_user(pid, (int *) arg))
-			return -EFAULT;
-		return f_setown(sock->file, pid, 1);
-	case FIOGETOWN:
-	case SIOCGPGRP:
-		return put_user(sock->file->f_owner.pid, (int *)arg);
 	case SIOCGSTAMP:
 		if(sk->stamp.tv_sec==0)
 			return -ENOENT;
@@ -488,10 +479,6 @@
 	case SIOCSIFDSTADDR:
 		return addrconf_set_dstaddr((void *) arg);
 	default:
-		if ((cmd >= SIOCDEVPRIVATE) &&
-		    (cmd <= (SIOCDEVPRIVATE + 15)))
-			return(dev_ioctl(cmd,(void *) arg));
-		
 		if(sk->prot->ioctl==0 || (err=sk->prot->ioctl(sk, cmd, arg))==-ENOIOCTLCMD)
 			return(dev_ioctl(cmd,(void *) arg));		
 		return err;
diff -Nru a/net/ipv6/datagram.c b/net/ipv6/datagram.c
--- a/net/ipv6/datagram.c	Mon Oct  7 11:25:25 2002
+++ b/net/ipv6/datagram.c	Mon Oct  7 11:25:25 2002
@@ -150,7 +150,7 @@
 			}
 		} else {
 			ipv6_addr_set(&sin->sin6_addr, 0, 0,
-				      __constant_htonl(0xffff),
+				      htonl(0xffff),
 				      *(u32*)(skb->nh.raw + serr->addr_offset));
 		}
 	}
@@ -173,7 +173,7 @@
 			struct inet_opt *inet = inet_sk(sk);
 
 			ipv6_addr_set(&sin->sin6_addr, 0, 0,
-				      __constant_htonl(0xffff),
+				      htonl(0xffff),
 				      skb->nh.iph->saddr);
 			if (inet->cmsg_flags)
 				ip_cmsg_recv(msg, skb);
diff -Nru a/net/ipv6/icmp.c b/net/ipv6/icmp.c
--- a/net/ipv6/icmp.c	Mon Oct  7 11:25:29 2002
+++ b/net/ipv6/icmp.c	Mon Oct  7 11:25:29 2002
@@ -198,7 +198,7 @@
 		u8 type;
 		if (skb_copy_bits(skb, ptr+offsetof(struct icmp6hdr, icmp6_type),
 				  &type, 1)
-		    || !(type & 0x80))
+		    || !(type & ICMPV6_INFOMSG_MASK))
 			return 1;
 	}
 	return 0;
@@ -216,7 +216,7 @@
 	int res = 0;
 
 	/* Informational messages are not limited. */
-	if (type & 0x80)
+	if (type & ICMPV6_INFOMSG_MASK)
 		return 1;
 
 	/* Do not limit pmtu discovery, it would break it. */
@@ -519,22 +519,22 @@
 				    skb_checksum(skb, 0, skb->len, 0))) {
 			if (net_ratelimit())
 				printk(KERN_DEBUG "ICMPv6 checksum failed [%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x > %04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x]\n",
-				       ntohs(saddr->in6_u.u6_addr16[0]),
-				       ntohs(saddr->in6_u.u6_addr16[1]),
-				       ntohs(saddr->in6_u.u6_addr16[2]),
-				       ntohs(saddr->in6_u.u6_addr16[3]),
-				       ntohs(saddr->in6_u.u6_addr16[4]),
-				       ntohs(saddr->in6_u.u6_addr16[5]),
-				       ntohs(saddr->in6_u.u6_addr16[6]),
-				       ntohs(saddr->in6_u.u6_addr16[7]),
-				       ntohs(daddr->in6_u.u6_addr16[0]),
-				       ntohs(daddr->in6_u.u6_addr16[1]),
-				       ntohs(daddr->in6_u.u6_addr16[2]),
-				       ntohs(daddr->in6_u.u6_addr16[3]),
-				       ntohs(daddr->in6_u.u6_addr16[4]),
-				       ntohs(daddr->in6_u.u6_addr16[5]),
-				       ntohs(daddr->in6_u.u6_addr16[6]),
-				       ntohs(daddr->in6_u.u6_addr16[7]));
+				       ntohs(saddr->s6_addr16[0]),
+				       ntohs(saddr->s6_addr16[1]),
+				       ntohs(saddr->s6_addr16[2]),
+				       ntohs(saddr->s6_addr16[3]),
+				       ntohs(saddr->s6_addr16[4]),
+				       ntohs(saddr->s6_addr16[5]),
+				       ntohs(saddr->s6_addr16[6]),
+				       ntohs(saddr->s6_addr16[7]),
+				       ntohs(daddr->s6_addr16[0]),
+				       ntohs(daddr->s6_addr16[1]),
+				       ntohs(daddr->s6_addr16[2]),
+				       ntohs(daddr->s6_addr16[3]),
+				       ntohs(daddr->s6_addr16[4]),
+				       ntohs(daddr->s6_addr16[5]),
+				       ntohs(daddr->s6_addr16[6]),
+				       ntohs(daddr->s6_addr16[7]));
 			goto discard_it;
 		}
 	}
@@ -613,7 +613,7 @@
 			printk(KERN_DEBUG "icmpv6: msg of unkown type\n");
 
 		/* informational */
-		if (type & 0x80)
+		if (type & ICMPV6_INFOMSG_MASK)
 			break;
 
 		/* 
diff -Nru a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c
--- a/net/ipv6/ip6_fib.c	Mon Oct  7 11:25:28 2002
+++ b/net/ipv6/ip6_fib.c	Mon Oct  7 11:25:28 2002
@@ -93,7 +93,7 @@
 
 static __u32	rt_sernum	= 0;
 
-static struct timer_list ip6_fib_timer = { function: fib6_run_gc };
+static struct timer_list ip6_fib_timer = { .function = fib6_run_gc };
 
 static struct fib6_walker_t fib6_walker_list = {
 	&fib6_walker_list, &fib6_walker_list, 
diff -Nru a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
--- a/net/ipv6/ip6_output.c	Mon Oct  7 11:25:25 2002
+++ b/net/ipv6/ip6_output.c	Mon Oct  7 11:25:25 2002
@@ -102,7 +102,7 @@
 	struct dst_entry *dst = skb->dst;
 	struct net_device *dev = dst->dev;
 
-	skb->protocol = __constant_htons(ETH_P_IPV6);
+	skb->protocol = htons(ETH_P_IPV6);
 	skb->dev = dev;
 
 	if (ipv6_addr_is_multicast(&skb->nh.ipv6h->daddr)) {
@@ -223,7 +223,7 @@
 	 *	Fill in the IPv6 header
 	 */
 
-	*(u32*)hdr = __constant_htonl(0x60000000) | fl->fl6_flowlabel;
+	*(u32*)hdr = htonl(0x60000000) | fl->fl6_flowlabel;
 	hlimit = -1;
 	if (np)
 		hlimit = np->hop_limit;
@@ -264,7 +264,7 @@
 	struct ipv6hdr *hdr;
 	int totlen;
 
-	skb->protocol = __constant_htons(ETH_P_IPV6);
+	skb->protocol = htons(ETH_P_IPV6);
 	skb->dev = dev;
 
 	totlen = len + sizeof(struct ipv6hdr);
diff -Nru a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c
--- a/net/ipv6/ipv6_sockglue.c	Mon Oct  7 11:25:25 2002
+++ b/net/ipv6/ipv6_sockglue.c	Mon Oct  7 11:25:25 2002
@@ -412,6 +412,15 @@
 	if (get_user(len, optlen))
 		return -EFAULT;
 	switch (optname) {
+	case IPV6_ADDRFORM:
+		if (sk->protocol != IPPROTO_UDP &&
+		    sk->protocol != IPPROTO_TCP)
+			return -EINVAL;
+		if (sk->state != TCP_ESTABLISHED)
+			return -ENOTCONN;
+		val = sk->family;
+		break;
+
 	case IPV6_PKTOPTIONS:
 	{
 		struct msghdr msg;
diff -Nru a/net/ipv6/mcast.c b/net/ipv6/mcast.c
--- a/net/ipv6/mcast.c	Mon Oct  7 11:25:37 2002
+++ b/net/ipv6/mcast.c	Mon Oct  7 11:25:37 2002
@@ -141,14 +141,14 @@
 
 	write_lock_bh(&ipv6_sk_mc_lock);
 	for (lnk = &np->ipv6_mc_list; (mc_lst = *lnk) !=NULL ; lnk = &mc_lst->next) {
-		if (mc_lst->ifindex == ifindex &&
+		if ((ifindex == 0 || mc_lst->ifindex == ifindex) &&
 		    ipv6_addr_cmp(&mc_lst->addr, addr) == 0) {
 			struct net_device *dev;
 
 			*lnk = mc_lst->next;
 			write_unlock_bh(&ipv6_sk_mc_lock);
 
-			if ((dev = dev_get_by_index(ifindex)) != NULL) {
+			if ((dev = dev_get_by_index(mc_lst->ifindex)) != NULL) {
 				ipv6_dev_mc_dec(dev, &mc_lst->addr);
 				dev_put(dev);
 			}
diff -Nru a/net/ipv6/netfilter/ip6_queue.c b/net/ipv6/netfilter/ip6_queue.c
--- a/net/ipv6/netfilter/ip6_queue.c	Mon Oct  7 11:25:29 2002
+++ b/net/ipv6/netfilter/ip6_queue.c	Mon Oct  7 11:25:29 2002
@@ -408,14 +408,8 @@
 	 */
 	if (e->info->hook == NF_IP_LOCAL_OUT) {
 		struct ipv6hdr *iph = e->skb->nh.ipv6h;
-		if (!(   iph->daddr.in6_u.u6_addr32[0] == e->rt_info.daddr.in6_u.u6_addr32[0]
-                      && iph->daddr.in6_u.u6_addr32[1] == e->rt_info.daddr.in6_u.u6_addr32[1]
-                      && iph->daddr.in6_u.u6_addr32[2] == e->rt_info.daddr.in6_u.u6_addr32[2]
-                      && iph->daddr.in6_u.u6_addr32[3] == e->rt_info.daddr.in6_u.u6_addr32[3]
-		      && iph->saddr.in6_u.u6_addr32[0] == e->rt_info.saddr.in6_u.u6_addr32[0]
-		      && iph->saddr.in6_u.u6_addr32[1] == e->rt_info.saddr.in6_u.u6_addr32[1]
-		      && iph->saddr.in6_u.u6_addr32[2] == e->rt_info.saddr.in6_u.u6_addr32[2]
-		      && iph->saddr.in6_u.u6_addr32[3] == e->rt_info.saddr.in6_u.u6_addr32[3]))
+		if (ipv6_addr_cmp(&iph->daddr, &e->rt_info.daddr) ||
+		    ipv6_addr_cmp(&iph->saddr, &e->rt_info.saddr))
 			return route6_me_harder(e->skb);
 	}
 	return 0;
diff -Nru a/net/ipv6/netfilter/ip6t_LOG.c b/net/ipv6/netfilter/ip6t_LOG.c
--- a/net/ipv6/netfilter/ip6t_LOG.c	Mon Oct  7 11:25:29 2002
+++ b/net/ipv6/netfilter/ip6t_LOG.c	Mon Oct  7 11:25:29 2002
@@ -112,7 +112,7 @@
 			printk("FRAG:%u ", ntohs(fhdr->frag_off) & 0xFFF8);
 
 			/* Max length: 11 "INCOMPLETE " */
-			if (fhdr->frag_off & __constant_htons(0x0001))
+			if (fhdr->frag_off & htons(0x0001))
 				printk("INCOMPLETE ");
 
 			printk("ID:%08x ", fhdr->identification);
diff -Nru a/net/ipv6/reassembly.c b/net/ipv6/reassembly.c
--- a/net/ipv6/reassembly.c	Mon Oct  7 11:25:29 2002
+++ b/net/ipv6/reassembly.c	Mon Oct  7 11:25:29 2002
@@ -372,7 +372,7 @@
  				     csum_partial(skb->nh.raw, (u8*)(fhdr+1)-skb->nh.raw, 0));
 
 	/* Is this the final fragment? */
-	if (!(fhdr->frag_off & __constant_htons(0x0001))) {
+	if (!(fhdr->frag_off & htons(0x0001))) {
 		/* If we already have some bits beyond end
 		 * or have different end, the segment is corrupted.
 		 */
@@ -648,7 +648,7 @@
 	hdr = skb->nh.ipv6h;
 	fhdr = (struct frag_hdr *)skb->h.raw;
 
-	if (!(fhdr->frag_off & __constant_htons(0xFFF9))) {
+	if (!(fhdr->frag_off & htons(0xFFF9))) {
 		/* It is not a fragmented frame */
 		skb->h.raw += sizeof(struct frag_hdr);
 		IP6_INC_STATS_BH(Ip6ReasmOKs);
diff -Nru a/net/ipv6/route.c b/net/ipv6/route.c
--- a/net/ipv6/route.c	Mon Oct  7 11:25:26 2002
+++ b/net/ipv6/route.c	Mon Oct  7 11:25:26 2002
@@ -13,6 +13,17 @@
  *      2 of the License, or (at your option) any later version.
  */
 
+/*	Changes:
+ *
+ *	YOSHIFUJI Hideaki @USAGI
+ *		reworked default router selection.
+ *		- respect outgoing interface
+ *		- select from (probably) reachable routers (i.e.
+ *		routers in REACHABLE, STALE, DELAY or PROBE states).
+ *		- always select the same router if it is (probably)
+ *		reachable.  otherwise, round-robin the list.
+ */
+
 #include <linux/config.h>
 #include <linux/errno.h>
 #include <linux/types.h>
@@ -168,6 +179,7 @@
 static struct rt6_info *rt6_dflt_pointer = NULL;
 static spinlock_t rt6_dflt_lock = SPIN_LOCK_UNLOCKED;
 
+/* Default Router Selection (RFC 2461 6.3.6) */
 static struct rt6_info *rt6_best_dflt(struct rt6_info *rt, int oif)
 {
 	struct rt6_info *match = NULL;
@@ -176,63 +188,116 @@
 
 	for (sprt = rt; sprt; sprt = sprt->u.next) {
 		struct neighbour *neigh;
+		int m = 0;
 
-		if ((neigh = sprt->rt6i_nexthop) != NULL) {
-			int m = -1;
+		if (!oif ||
+		    (sprt->rt6i_dev &&
+		     sprt->rt6i_dev->ifindex == oif))
+			m += 8;
 
+		if (sprt == rt6_dflt_pointer)
+			m += 4;
+
+		if ((neigh = sprt->rt6i_nexthop) != NULL) {
+			read_lock_bh(&neigh->lock);
 			switch (neigh->nud_state) {
 			case NUD_REACHABLE:
-				if (sprt != rt6_dflt_pointer) {
-					rt = sprt;
-					goto out;
-				}
-				m = 2;
+				m += 3;
 				break;
 
+			case NUD_STALE:
 			case NUD_DELAY:
-				m = 1;
+			case NUD_PROBE:
+				m += 2;
 				break;
 
-			case NUD_STALE:
-				m = 1;
+			case NUD_NOARP:
+			case NUD_PERMANENT:
+				m += 1;
 				break;
-			};
 
-			if (oif && sprt->rt6i_dev->ifindex == oif) {
-				m += 2;
+			case NUD_INCOMPLETE:
+			default:
+				read_unlock_bh(&neigh->lock);
+				continue;
 			}
+			read_unlock_bh(&neigh->lock);
+		} else {
+			continue;
+		}
 
-			if (m >= mpri) {
-				mpri = m;
-				match = sprt;
+		if (m > mpri || m >= 12) {
+			match = sprt;
+			mpri = m;
+			if (m >= 12) {
+				/* we choose the lastest default router if it
+				 * is in (probably) reachable state.
+				 * If route changed, we should do pmtu
+				 * discovery. --yoshfuji
+				 */
+				break;
 			}
 		}
 	}
 
-	if (match) {
-		rt = match;
-	} else {
+	spin_lock(&rt6_dflt_lock);
+	if (!match) {
 		/*
 		 *	No default routers are known to be reachable.
 		 *	SHOULD round robin
 		 */
-		spin_lock(&rt6_dflt_lock);
 		if (rt6_dflt_pointer) {
-			struct rt6_info *next;
-
-			if ((next = rt6_dflt_pointer->u.next) != NULL &&
-			    next->u.dst.obsolete <= 0 &&
-			    next->u.dst.error == 0)
-				rt = next;
+			for (sprt = rt6_dflt_pointer->u.next;
+			     sprt; sprt = sprt->u.next) {
+				if (sprt->u.dst.obsolete <= 0 &&
+				    sprt->u.dst.error == 0) {
+					match = sprt;
+					break;
+				}
+			}
+			for (sprt = rt;
+			     !match && sprt && sprt != rt6_dflt_pointer; 
+			     sprt = sprt->u.next) {
+				if (sprt->u.dst.obsolete <= 0 &&
+				    sprt->u.dst.error == 0) {
+					match = sprt;
+					break;
+				}
+			}
 		}
-		spin_unlock(&rt6_dflt_lock);
 	}
 
-out:
-	spin_lock(&rt6_dflt_lock);
-	rt6_dflt_pointer = rt;
+	if (match) {
+		if (rt6_dflt_pointer != match)
+			RT6_TRACE("changed default router: %p->%p\n",
+				  rt6_dflt_pointer, match);
+		rt6_dflt_pointer = match;
+	}
 	spin_unlock(&rt6_dflt_lock);
-	return rt;
+
+	if (!match) {
+		/*
+		 * Last Resort: if no default routers found, 
+		 * use addrconf default route.
+		 * We don't record this route.
+		 */
+		for (sprt = ip6_routing_table.leaf;
+		     sprt; sprt = sprt->u.next) {
+			if ((sprt->rt6i_flags & RTF_DEFAULT) &&
+			    (!oif ||
+			     (sprt->rt6i_dev &&
+			      sprt->rt6i_dev->ifindex == oif))) {
+				match = sprt;
+				break;
+			}
+		}
+		if (!match) {
+			/* no default route.  give up. */
+			match = &ip6_null_entry;
+		}
+	}
+
+	return match;
 }
 
 struct rt6_info *rt6_lookup(struct in6_addr *daddr, struct in6_addr *saddr,
diff -Nru a/net/ipv6/sit.c b/net/ipv6/sit.c
--- a/net/ipv6/sit.c	Mon Oct  7 11:25:39 2002
+++ b/net/ipv6/sit.c	Mon Oct  7 11:25:39 2002
@@ -396,7 +396,7 @@
 		skb->mac.raw = skb->nh.raw;
 		skb->nh.raw = skb->data;
 		memset(&(IPCB(skb)->opt), 0, sizeof(struct ip_options));
-		skb->protocol = __constant_htons(ETH_P_IPV6);
+		skb->protocol = htons(ETH_P_IPV6);
 		skb->pkt_type = PACKET_HOST;
 		tunnel->stat.rx_packets++;
 		tunnel->stat.rx_bytes += skb->len;
@@ -470,7 +470,7 @@
 		goto tx_error;
 	}
 
-	if (skb->protocol != __constant_htons(ETH_P_IPV6))
+	if (skb->protocol != htons(ETH_P_IPV6))
 		goto tx_error;
 
 	if (!dst)
@@ -588,7 +588,7 @@
 	iph->version		=	4;
 	iph->ihl		=	sizeof(struct iphdr)>>2;
 	if (mtu > IPV6_MIN_MTU)
-		iph->frag_off	=	__constant_htons(IP_DF);
+		iph->frag_off	=	htons(IP_DF);
 	else
 		iph->frag_off	=	0;
 
@@ -659,10 +659,10 @@
 
 		err = -EINVAL;
 		if (p.iph.version != 4 || p.iph.protocol != IPPROTO_IPV6 ||
-		    p.iph.ihl != 5 || (p.iph.frag_off&__constant_htons(~IP_DF)))
+		    p.iph.ihl != 5 || (p.iph.frag_off&htons(~IP_DF)))
 			goto done;
 		if (p.iph.ttl)
-			p.iph.frag_off |= __constant_htons(IP_DF);
+			p.iph.frag_off |= htons(IP_DF);
 
 		t = ipip6_tunnel_locate(&p, cmd == SIOCADDTUNNEL);
 
diff -Nru a/net/ipv6/udp.c b/net/ipv6/udp.c
--- a/net/ipv6/udp.c	Mon Oct  7 11:25:28 2002
+++ b/net/ipv6/udp.c	Mon Oct  7 11:25:28 2002
@@ -424,11 +424,11 @@
 		sin6->sin6_flowinfo = 0;
 		sin6->sin6_scope_id = 0;
 
-		if (skb->protocol == __constant_htons(ETH_P_IP)) {
+		if (skb->protocol == htons(ETH_P_IP)) {
 			struct inet_opt *inet = inet_sk(sk);
 
 			ipv6_addr_set(&sin6->sin6_addr, 0, 0,
-				      __constant_htonl(0xffff), skb->nh.iph->saddr);
+				      htonl(0xffff), skb->nh.iph->saddr);
 			if (inet->cmsg_flags)
 				ip_cmsg_recv(msg, skb);
 		} else {
diff -Nru a/net/ipx/ChangeLog b/net/ipx/ChangeLog
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/net/ipx/ChangeLog	Mon Oct  7 11:26:06 2002
@@ -0,0 +1,101 @@
+ Revision 0.21:	Uses the new generic socket option code.
+
+ Revision 0.22:	Gcc clean ups and drop out device registration. Use the
+ 		new multi-protocol edition of hard_header
+
+ Revision 0.23: IPX /proc by Mark Evans. Adding a route will
+ 		will overwrite any existing route to the same network.
+
+ Revision 0.24:	Supports new /proc with no 4K limit
+
+ Revision 0.25:	Add ephemeral sockets, passive local network
+ 		identification, support for local net 0 and
+ 		multiple datalinks <Greg Page>
+
+ Revision 0.26: Device drop kills IPX routes via it. (needed for module)
+
+ Revision 0.27: Autobind <Mark Evans>
+
+ Revision 0.28: Small fix for multiple local networks <Thomas Winder>
+
+ Revision 0.29: Assorted major errors removed <Mark Evans>
+ 		Small correction to promisc mode error fix <Alan Cox>
+ 		Asynchronous I/O support. Changed to use notifiers
+ 		and the newer packet_type stuff. Assorted major
+ 		fixes <Alejandro Liu>
+
+ Revision 0.30:	Moved to net/ipx/...	<Alan Cox>
+ 		Don't set address length on recvfrom that errors.
+ 		Incorrect verify_area.
+
+ Revision 0.31:	New sk_buffs. This still needs a lot of 
+ 		testing. <Alan Cox>
+
+ Revision 0.32: Using sock_alloc_send_skb, firewall hooks. <Alan Cox>
+ 		Supports sendmsg/recvmsg
+
+ Revision 0.33:	Internal network support, routing changes, uses a
+ 		protocol private area for ipx data.
+
+ Revision 0.34:	Module support. <Jim Freeman>
+
+ Revision 0.35: Checksum support. <Neil Turton>, hooked in by <Alan Cox>
+ 		Handles WIN95 discovery packets <Volker Lendecke>
+
+ Revision 0.36:	Internal bump up for 2.1
+
+ Revision 0.37:	Began adding POSIXisms.
+
+ Revision 0.38: Asynchronous socket stuff made current.
+
+ Revision 0.39: SPX interfaces
+
+ Revision 0.40: Tiny SIOCGSTAMP fix (chris@cybernet.co.nz)
+
+ Revision 0.41: 802.2TR removed (p.norton@computer.org)
+ 		Fixed connecting to primary net,
+ 		Automatic binding on send & receive,
+ 		Martijn van Oosterhout <kleptogimp@geocities.com>
+
+ Revision 042:  Multithreading - use spinlocks and refcounting to
+ 		protect some structures: ipx_interface sock list, list
+ 		of ipx interfaces, etc. 
+ 		Bugfixes - do refcounting on net_devices, check function
+ 		results, etc. Thanks to davem and freitag for
+ 		suggestions and guidance.
+ 		Arnaldo Carvalho de Melo <acme@conectiva.com.br>,
+ 		November, 2000
+
+ Revision 043:	Shared SKBs, don't mangle packets, some cleanups
+ 		Arnaldo Carvalho de Melo <acme@conectiva.com.br>,
+ 		December, 2000
+
+ Revision 044:	Call ipxitf_hold on NETDEV_UP - acme
+
+ Revision 045:	fix PPROP routing bug - acme
+
+ Revision 046:	Further fixes to PPROP, ipxitf_create_internal was
+ 		doing an unneeded MOD_INC_USE_COUNT, implement
+ 		sysctl for ipx_pprop_broacasting, fix the ipx sysctl
+ 		handling, making it dynamic, some cleanups, thanks to
+ 		Petr Vandrovec for review and good suggestions. (acme)
+
+ Revision 047:	Cleanups, CodingStyle changes, move the ncp connection
+ 		hack out of line - acme
+
+ Revision 048:	Use sk->protinfo to store the pointer to IPX private
+ 		area, remove af_ipx from sk->protinfo and move ipx_opt
+ 		to include/net/ipx.h, use IPX_SK like DecNET, etc - acme
+
+ Revision 049:	SPX support dropped, see comment in ipx_create - acme
+
+ Revision 050:	Use seq_file for proc stuff, moving it to ipx_proc.c - acme
+
+Other fixes:
+ 
+ Protect the module by a MOD_INC_USE_COUNT/MOD_DEC_USE_COUNT pair. Also, now
+ usage count is managed this way:
+ -Count one if the auto_interface mode is on
+ -Count one per configured interface
+ 
+ Jacques Gelinas (jacques@solucorp.qc.ca)
diff -Nru a/net/ipx/Makefile b/net/ipx/Makefile
--- a/net/ipx/Makefile	Mon Oct  7 11:25:36 2002
+++ b/net/ipx/Makefile	Mon Oct  7 11:25:36 2002
@@ -4,7 +4,7 @@
 
 obj-$(CONFIG_IPX) += ipx.o
 
-ipx-y			:= af_ipx.o
+ipx-y			:= af_ipx.o ipx_proc.o
 ipx-$(CONFIG_SYSCTL)	+= sysctl_net_ipx.o
 ipx-objs		:= $(ipx-y)
 
diff -Nru a/net/ipx/af_ipx.c b/net/ipx/af_ipx.c
--- a/net/ipx/af_ipx.c	Mon Oct  7 11:25:26 2002
+++ b/net/ipx/af_ipx.c	Mon Oct  7 11:25:26 2002
@@ -15,81 +15,17 @@
  *	liability nor provide warranty for any of this software. This material
  *	is provided as is and at no charge.
  *
- *	Revision 0.21:	Uses the new generic socket option code.
- *	Revision 0.22:	Gcc clean ups and drop out device registration. Use the
- *			new multi-protocol edition of hard_header
- *	Revision 0.23:  IPX /proc by Mark Evans. Adding a route will
- *     			will overwrite any existing route to the same network.
- *	Revision 0.24:	Supports new /proc with no 4K limit
- *	Revision 0.25:	Add ephemeral sockets, passive local network
- *			identification, support for local net 0 and
- *			multiple datalinks <Greg Page>
- *	Revision 0.26:  Device drop kills IPX routes via it. (needed for module)
- *	Revision 0.27:  Autobind <Mark Evans>
- *	Revision 0.28:  Small fix for multiple local networks <Thomas Winder>
- *	Revision 0.29:  Assorted major errors removed <Mark Evans>
- *			Small correction to promisc mode error fix <Alan Cox>
- *			Asynchronous I/O support. Changed to use notifiers
- *			and the newer packet_type stuff. Assorted major
- *			fixes <Alejandro Liu>
- *	Revision 0.30:	Moved to net/ipx/...	<Alan Cox>
- *			Don't set address length on recvfrom that errors.
- *			Incorrect verify_area.
- *	Revision 0.31:	New sk_buffs. This still needs a lot of 
- *			testing. <Alan Cox>
- *	Revision 0.32:  Using sock_alloc_send_skb, firewall hooks. <Alan Cox>
- *			Supports sendmsg/recvmsg
- *	Revision 0.33:	Internal network support, routing changes, uses a
- *			protocol private area for ipx data.
- *	Revision 0.34:	Module support. <Jim Freeman>
- *	Revision 0.35:  Checksum support. <Neil Turton>, hooked in by <Alan Cox>
- *			Handles WIN95 discovery packets <Volker Lendecke>
- *	Revision 0.36:	Internal bump up for 2.1
- *	Revision 0.37:	Began adding POSIXisms.
- *	Revision 0.38:  Asynchronous socket stuff made current.
- *	Revision 0.39:  SPX interfaces
- *	Revision 0.40:  Tiny SIOCGSTAMP fix (chris@cybernet.co.nz)
- *      Revision 0.41:  802.2TR removed (p.norton@computer.org)
- *			Fixed connecting to primary net,
- *			Automatic binding on send & receive,
- *			Martijn van Oosterhout <kleptogimp@geocities.com>
- *	Revision 042:   Multithreading - use spinlocks and refcounting to
- *			protect some structures: ipx_interface sock list, list
- *			of ipx interfaces, etc. 
- *			Bugfixes - do refcounting on net_devices, check function
- *			results, etc. Thanks to davem and freitag for
- *			suggestions and guidance.
- *			Arnaldo Carvalho de Melo <acme@conectiva.com.br>,
- *			November, 2000
- *	Revision 043:	Shared SKBs, don't mangle packets, some cleanups
- *			Arnaldo Carvalho de Melo <acme@conectiva.com.br>,
- *			December, 2000
- *	Revision 044:	Call ipxitf_hold on NETDEV_UP (acme)
- *	Revision 045:	fix PPROP routing bug (acme)
- *	Revision 046:	Further fixes to PPROP, ipxitf_create_internal was
- *			doing an unneeded MOD_INC_USE_COUNT, implement
- *			sysctl for ipx_pprop_broacasting, fix the ipx sysctl
- *			handling, making it dynamic, some cleanups, thanks to
- *			Petr Vandrovec for review and good suggestions. (acme)
- *	Revision 047:	Cleanups, CodingStyle changes, move the ncp connection
- *			hack out of line (acme)
- *	Revision 048:	Use sk->protinfo to store the pointer to IPX private
- *			area, remove af_ipx from sk->protinfo and move ipx_opt
- *			to include/net/ipx.h, use IPX_SK like DecNET, etc
- *	Revision 049:	SPX support dropped, see comment in ipx_create
- *
- *	Protect the module by a MOD_INC_USE_COUNT/MOD_DEC_USE_COUNT
- *	pair. Also, now usage count is managed this way
- *	-Count one if the auto_interface mode is on
- *      -Count one per configured interface
- *
- *	Jacques Gelinas (jacques@solucorp.qc.ca)
- *
+ *	Portions Copyright (c) 2000-2002 Conectiva, Inc. <acme@conectiva.com.br>
+ *	Neither Arnaldo Carvalho de Melo nor Conectiva, Inc. admit liability nor
+ *	provide warranty for any of this software. This material is provided
+ *	"AS-IS" and at no charge.
  *
  * 	Portions Copyright (c) 1995 Caldera, Inc. <greg@caldera.com>
  *	Neither Greg Page nor Caldera, Inc. admit liability nor provide
  *	warranty for any of this software. This material is provided
  *	"AS-IS" and at no charge.
+ *
+ *	See net/ipx/ChangeLog.
  */
 
 #include <linux/config.h>
@@ -145,13 +81,13 @@
 
 static struct proto_ops ipx_dgram_ops;
 
-static struct ipx_route *ipx_routes;
-static rwlock_t ipx_routes_lock = RW_LOCK_UNLOCKED;
+struct ipx_route *ipx_routes;
+rwlock_t ipx_routes_lock = RW_LOCK_UNLOCKED;
 
-static struct ipx_interface *ipx_interfaces;
-static spinlock_t ipx_interfaces_lock = SPIN_LOCK_UNLOCKED;
+struct ipx_interface *ipx_interfaces;
+spinlock_t ipx_interfaces_lock = SPIN_LOCK_UNLOCKED;
 
-static struct ipx_interface *ipx_primary_net;
+struct ipx_interface *ipx_primary_net;
 static struct ipx_interface *ipx_internal_net;
 
 #undef IPX_REFCNT_DEBUG
@@ -791,8 +727,6 @@
 	return ipxrtr_add_route(intrfc->if_netnum, intrfc, NULL);
 }
 
-static const char *ipx_frame_name(unsigned short);
-static const char *ipx_device_name(struct ipx_interface *);
 static void ipxitf_discover_netnum(struct ipx_interface *intrfc,
 				   struct sk_buff *skb);
 static int ipxitf_pprop(struct ipx_interface *intrfc, struct sk_buff *skb);
@@ -1656,7 +1590,7 @@
 out:	return ret;
 }
 
-static const char *ipx_frame_name(unsigned short frame)
+const char *ipx_frame_name(unsigned short frame)
 {
 	char* ret = "None";
 
@@ -1671,189 +1605,12 @@
 	return ret;
 }
 
-static const char *ipx_device_name(struct ipx_interface *intrfc)
+const char *ipx_device_name(struct ipx_interface *intrfc)
 {
 	return intrfc->if_internal ? "Internal" :
 		intrfc->if_dev ? intrfc->if_dev->name : "Unknown";
 }
 
-/* Called from proc fs */
-static int ipx_interface_get_info(char *buffer, char **start, off_t offset,
-				  int length)
-{
-	struct ipx_interface *i;
-	off_t begin = 0, pos = 0;
-	int len = 0;
-
-	/* Theory.. Keep printing in the same place until we pass offset */
-
-	len += sprintf(buffer, "%-11s%-15s%-9s%-11s%s", "Network",
-		"Node_Address", "Primary", "Device", "Frame_Type");
-#ifdef IPX_REFCNT_DEBUG
-	len += sprintf(buffer + len, "  refcnt");
-#endif
-	strcat(buffer + len++, "\n");
-	spin_lock_bh(&ipx_interfaces_lock);
-	for (i = ipx_interfaces; i; i = i->if_next) {
-		len += sprintf(buffer + len, "%08lX   ",
-				(long unsigned int) ntohl(i->if_netnum));
-		len += sprintf(buffer + len, "%02X%02X%02X%02X%02X%02X   ",
-				i->if_node[0], i->if_node[1], i->if_node[2],
-				i->if_node[3], i->if_node[4], i->if_node[5]);
-		len += sprintf(buffer + len, "%-9s", i == ipx_primary_net ?
-			"Yes" : "No");
-		len += sprintf(buffer + len, "%-11s", ipx_device_name(i));
-		len += sprintf(buffer + len, "%-9s",
-			ipx_frame_name(i->if_dlink_type));
-#ifdef IPX_REFCNT_DEBUG
-		len += sprintf(buffer + len, "%6d", atomic_read(&i->refcnt));
-#endif
-		strcat(buffer + len++, "\n");
-		/* Are we still dumping unwanted data then discard the record */
-		pos = begin + len;
-
-		if (pos < offset) {
-			len   = 0;	/* Keep dumping into the buffer start */
-			begin = pos;
-		}
-		if (pos > offset + length)	/* We have dumped enough */
-			break;
-	}
-	spin_unlock_bh(&ipx_interfaces_lock);
-
-	/* The data in question runs from begin to begin+len */
-	*start = buffer + (offset - begin);	/* Start of wanted data */
-	len -= (offset - begin); /* Remove unwanted header data from length */
-	if (len > length)
-		len = length;	/* Remove unwanted tail data from length */
-
-	return len;
-}
-
-static int ipx_get_info(char *buffer, char **start, off_t offset, int length)
-{
-	struct sock *s;
-	struct ipx_interface *i;
-	off_t begin = 0, pos = 0;
-	int len = 0;
-
-	/* Theory.. Keep printing in the same place until we pass offset */
-
-#ifdef CONFIG_IPX_INTERN
-	len += sprintf(buffer, "%-28s%-28s%-10s%-10s%-7s%s\n", "Local_Address",
-#else
-	len += sprintf(buffer, "%-15s%-28s%-10s%-10s%-7s%s\n", "Local_Address",
-#endif	/* CONFIG_IPX_INTERN */
-			"Remote_Address", "Tx_Queue", "Rx_Queue",
-			"State", "Uid");
-
-	spin_lock_bh(&ipx_interfaces_lock);
-	for (i = ipx_interfaces; i; i = i->if_next) {
-		ipxitf_hold(i);
-		spin_lock_bh(&i->if_sklist_lock);
-		for (s = i->if_sklist; s; s = s->next) {
-			struct ipx_opt *ipxs = ipx_sk(s);
-#ifdef CONFIG_IPX_INTERN
-			len += sprintf(buffer + len,
-				       "%08lX:%02X%02X%02X%02X%02X%02X:%04X  ",
-                             (unsigned long)htonl(ipxs->intrfc->if_netnum),
-				       ipxs->node[0], ipxs->node[1],
-				       ipxs->node[2], ipxs->node[3],
-				       ipxs->node[4], ipxs->node[5],
-				       htons(ipxs->port));
-#else
-			len += sprintf(buffer + len, "%08lX:%04X  ",
-				       (unsigned long) htonl(i->if_netnum),
-				       htons(ipxs->port));
-#endif	/* CONFIG_IPX_INTERN */
-			if (s->state != TCP_ESTABLISHED)
-				len += sprintf(buffer + len, "%-28s",
-						"Not_Connected");
-			else {
-				len += sprintf(buffer + len,
-					"%08lX:%02X%02X%02X%02X%02X%02X:%04X  ",
-				      (unsigned long)htonl(ipxs->dest_addr.net),
-					ipxs->dest_addr.node[0],
-					ipxs->dest_addr.node[1],
-					ipxs->dest_addr.node[2],
-					ipxs->dest_addr.node[3],
-					ipxs->dest_addr.node[4],
-					ipxs->dest_addr.node[5],
-					htons(ipxs->dest_addr.sock));
-			}
-
-			len += sprintf(buffer + len, "%08X  %08X  ",
-				atomic_read(&s->wmem_alloc),
-				atomic_read(&s->rmem_alloc));
-			len += sprintf(buffer + len, "%02X     %03d\n",
-				s->state, SOCK_INODE(s->socket)->i_uid);
-
-			pos = begin + len;
-			if (pos < offset) {
-				len   = 0;
-				begin = pos;
-			}
-
-			if (pos > offset + length)  /* We have dumped enough */
-				break;
-		}
-		spin_unlock_bh(&i->if_sklist_lock);
-		ipxitf_put(i);
-	}
-	spin_unlock_bh(&ipx_interfaces_lock);
-
-	/* The data in question runs from begin to begin+len */
-	*start = buffer + offset - begin;
-	len -= (offset - begin);
-	if (len > length)
-		len = length;
-
-	return len;
-}
-
-static int ipx_rt_get_info(char *buffer, char **start, off_t offset, int length)
-{
-	struct ipx_route *rt;
-	off_t begin = 0, pos = 0;
-	int len = 0;
-
-	len += sprintf(buffer, "%-11s%-13s%s\n",
-			"Network", "Router_Net", "Router_Node");
-	read_lock_bh(&ipx_routes_lock);
-	for (rt = ipx_routes; rt; rt = rt->ir_next) {
-		len += sprintf(buffer + len, "%08lX   ",
-				(long unsigned int) ntohl(rt->ir_net));
-		if (rt->ir_routed) {
-			len += sprintf(buffer + len,
-					"%08lX     %02X%02X%02X%02X%02X%02X\n",
-			(long unsigned int) ntohl(rt->ir_intrfc->if_netnum),
-				rt->ir_router_node[0], rt->ir_router_node[1],
-				rt->ir_router_node[2], rt->ir_router_node[3],
-				rt->ir_router_node[4], rt->ir_router_node[5]);
-		} else {
-			len += sprintf(buffer + len, "%-13s%s\n",
-					"Directly", "Connected");
-		}
-
-		pos = begin + len;
-		if (pos < offset) {
-			len = 0;
-			begin = pos;
-		}
-
-		if (pos > offset + length)
-			break;
-	}
-	read_unlock_bh(&ipx_routes_lock);
-
-	*start = buffer + (offset - begin);
-	len -= (offset - begin);
-	if (len > length)
-		len = length;
-
-	return len;
-}
-
 /* Handling for system calls applied via the various interfaces to an IPX
  * socket object. */
 
@@ -2516,9 +2273,9 @@
 static unsigned char ipx_8022_type = 0xE0;
 static unsigned char ipx_snap_id[5] = { 0x0, 0x0, 0x0, 0x81, 0x37 };
 static char ipx_banner[] __initdata =
-	KERN_INFO "NET4: Linux IPX 0.49 for NET4.0\n"
+	KERN_INFO "NET4: Linux IPX 0.50 for NET4.0\n"
 	KERN_INFO "IPX Portions Copyright (c) 1995 Caldera, Inc.\n" \
-	KERN_INFO "IPX Portions Copyright (c) 2000, 2001 Conectiva, Inc.\n";
+	KERN_INFO "IPX Portions Copyright (c) 2000-2002 Conectiva, Inc.\n";
 static char ipx_EII_err_msg[] __initdata =
 	KERN_CRIT "IPX: Unable to register with Ethernet II\n";
 static char ipx_8023_err_msg[] __initdata =
@@ -2554,11 +2311,7 @@
 
 	register_netdevice_notifier(&ipx_dev_notifier);
 	ipx_register_sysctl();
-#ifdef CONFIG_PROC_FS
-	proc_net_create("ipx", 0, ipx_get_info);
-	proc_net_create("ipx_interface", 0, ipx_interface_get_info);
-	proc_net_create("ipx_route", 0, ipx_rt_get_info);
-#endif
+	ipx_proc_init();
 	printk(ipx_banner);
 	return 0;
 }
@@ -2580,14 +2333,13 @@
 
 static void __exit ipx_proto_finito(void)
 {
-	/* no need to worry about having anything on the ipx_interfaces
-	 * list, when a interface is created we increment the module
-	 * usage count, so the module will only be unloaded when there
-	 * are no more interfaces */
-
-	proc_net_remove("ipx_route");
-	proc_net_remove("ipx_interface");
-	proc_net_remove("ipx");
+	/*
+	 * No need to worry about having anything on the ipx_interfaces list,
+	 * when a interface is created we increment the module usage count, so
+	 * the module will only be unloaded when there are no more interfaces
+	 */
+
+	ipx_proc_exit();
 	ipx_unregister_sysctl();
 
 	unregister_netdevice_notifier(&ipx_dev_notifier);
diff -Nru a/net/ipx/ipx_proc.c b/net/ipx/ipx_proc.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/net/ipx/ipx_proc.c	Mon Oct  7 11:25:40 2002
@@ -0,0 +1,388 @@
+/*
+ *	IPX proc routines
+ *
+ * 	Copyright(C) Arnaldo Carvalho de Melo <acme@conectiva.com.br>, 2002
+ */
+
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/proc_fs.h>
+#include <linux/spinlock.h>
+#include <linux/seq_file.h>
+#include <linux/tcp.h>
+#include <net/ipx.h>
+
+#ifdef CONFIG_PROC_FS
+static __inline__ struct ipx_interface *ipx_get_interface_idx(loff_t pos)
+{
+	struct ipx_interface *i;
+
+	for (i = ipx_interfaces; pos && i; i = i->if_next)
+		--pos;
+
+	return i;
+}
+
+static void *ipx_seq_interface_start(struct seq_file *seq, loff_t *pos)
+{
+	loff_t l = *pos;
+
+	spin_lock_bh(&ipx_interfaces_lock);
+	return l ? ipx_get_interface_idx(--l) : (void *)1;
+}
+
+static void *ipx_seq_interface_next(struct seq_file *seq, void *v, loff_t *pos)
+{
+	struct ipx_interface *i;
+
+	++*pos;
+	if (v == (void *)1) {
+		i = NULL;
+		if (ipx_interfaces)
+			i = ipx_interfaces;
+		goto out;
+	}
+	i = v;
+	i = i->if_next;
+out:
+	return i;
+}
+
+static void ipx_seq_interface_stop(struct seq_file *seq, void *v)
+{
+	spin_unlock_bh(&ipx_interfaces_lock);
+}
+
+static int ipx_seq_interface_show(struct seq_file *seq, void *v)
+{
+	struct ipx_interface *i;
+
+	if (v == (void *)1) {
+		seq_puts(seq, "Network    Node_Address   Primary  Device     "
+			      "Frame_Type");
+#ifdef IPX_REFCNT_DEBUG
+		seq_puts(seq, "  refcnt");
+#endif
+		seq_puts(seq, "\n");
+		goto out;
+	}
+
+	i = v;
+	seq_printf(seq, "%08lX   ", (unsigned long int)ntohl(i->if_netnum));
+	seq_printf(seq, "%02X%02X%02X%02X%02X%02X   ",
+			i->if_node[0], i->if_node[1], i->if_node[2],
+			i->if_node[3], i->if_node[4], i->if_node[5]);
+	seq_printf(seq, "%-9s", i == ipx_primary_net ? "Yes" : "No");
+	seq_printf(seq, "%-11s", ipx_device_name(i));
+	seq_printf(seq, "%-9s", ipx_frame_name(i->if_dlink_type));
+#ifdef IPX_REFCNT_DEBUG
+	seq_printf(seq, "%6d", atomic_read(&i->refcnt));
+#endif
+	seq_puts(seq, "\n");
+out:
+	return 0;
+}
+
+static __inline__ struct ipx_route *ipx_get_route_idx(loff_t pos)
+{
+	struct ipx_route *r;
+
+	for (r = ipx_routes; pos && r; r = r->ir_next)
+		--pos;
+
+	return r;
+}
+
+static void *ipx_seq_route_start(struct seq_file *seq, loff_t *pos)
+{
+	loff_t l = *pos;
+	read_lock_bh(&ipx_routes_lock);
+	return l ? ipx_get_route_idx(--l) : (void *)1;
+}
+
+static void *ipx_seq_route_next(struct seq_file *seq, void *v, loff_t *pos)
+{
+	struct ipx_route *r;
+
+	++*pos;
+	if (v == (void *)1) {
+		r = NULL;
+		if (ipx_routes)
+			r = ipx_routes;
+		goto out;
+	}
+	r = v;
+	r = r->ir_next;
+out:
+	return r;
+}
+
+static void ipx_seq_route_stop(struct seq_file *seq, void *v)
+{
+	read_unlock_bh(&ipx_routes_lock);
+}
+
+static int ipx_seq_route_show(struct seq_file *seq, void *v)
+{
+	struct ipx_route *rt;
+
+	if (v == (void *)1) {
+		seq_puts(seq, "Network    Router_Net   Router_Node\n");
+		goto out;
+	}
+	rt = v;
+	seq_printf(seq, "%08lX   ", (unsigned long int)ntohl(rt->ir_net));
+	if (rt->ir_routed)
+		seq_printf(seq, "%08lX     %02X%02X%02X%02X%02X%02X\n",
+			   (long unsigned int)ntohl(rt->ir_intrfc->if_netnum),
+			   rt->ir_router_node[0], rt->ir_router_node[1],
+			   rt->ir_router_node[2], rt->ir_router_node[3],
+			   rt->ir_router_node[4], rt->ir_router_node[5]);
+	else
+		seq_puts(seq, "Directly     Connected\n");
+out:
+	return 0;
+}
+
+static __inline__ struct sock *ipx_get_socket_idx(loff_t pos)
+{
+	struct sock *s = NULL;
+	struct ipx_interface *i;
+
+	for (i = ipx_interfaces; pos && i; i = i->if_next) {
+		spin_lock_bh(&i->if_sklist_lock);
+		for (s = i->if_sklist; pos && s; s = s->next)
+			--pos;
+		if (!pos) {
+			if (!s)
+				spin_unlock_bh(&i->if_sklist_lock);
+			break;
+		}
+		spin_unlock_bh(&i->if_sklist_lock);
+	}
+
+	return s;
+}
+
+static void *ipx_seq_socket_start(struct seq_file *seq, loff_t *pos)
+{
+	loff_t l = *pos;
+
+	spin_lock_bh(&ipx_interfaces_lock);
+	return l ? ipx_get_socket_idx(--l) : (void *)1;
+}
+
+static void *ipx_seq_socket_next(struct seq_file *seq, void *v, loff_t *pos)
+{
+	struct sock* sk;
+	struct ipx_interface *i;
+	struct ipx_opt *ipxs;
+
+	++*pos;
+	if (v == (void *)1) {
+		sk = NULL;
+		if (!ipx_interfaces)
+			goto out;
+		sk = ipx_interfaces->if_sklist;
+		if (sk)
+			spin_lock_bh(&ipx_interfaces->if_sklist_lock);
+		goto out;
+	}
+	sk = v;
+	if (sk->next) {
+		sk = sk->next;
+		goto out;
+	}
+	ipxs = ipx_sk(sk);
+	i = ipxs->intrfc;
+	spin_unlock_bh(&i->if_sklist_lock);
+	sk = NULL;
+	for (;;) {
+		if (!i->if_next)
+			break;
+		i = i->if_next;
+		spin_lock_bh(&i->if_sklist_lock);
+		if (i->if_sklist) {
+			sk = i->if_sklist;
+			break;
+		}
+		spin_unlock_bh(&i->if_sklist_lock);
+	}
+out:
+	return sk;
+}
+
+static int ipx_seq_socket_show(struct seq_file *seq, void *v)
+{
+	struct sock *s;
+	struct ipx_opt *ipxs;
+
+	if (v == (void *)1) {
+#ifdef CONFIG_IPX_INTERN
+		seq_puts(seq, "Local_Address               "
+			      "Remote_Address              Tx_Queue  "
+			      "Rx_Queue  State  Uid\n");
+#else
+		seq_puts(seq, "Local_Address  Remote_Address              "
+			      "Tx_Queue  Rx_Queue  State  Uid\n");
+#endif
+		goto out;
+	}
+
+	s = v;
+	ipxs = ipx_sk(s);
+#ifdef CONFIG_IPX_INTERN
+	seq_printf(seq, "%08lX:%02X%02X%02X%02X%02X%02X:%04X  ",
+		   (unsigned long)htonl(ipxs->intrfc->if_netnum),
+		   ipxs->node[0], ipxs->node[1], ipxs->node[2], ipxs->node[3],
+		   ipxs->node[4], ipxs->node[5], htons(ipxs->port));
+#else
+	seq_printf(seq, "%08lX:%04X  ", (unsigned long) htonl(ipxs->intrfc->if_netnum),
+		   htons(ipxs->port));
+#endif	/* CONFIG_IPX_INTERN */
+	if (s->state != TCP_ESTABLISHED)
+		seq_printf(seq, "%-28s", "Not_Connected");
+	else {
+		seq_printf(seq, "%08lX:%02X%02X%02X%02X%02X%02X:%04X  ",
+			   (unsigned long)htonl(ipxs->dest_addr.net),
+			   ipxs->dest_addr.node[0], ipxs->dest_addr.node[1],
+			   ipxs->dest_addr.node[2], ipxs->dest_addr.node[3],
+			   ipxs->dest_addr.node[4], ipxs->dest_addr.node[5],
+			   htons(ipxs->dest_addr.sock));
+	}
+
+	seq_printf(seq, "%08X  %08X  %02X     %03d\n",
+		   atomic_read(&s->wmem_alloc), atomic_read(&s->rmem_alloc),
+		   s->state, SOCK_INODE(s->socket)->i_uid);
+out:
+	return 0;
+}
+
+struct seq_operations ipx_seq_interface_ops = {
+	.start  = ipx_seq_interface_start,
+	.next   = ipx_seq_interface_next,
+	.stop   = ipx_seq_interface_stop,
+	.show   = ipx_seq_interface_show,
+};
+
+struct seq_operations ipx_seq_route_ops = {
+	.start  = ipx_seq_route_start,
+	.next   = ipx_seq_route_next,
+	.stop   = ipx_seq_route_stop,
+	.show   = ipx_seq_route_show,
+};
+
+struct seq_operations ipx_seq_socket_ops = {
+	.start  = ipx_seq_socket_start,
+	.next   = ipx_seq_socket_next,
+	.stop   = ipx_seq_interface_stop,
+	.show   = ipx_seq_socket_show,
+};
+
+static int ipx_seq_route_open(struct inode *inode, struct file *file)
+{
+	return seq_open(file, &ipx_seq_route_ops);
+}
+
+static int ipx_seq_interface_open(struct inode *inode, struct file *file)
+{
+	return seq_open(file, &ipx_seq_interface_ops);
+}
+
+static int ipx_seq_socket_open(struct inode *inode, struct file *file)
+{
+	return seq_open(file, &ipx_seq_socket_ops);
+}
+
+static struct file_operations ipx_seq_interface_fops = {
+	.open           = ipx_seq_interface_open,
+	.read           = seq_read,
+	.llseek         = seq_lseek,
+	.release        = seq_release,
+};
+
+static struct file_operations ipx_seq_route_fops = {
+	.open           = ipx_seq_route_open,
+	.read           = seq_read,
+	.llseek         = seq_lseek,
+	.release        = seq_release,
+};
+
+static struct file_operations ipx_seq_socket_fops = {
+	.open           = ipx_seq_socket_open,
+	.read           = seq_read,
+	.llseek         = seq_lseek,
+	.release        = seq_release,
+};
+
+static int ipx_proc_perms(struct inode* inode, int op)
+{
+	return 0;
+}
+
+static struct inode_operations ipx_seq_inode = {
+	.permission     = ipx_proc_perms,
+};
+
+static struct proc_dir_entry *ipx_proc_dir;
+
+int __init ipx_proc_init(void)
+{
+	struct proc_dir_entry *p;
+	int rc = -ENOMEM;
+       
+	ipx_proc_dir = proc_mkdir("ipx", proc_net);
+
+	if (!ipx_proc_dir)
+		goto out;
+	p = create_proc_entry("interface", 0, ipx_proc_dir);
+	if (!p)
+		goto out_interface;
+
+	p->proc_fops = &ipx_seq_interface_fops;
+	p->proc_iops = &ipx_seq_inode;
+	p = create_proc_entry("route", 0, ipx_proc_dir);
+	if (!p)
+		goto out_route;
+
+	p->proc_fops = &ipx_seq_route_fops;
+	p->proc_iops = &ipx_seq_inode;
+	p = create_proc_entry("socket", 0, ipx_proc_dir);
+	if (!p)
+		goto out_socket;
+
+	p->proc_fops = &ipx_seq_socket_fops;
+	p->proc_iops = &ipx_seq_inode;
+
+	rc = 0;
+out:
+	return rc;
+out_socket:
+	remove_proc_entry("route", ipx_proc_dir);
+out_route:
+	remove_proc_entry("interface", ipx_proc_dir);
+out_interface:
+	remove_proc_entry("ipx", proc_net);
+	goto out;
+}
+
+void __exit ipx_proc_exit(void)
+{
+	remove_proc_entry("interface", ipx_proc_dir);
+	remove_proc_entry("route", ipx_proc_dir);
+	remove_proc_entry("socket", ipx_proc_dir);
+	remove_proc_entry("ipx", proc_net);
+}
+
+#else /* CONFIG_PROC_FS */
+
+int __init ipx_proc_init(void)
+{
+	return 0;
+}
+
+void __exit ipx_proc_exit(void)
+{
+}
+
+#endif /* CONFIG_PROC_FS */
diff -Nru a/net/irda/ircomm/ircomm_param.c b/net/irda/ircomm/ircomm_param.c
--- a/net/irda/ircomm/ircomm_param.c	Mon Oct  7 11:25:35 2002
+++ b/net/irda/ircomm/ircomm_param.c	Mon Oct  7 11:25:35 2002
@@ -29,7 +29,7 @@
  ********************************************************************/
 
 #include <linux/sched.h>
-#include <linux/tqueue.h>
+#include <linux/workqueue.h>
 #include <linux/interrupt.h>
 
 #include <net/irda/irda.h>
@@ -166,8 +166,7 @@
 
 	if (flush) {
 		/* ircomm_tty_do_softint will take care of the rest */
-		queue_task(&self->tqueue, &tq_immediate);
-		mark_bh(IMMEDIATE_BH);
+		schedule_work(&self->tqueue);
 	}
 
 	return count;
diff -Nru a/net/irda/ircomm/ircomm_tty.c b/net/irda/ircomm/ircomm_tty.c
--- a/net/irda/ircomm/ircomm_tty.c	Mon Oct  7 11:25:29 2002
+++ b/net/irda/ircomm/ircomm_tty.c	Mon Oct  7 11:25:29 2002
@@ -420,8 +420,7 @@
 		self->flow = FLOW_STOP;
 
 		self->line = line;
-		self->tqueue.routine = ircomm_tty_do_softint;
-		self->tqueue.data = self;
+		INIT_WORK(&self->tqueue, ircomm_tty_do_softint, self);
 		self->max_header_size = IRCOMM_TTY_HDR_UNITIALISED;
 		self->max_data_size = 64-self->max_header_size;
 		self->close_delay = 5*HZ/10;
@@ -632,8 +631,7 @@
 	 * Let do_softint() do this to avoid race condition with 
 	 * do_softint() ;-) 
 	 */
-	queue_task(&self->tqueue, &tq_immediate);
-	mark_bh(IMMEDIATE_BH);
+	schedule_work(&self->tqueue);
 }
 
 /*
@@ -806,8 +804,7 @@
 	 * its 256 byte tx buffer). We will then defragment and send out
 	 * all this data as one single packet.  
 	 */
-	queue_task(&self->tqueue, &tq_immediate);
-	mark_bh(IMMEDIATE_BH);
+	schedule_work(&self->tqueue);
 	
 	return len;
 }
@@ -1132,8 +1129,7 @@
 				/* Wake up processes blocked on open */
 				wake_up_interruptible(&self->open_wait);
 
-				queue_task(&self->tqueue, &tq_immediate);
-				mark_bh(IMMEDIATE_BH);
+				schedule_work(&self->tqueue);
 				return;
 			}
 		} else {
@@ -1246,8 +1242,7 @@
 		tty->hw_stopped = 0;
 
 		/* ircomm_tty_do_softint will take care of the rest */
-		queue_task(&self->tqueue, &tq_immediate);
-		mark_bh(IMMEDIATE_BH);
+		schedule_work(&self->tqueue);
 		break;
 	default:  /* If we get here, something is very wrong, better stop */
 	case FLOW_STOP:
diff -Nru a/net/irda/ircomm/ircomm_tty_attach.c b/net/irda/ircomm/ircomm_tty_attach.c
--- a/net/irda/ircomm/ircomm_tty_attach.c	Mon Oct  7 11:25:38 2002
+++ b/net/irda/ircomm/ircomm_tty_attach.c	Mon Oct  7 11:25:38 2002
@@ -535,8 +535,7 @@
 		wake_up_interruptible(&self->open_wait);
 	}
 
-	queue_task(&self->tqueue, &tq_immediate);
-	mark_bh(IMMEDIATE_BH);
+	schedule_work(&self->tqueue);
 }
 
 /*
diff -Nru a/net/llc/Makefile b/net/llc/Makefile
--- a/net/llc/Makefile	Mon Oct  7 11:25:37 2002
+++ b/net/llc/Makefile	Mon Oct  7 11:25:37 2002
@@ -16,7 +16,7 @@
 
 llc-y := llc_if.o llc_c_ev.o llc_c_ac.o llc_mac.o llc_sap.o llc_s_st.o \
 	 llc_main.o llc_s_ac.o llc_conn.o llc_c_st.o llc_stat.o llc_actn.o \
-	 llc_s_ev.o llc_evnt.o llc_pdu.o
+	 llc_s_ev.o llc_evnt.o llc_pdu.o llc_proc.o
 llc-$(CONFIG_LLC_UI)	+= af_llc.o
 llc-objs	 	:= $(llc-y)
 
diff -Nru a/net/llc/af_llc.c b/net/llc/af_llc.c
--- a/net/llc/af_llc.c	Mon Oct  7 11:25:25 2002
+++ b/net/llc/af_llc.c	Mon Oct  7 11:25:25 2002
@@ -22,27 +22,12 @@
  */
 #include <linux/config.h>
 #include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/types.h>
-#include <linux/socket.h>
-#include <linux/sockios.h>
-#include <asm/uaccess.h>
-#include <asm/ioctls.h>
-#include <linux/proc_fs.h>
-#include <linux/in.h>
 #include <linux/tcp.h>
-#include <linux/netdevice.h>
-#include <linux/inetdevice.h>
-#include <linux/errno.h>
-#include <net/sock.h>
-#include <net/llc_if.h>
 #include <net/llc_sap.h>
 #include <net/llc_pdu.h>
 #include <net/llc_conn.h>
 #include <net/llc_mac.h>
 #include <net/llc_main.h>
-#include <linux/llc.h>
-#include <linux/if_arp.h>
 #include <linux/rtnetlink.h>
 #include <linux/init.h>
 
@@ -569,6 +554,13 @@
 		rc = -EAGAIN;
 		if (!timeout)
 			break;
+		/*
+		 * Well, if we have backlog, try to process it now.
+		 */
+                if (sk->backlog.tail) {
+			release_sock(sk);
+			lock_sock(sk);
+		}
 		rc = 0;
 		if (skb_queue_empty(&sk->receive_queue)) {
 			release_sock(sk);
@@ -1020,94 +1012,6 @@
 	return rc;
 }
 
-#ifdef CONFIG_PROC_FS
-#define MAC_FORMATTED_SIZE 17
-static void llc_ui_format_mac(char *bf, unsigned char *mac)
-{
-	sprintf(bf, "%02X:%02X:%02X:%02X:%02X:%02X",
-		mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
-}
-
-/**
- *	llc_ui_get_info - return info to procfs
- *	@buffer: where to put the formatted output
- *	@start: starting from
- *	@offset: offset into buffer.
- *	@length: size of the buffer
- *
- *	Get the output of the local llc ui socket list to the caller.
- *	Returns the length of data wrote to buffer.
- */
-static int llc_ui_get_info(char *buffer, char **start, off_t offset, int length)
-{
-	off_t pos = 0;
-	off_t begin = 0;
-	struct llc_sap *sap;
-	struct sock *sk;
-	struct list_head *sap_entry;
-	struct llc_station *station = llc_station_get();
-	int len = sprintf(buffer, "SKt Mc local_mac_sap        "
-				  "remote_mac_sap       tx_queue rx_queue st uid "
-				  "link\n");
-
-	/* Output the LLC socket data for the /proc filesystem */
-	read_lock_bh(&station->sap_list.lock);
-	list_for_each(sap_entry, &station->sap_list.list) {
-		sap = list_entry(sap_entry, struct llc_sap, node);
-
-		read_lock_bh(&sap->sk_list.lock);
-		for (sk = sap->sk_list.list; sk; sk = sk->next) {
-			struct llc_opt *llc = llc_sk(sk);
-
-			len += sprintf(buffer + len, "%2X  %2X ", sk->type,
-				       !llc_mac_null(llc->addr.sllc_mmac));
-			if (llc->dev && llc_mac_null(llc->addr.sllc_mmac))
-				llc_ui_format_mac(buffer + len,
-						  llc->dev->dev_addr);
-			else {
-				if (!llc_mac_null(llc->addr.sllc_mmac))
-					llc_ui_format_mac(buffer + len,
-							  llc->addr.sllc_mmac);
-				else
-					sprintf(buffer + len,
-						"00:00:00:00:00:00");
-			}
-			len += MAC_FORMATTED_SIZE;
-			len += sprintf(buffer + len, "@%02X ", sap->laddr.lsap);
-			llc_ui_format_mac(buffer + len, llc->addr.sllc_dmac);
-			len += MAC_FORMATTED_SIZE;
-			len += sprintf(buffer + len,
-					"@%02X %8d %8d %2d %3d ",
-					llc->addr.sllc_dsap,
-					atomic_read(&sk->wmem_alloc),
-					atomic_read(&sk->rmem_alloc),
-					sk->state,
-					sk->socket ?
-					  SOCK_INODE(sk->socket)->i_uid : -1);
-			len += sprintf(buffer + len, "%4d\n", llc->link);
-			/* Are we still dumping unwanted data then discard the record */
-			pos = begin + len;
-
-			if (pos < offset) {
-				len = 0; /* Keep dumping into the buffer start */
-				begin = pos;
-			}
-			if (pos > offset + length) /* We have dumped enough */
-				break;
-		}
-		read_unlock_bh(&sap->sk_list.lock);
-	}
-	read_unlock_bh(&station->sap_list.lock);
-
-	/* The data in question runs from begin to begin + len */
-	*start = buffer + offset - begin; /* Start of wanted data */
-	len -= offset - begin; /* Remove unwanted header data from length */
-	if (len > length)
-		len = length; /* Remove unwanted tail data from length */
-	return len;
-}
-#endif /* CONFIG_PROC_FS */
-
 static struct net_proto_family llc_ui_family_ops = {
 	.family = PF_LLC,
 	.create = llc_ui_create,
@@ -1140,13 +1044,11 @@
 {
 	llc_ui_sap_last_autoport = LLC_SAP_DYN_START;
 	sock_register(&llc_ui_family_ops);
-	proc_net_create("llc", 0, llc_ui_get_info);
 	printk(llc_ui_banner);
 	return 0;
 }
 
 void __exit llc_ui_exit(void)
 {
-	proc_net_remove("llc");
 	sock_unregister(PF_LLC);
 }
diff -Nru a/net/llc/llc_c_ac.c b/net/llc/llc_c_ac.c
--- a/net/llc/llc_c_ac.c	Mon Oct  7 11:25:39 2002
+++ b/net/llc/llc_c_ac.c	Mon Oct  7 11:25:39 2002
@@ -1487,7 +1487,7 @@
 	if (llc_sk(sk)->state == LLC_CONN_OUT_OF_SVC) {
 		printk(KERN_WARNING "%s: timer called on closed connection\n",
 		       __FUNCTION__);
-		llc_conn_free_ev(skb);
+		kfree_skb(skb);
 	} else {
 		if (!sk->lock.users)
 			llc_conn_state_process(sk, skb);
diff -Nru a/net/llc/llc_conn.c b/net/llc/llc_conn.c
--- a/net/llc/llc_conn.c	Mon Oct  7 11:25:25 2002
+++ b/net/llc/llc_conn.c	Mon Oct  7 11:25:25 2002
@@ -71,6 +71,12 @@
 	struct llc_opt *llc = llc_sk(sk);
 	struct llc_conn_state_ev *ev = llc_conn_ev(skb);
 
+	/*
+	 * We have to hold the skb, because llc_conn_service will kfree it in
+	 * the sending path and we need to look at the skb->cb, where we encode
+	 * llc_conn_state_ev.
+	 */
+	skb_get(skb);
 	ev->ind_prim = ev->cfm_prim = 0;
 	rc = llc_conn_service(sk, skb); /* sending event to state machine */
 	if (rc) {
@@ -81,10 +87,10 @@
 	if (!ev->ind_prim && !ev->cfm_prim) {   /* indicate or confirm not required */
 		if (!skb->list)
 			goto out_kfree_skb;
-		goto out;
+		goto out_skb_put;
 	}
 
-	if (ev->ind_prim && ev->cfm_prim)
+	if (ev->ind_prim && ev->cfm_prim) /* Paranoia */
 		skb_get(skb);
 
 	switch (ev->ind_prim) {
@@ -180,11 +186,12 @@
 					__FUNCTION__, ev->cfm_prim);
 			break;
 		}
-		goto out; /* No confirmation */
+		goto out_skb_put; /* No confirmation */
 	}
 out_kfree_skb:
 	kfree_skb(skb);
-out:
+out_skb_put:
+	kfree_skb(skb);
 	return rc;
 }
 
@@ -226,25 +233,29 @@
 	struct sk_buff *skb;
 	struct llc_pdu_sn *pdu;
 	u16 nbr_unack_pdus;
+	struct llc_opt *llc;
 	u8 howmany_resend = 0;
 
 	llc_conn_remove_acked_pdus(sk, nr, &nbr_unack_pdus);
 	if (!nbr_unack_pdus)
 		goto out;
-	/* process unack PDUs only if unack queue is not empty; remove
+	/*
+	 * Process unack PDUs only if unack queue is not empty; remove
 	 * appropriate PDUs, fix them up, and put them on mac_pdu_q.
 	 */
-	while ((skb = skb_dequeue(&llc_sk(sk)->pdu_unack_q)) != NULL) {
-		pdu = (struct llc_pdu_sn *)skb->nh.raw;
+	llc = llc_sk(sk);
+
+	while ((skb = skb_dequeue(&llc->pdu_unack_q)) != NULL) {
+		pdu = llc_pdu_sn_hdr(skb);
 		llc_pdu_set_cmd_rsp(skb, LLC_PDU_CMD);
 		llc_pdu_set_pf_bit(skb, first_p_bit);
 		skb_queue_tail(&sk->write_queue, skb);
 		first_p_bit = 0;
-		llc_sk(sk)->vS = LLC_I_GET_NS(pdu);
+		llc->vS = LLC_I_GET_NS(pdu);
 		howmany_resend++;
 	}
 	if (howmany_resend > 0)
-		llc_sk(sk)->vS = (llc_sk(sk)->vS + 1) % LLC_2_SEQ_NBR_MODULO;
+		llc->vS = (llc->vS + 1) % LLC_2_SEQ_NBR_MODULO;
 	/* any PDUs to re-send are queued up; start sending to MAC */
 	llc_conn_send_pdus(sk);
 out:;
@@ -263,27 +274,29 @@
 void llc_conn_resend_i_pdu_as_rsp(struct sock *sk, u8 nr, u8 first_f_bit)
 {
 	struct sk_buff *skb;
-	struct llc_pdu_sn *pdu;
 	u16 nbr_unack_pdus;
+	struct llc_opt *llc = llc_sk(sk);
 	u8 howmany_resend = 0;
 
 	llc_conn_remove_acked_pdus(sk, nr, &nbr_unack_pdus);
 	if (!nbr_unack_pdus)
 		goto out;
-	/* process unack PDUs only if unack queue is not empty; remove
+	/*
+	 * Process unack PDUs only if unack queue is not empty; remove
 	 * appropriate PDUs, fix them up, and put them on mac_pdu_q
 	 */
-	while ((skb = skb_dequeue(&llc_sk(sk)->pdu_unack_q)) != NULL) {
-		pdu = (struct llc_pdu_sn *)skb->nh.raw;
+	while ((skb = skb_dequeue(&llc->pdu_unack_q)) != NULL) {
+		struct llc_pdu_sn *pdu = llc_pdu_sn_hdr(skb);
+
 		llc_pdu_set_cmd_rsp(skb, LLC_PDU_RSP);
 		llc_pdu_set_pf_bit(skb, first_f_bit);
 		skb_queue_tail(&sk->write_queue, skb);
 		first_f_bit = 0;
-		llc_sk(sk)->vS = LLC_I_GET_NS(pdu);
+		llc->vS = LLC_I_GET_NS(pdu);
 		howmany_resend++;
 	}
 	if (howmany_resend > 0)
-		llc_sk(sk)->vS = (llc_sk(sk)->vS + 1) % LLC_2_SEQ_NBR_MODULO;
+		llc->vS = (llc->vS + 1) % LLC_2_SEQ_NBR_MODULO;
 	/* any PDUs to re-send are queued up; start sending to MAC */
 	llc_conn_send_pdus(sk);
 out:;
@@ -304,25 +317,26 @@
 	struct sk_buff *skb;
 	struct llc_pdu_sn *pdu;
 	int nbr_acked = 0;
-	int q_len = skb_queue_len(&llc_sk(sk)->pdu_unack_q);
+	struct llc_opt *llc = llc_sk(sk);
+	int q_len = skb_queue_len(&llc->pdu_unack_q);
 
 	if (!q_len)
 		goto out;
-	skb = skb_peek(&llc_sk(sk)->pdu_unack_q);
-	pdu = (struct llc_pdu_sn *)skb->nh.raw;
+	skb = skb_peek(&llc->pdu_unack_q);
+	pdu = llc_pdu_sn_hdr(skb);
 
 	/* finding position of last acked pdu in queue */
 	pdu_pos = ((int)LLC_2_SEQ_NBR_MODULO + (int)nr -
 			(int)LLC_I_GET_NS(pdu)) % LLC_2_SEQ_NBR_MODULO;
 
 	for (i = 0; i < pdu_pos && i < q_len; i++) {
-		skb = skb_dequeue(&llc_sk(sk)->pdu_unack_q);
+		skb = skb_dequeue(&llc->pdu_unack_q);
 		if (skb)
 			kfree_skb(skb);
 		nbr_acked++;
 	}
 out:
-	*how_many_unacked = skb_queue_len(&llc_sk(sk)->pdu_unack_q);
+	*how_many_unacked = skb_queue_len(&llc->pdu_unack_q);
 	return nbr_acked;
 }
 
@@ -337,7 +351,7 @@
 	struct sk_buff *skb;
 
 	while ((skb = skb_dequeue(&sk->write_queue)) != NULL) {
-		struct llc_pdu_sn *pdu = (struct llc_pdu_sn *)skb->nh.raw;
+		struct llc_pdu_sn *pdu = llc_pdu_sn_hdr(skb);
 
 		if (!LLC_PDU_TYPE_IS_I(pdu) &&
 		    !(skb->dev->flags & IFF_LOOPBACK)) {
@@ -350,31 +364,6 @@
 		}
 		dev_queue_xmit(skb);
 	}
-}
-
-/**
- *	llc_conn_free_ev - free event
- *	@skb: event to free
- *
- *	Free allocated event.
- */
-void llc_conn_free_ev(struct sk_buff *skb)
-{
-	struct llc_conn_state_ev *ev = llc_conn_ev(skb);
-
-	if (ev->type == LLC_CONN_EV_TYPE_PDU) {
-		/* free the frame that is bound to this event */
-		struct llc_pdu_sn *pdu = llc_pdu_sn_hdr(skb);
-
-		if (LLC_PDU_TYPE_IS_I(pdu) || !ev->ind_prim)
-			kfree_skb(skb);
-	} else if (ev->type == LLC_CONN_EV_TYPE_PRIM &&
-		   ev->prim != LLC_DATA_PRIM)
-		kfree_skb(skb);
-	else if (ev->type == LLC_CONN_EV_TYPE_P_TMR ||
-		 ev->type == LLC_CONN_EV_TYPE_BUSY_TMR ||
-		 ev->type == LLC_CONN_EV_TYPE_REJ_TMR)
-		kfree_skb(skb);
 }
 
 /**
diff -Nru a/net/llc/llc_if.c b/net/llc/llc_if.c
--- a/net/llc/llc_if.c	Mon Oct  7 11:25:35 2002
+++ b/net/llc/llc_if.c	Mon Oct  7 11:25:35 2002
@@ -26,7 +26,6 @@
 #include <net/llc_c_ac.h>
 #include <net/llc_c_st.h>
 #include <net/llc_main.h>
-#include <net/llc_mac.h>
 
 /**
  *	llc_sap_open - open interface to the upper layers.
@@ -57,7 +56,7 @@
 	/* allocated a SAP; initialize it and clear out its memory pool */
 	sap->laddr.lsap = lsap;
 	sap->rcv_func	= func;
-	sap->station	= llc_station_get();
+	sap->station	= &llc_main_station;
 	/* initialized SAP; add it to list of SAPs this station manages */
 	llc_sap_save(sap);
 out:
diff -Nru a/net/llc/llc_mac.c b/net/llc/llc_mac.c
--- a/net/llc/llc_mac.c	Mon Oct  7 11:25:37 2002
+++ b/net/llc/llc_mac.c	Mon Oct  7 11:25:37 2002
@@ -197,12 +197,11 @@
  */
 static void llc_station_rcv(struct sk_buff *skb)
 {
-	struct llc_station *station = llc_station_get();
 	struct llc_station_state_ev *ev = llc_station_ev(skb);
 
 	ev->type   = LLC_STATION_EV_TYPE_PDU;
 	ev->reason = 0;
-	llc_station_state_process(station, skb);
+	llc_station_state_process(&llc_main_station, skb);
 }
 
 
@@ -293,28 +292,4 @@
 			rc = 1;
 	}
 	return rc;
-}
-
-/**
- *	mac_dev_peer - search the appropriate dev to send packets to peer
- *	@current_dev - Current device suggested by upper layer
- *	@type - hardware type
- *	@mac - mac address
- *
- *	Check if the we should use loopback to send packets, i.e., if the
- *	dmac belongs to one of the local interfaces, returning the pointer
- *	to the loopback &net_device struct or the current_dev if it is not
- *	local.
- */
-struct net_device *mac_dev_peer(struct net_device *current_dev, int type,
-				u8 *mac)
-{
-	struct net_device *dev;
-
-        rtnl_lock();
-        dev = dev_getbyhwaddr(type, mac);
-        if (dev)
-                dev = __dev_get_by_name("lo");
-        rtnl_unlock();
-	return dev ? : current_dev;
 }
diff -Nru a/net/llc/llc_main.c b/net/llc/llc_main.c
--- a/net/llc/llc_main.c	Mon Oct  7 11:25:32 2002
+++ b/net/llc/llc_main.c	Mon Oct  7 11:25:32 2002
@@ -3,7 +3,7 @@
  * 	and connections of the LLC.
  *
  * Copyright (c) 1997 by Procom Technology, Inc.
- * 		 2001 by Arnaldo Carvalho de Melo <acme@conectiva.com.br>
+ * 		 2001, 2002 by Arnaldo Carvalho de Melo <acme@conectiva.com.br>
  *
  * This program can be redistributed or modified under the terms of the
  * GNU General Public License as published by the Free Software Foundation.
@@ -12,14 +12,9 @@
  *
  * See the GNU General Public License for more details.
  */
-#include <linux/kernel.h>
-#include <linux/netdevice.h>
-#include <net/sock.h>
-#include <linux/sched.h>
+#include <linux/config.h>
 #include <linux/init.h>
 #include <linux/module.h>
-#include <linux/proc_fs.h>
-#include <net/llc_if.h>
 #include <net/llc_sap.h>
 #include <net/llc_conn.h>
 #include <net/llc_main.h>
@@ -33,7 +28,7 @@
 #include <net/llc_s_ev.h>
 #include <net/llc_s_st.h>
 #include <net/llc_mac.h>
-#include <linux/llc.h>
+#include <net/llc_proc.h>
 
 /* static function prototypes */
 static void llc_station_service_events(struct llc_station *station);
@@ -50,7 +45,7 @@
 						    struct sk_buff *skb);
 static int llc_rtn_all_conns(struct llc_sap *sap);
 
-static struct llc_station llc_main_station;	/* only one of its kind */
+struct llc_station llc_main_station;	/* only one of its kind */
 
 #undef LLC_REFCNT_DEBUG
 #ifdef LLC_REFCNT_DEBUG
@@ -147,20 +142,22 @@
 		if (llc->state > 1) /* not closed */
 			rc = llc_conn_rcv(sk, skb);
 		else
-			kfree_skb(skb);
+			goto out_kfree_skb;
 	} else if (llc_backlog_type(skb) == LLC_EVENT) {
 		/* timer expiration event */
 		if (llc->state > 1)  /* not closed */
 			rc = llc_conn_state_process(sk, skb);
 		else
-			llc_conn_free_ev(skb);
-		kfree_skb(skb);
+			goto out_kfree_skb;
 	} else {
 		printk(KERN_ERR "%s: invalid skb in backlog\n", __FUNCTION__);
-		kfree_skb(skb);
+		goto out_kfree_skb;
 	}
-
+out:
 	return rc;
+out_kfree_skb:
+	kfree_skb(skb);
+	goto out;
 }
 
 /**
@@ -338,16 +335,6 @@
 }
 
 /**
- *	llc_station_get - get addr of global station.
- *
- *	Returns address of a place to copy the global station to it.
- */
-struct llc_station *llc_station_get(void)
-{
-	return &llc_main_station;
-}
-
-/**
  *	llc_station_state_process: queue event and try to process queue.
  *	@station: Address of the station
  *	@skb: Address of the event
@@ -535,80 +522,6 @@
 	return skb;
 }
 
-static char *llc_conn_state_names[] = {
-	[LLC_CONN_STATE_ADM] =        "adm", 
-	[LLC_CONN_STATE_SETUP] =      "setup", 
-	[LLC_CONN_STATE_NORMAL] =     "normal",
-	[LLC_CONN_STATE_BUSY] =       "busy", 
-	[LLC_CONN_STATE_REJ] =        "rej", 
-	[LLC_CONN_STATE_AWAIT] =      "await", 
-	[LLC_CONN_STATE_AWAIT_BUSY] = "await_busy",
-	[LLC_CONN_STATE_AWAIT_REJ] =  "await_rej",
-	[LLC_CONN_STATE_D_CONN]	=     "d_conn",
-	[LLC_CONN_STATE_RESET] =      "reset", 
-	[LLC_CONN_STATE_ERROR] =      "error", 
-	[LLC_CONN_STATE_TEMP] =       "temp", 
-};
-
-static int llc_proc_get_info(char *bf, char **start, off_t offset, int length)
-{
-	struct list_head *sap_entry;
-	struct sock *sk;
-	off_t begin = 0, pos = 0;
-	int len = 0;
-
-	read_lock_bh(&llc_main_station.sap_list.lock);
-	list_for_each(sap_entry, &llc_main_station.sap_list.list) {
-		struct llc_sap *sap = list_entry(sap_entry, struct llc_sap,
-						 node);
-
-		len += sprintf(bf + len, "lsap=%02X\n", sap->laddr.lsap);
-		read_lock_bh(&sap->sk_list.lock);
-		if (!sap->sk_list.list) {
-			len += sprintf(bf + len, "no connections\n");
-			goto unlock;
-		}
-		len += sprintf(bf + len, "connection list:\n"
-					 "dsap state      retr txw rxw "
-					 "pf ff sf df rs cs "
-					 "tack tpfc trs tbs blog busr\n");
-		for (sk = sap->sk_list.list; sk; sk = sk->next) {
-			struct llc_opt *llc = llc_sk(sk);
-
-			len += sprintf(bf + len, " %02X  %-10s %3d  %3d %3d "
-						 "%2d %2d %2d "
-						 "%2d %2d %2d "
-						 "%4d %4d %3d %3d %4d %4d\n",
-				       llc->daddr.lsap,
-				       llc_conn_state_names[llc->state],
-				       llc->retry_count, llc->k, llc->rw,
-				       llc->p_flag, llc->f_flag, llc->s_flag,
-				       llc->data_flag, llc->remote_busy_flag,
-				       llc->cause_flag,
-				       timer_pending(&llc->ack_timer.timer),
-				       timer_pending(&llc->pf_cycle_timer.timer),
-				       timer_pending(&llc->rej_sent_timer.timer),
-				       timer_pending(&llc->busy_state_timer.timer),
-				       !!sk->backlog.tail, sk->lock.users);
-		}
-unlock:	
-		read_unlock_bh(&sap->sk_list.lock);
-		pos = begin + len;
-		if (pos < offset) {
-			len = 0; /* Keep dumping into the buffer start */
-			begin = pos;
-		}
-		if (pos > offset + length) /* We have dumped enough */
-			break;
-	}
-	read_unlock_bh(&llc_main_station.sap_list.lock);
-
-	/* The data in question runs from begin to begin + len */
-	*start = bf + (offset - begin); /* Start of wanted data */
-	len -= (offset - begin); /* Remove unwanted header data from length */
-	return len;
-}
-
 static struct packet_type llc_packet_type = {
 	.type = __constant_htons(ETH_P_802_2),
 	.func = llc_rcv,
@@ -642,9 +555,11 @@
 	llc_main_station.ack_timer.data     = (unsigned long)&llc_main_station;
 	llc_main_station.ack_timer.function = llc_station_ack_tmr_cb;
 
+	if (llc_proc_init())
+		goto err;
 	skb = alloc_skb(0, GFP_ATOMIC);
 	if (!skb)
-		goto err;
+		goto err_skb;
 	llc_build_offset_table();
 	ev = llc_station_ev(skb);
 	memset(ev, 0, sizeof(*ev));
@@ -659,12 +574,13 @@
 	ev->type	= LLC_STATION_EV_TYPE_SIMPLE;
 	ev->prim_type	= LLC_STATION_EV_ENABLE_WITHOUT_DUP_ADDR_CHECK;
 	rc = llc_station_next_state(&llc_main_station, skb);
-	proc_net_create("802.2", 0, llc_proc_get_info);
 	llc_ui_init();
 	dev_add_pack(&llc_packet_type);
 	dev_add_pack(&llc_tr_packet_type);
 out:
 	return rc;
+err_skb:
+	llc_proc_exit();
 err:
 	printk(llc_error_msg);
 	rc = 1;
@@ -674,9 +590,9 @@
 static void __exit llc_exit(void)
 {
 	llc_ui_exit();
+	llc_proc_exit();
 	dev_remove_pack(&llc_packet_type);
 	dev_remove_pack(&llc_tr_packet_type);
-	proc_net_remove("802.2");
 }
 
 module_init(llc_init);
diff -Nru a/net/llc/llc_pdu.c b/net/llc/llc_pdu.c
--- a/net/llc/llc_pdu.c	Mon Oct  7 11:25:26 2002
+++ b/net/llc/llc_pdu.c	Mon Oct  7 11:25:26 2002
@@ -15,7 +15,6 @@
 #include <linux/if_tr.h>
 #include <net/llc_pdu.h>
 #include <net/llc_if.h>
-#include <net/llc_mac.h>
 #include <net/llc_main.h>
 
 static void llc_pdu_decode_pdu_type(struct sk_buff *skb, u8 *type);
diff -Nru a/net/llc/llc_proc.c b/net/llc/llc_proc.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/net/llc/llc_proc.c	Mon Oct  7 11:25:40 2002
@@ -0,0 +1,287 @@
+/*
+ * proc_llc.c - proc interface for LLC
+ *
+ * Copyright (c) 2001 by Jay Schulist <jschlst@samba.org>
+ *		 2002 by Arnaldo Carvalho de Melo <acme@conectiva.com.br>
+ *
+ * This program can be redistributed or modified under the terms of the
+ * GNU General Public License as published by the Free Software Foundation.
+ * This program is distributed without any warranty or implied warranty
+ * of merchantability or fitness for a particular purpose.
+ *
+ * See the GNU General Public License for more details.
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/proc_fs.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/seq_file.h>
+#include <net/sock.h>
+#include <net/llc_c_ac.h>
+#include <net/llc_c_ev.h>
+#include <net/llc_c_st.h>
+#include <net/llc_conn.h>
+#include <net/llc_mac.h>
+#include <net/llc_main.h>
+#include <net/llc_sap.h>
+
+#ifdef CONFIG_PROC_FS
+static void llc_ui_format_mac(struct seq_file *seq, unsigned char *mac)
+{
+	seq_printf(seq, "%02X:%02X:%02X:%02X:%02X:%02X",
+		   mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
+}
+
+static __inline__ struct sock *llc_get_sk_idx(loff_t pos)
+{
+	struct list_head *sap_entry;
+	struct llc_sap *sap;
+	struct sock *sk = NULL;
+
+	list_for_each(sap_entry, &llc_main_station.sap_list.list) {
+		sap = list_entry(sap_entry, struct llc_sap, node);
+
+		read_lock_bh(&sap->sk_list.lock);
+		for (sk = sap->sk_list.list; pos && sk; sk = sk->next)
+			--pos;
+		if (!pos) {
+			if (!sk)
+				read_unlock_bh(&sap->sk_list.lock);
+			break;
+		}
+		read_unlock_bh(&sap->sk_list.lock);
+	}
+	return sk;
+}
+
+static void *llc_seq_start(struct seq_file *seq, loff_t *pos)
+{
+	loff_t l = *pos;
+
+	read_lock_bh(&llc_main_station.sap_list.lock);
+	return l ? llc_get_sk_idx(--l) : (void *)1;
+}
+
+static void *llc_seq_next(struct seq_file *seq, void *v, loff_t *pos)
+{
+	struct sock* sk;
+	struct llc_opt *llc;
+	struct llc_sap *sap;
+
+	++*pos;
+	if (v == (void *)1) {
+		if (list_empty(&llc_main_station.sap_list.list)) {
+			sk = NULL;
+			goto out;
+		}
+		sap = list_entry(llc_main_station.sap_list.list.next,
+				 struct llc_sap, node);
+
+		read_lock_bh(&sap->sk_list.lock);
+		sk = sap->sk_list.list;
+		goto out;
+	}
+	sk = v;
+	if (sk->next) {
+		sk = sk->next;
+		goto out;
+	}
+	llc = llc_sk(sk);
+	sap = llc->sap;
+	read_unlock_bh(&sap->sk_list.lock);
+	sk = NULL;
+	for (;;) {
+		if (sap->node.next == &llc_main_station.sap_list.list)
+			break;
+		sap = list_entry(sap->node.next, struct llc_sap, node);
+		read_lock_bh(&sap->sk_list.lock);
+		if (sap->sk_list.list) {
+			sk = sap->sk_list.list;
+			break;
+		}
+		read_unlock_bh(&sap->sk_list.lock);
+	}
+out:
+	return sk;
+}
+
+static void llc_seq_stop(struct seq_file *seq, void *v)
+{
+	read_unlock_bh(&llc_main_station.sap_list.lock);
+}
+
+static int llc_seq_socket_show(struct seq_file *seq, void *v)
+{
+	struct sock* sk;
+	struct llc_opt *llc;
+
+	if (v == (void *)1) {
+		seq_puts(seq, "SKt Mc local_mac_sap        remote_mac_sap   "
+			      "    tx_queue rx_queue st uid link\n");
+		goto out;
+	}
+	sk = v;
+	llc = llc_sk(sk);
+
+	seq_printf(seq, "%2X  %2X ", sk->type,
+		   !llc_mac_null(llc->addr.sllc_mmac));
+
+	if (llc->dev && llc_mac_null(llc->addr.sllc_mmac))
+		llc_ui_format_mac(seq, llc->dev->dev_addr);
+	else if (!llc_mac_null(llc->addr.sllc_mmac))
+		llc_ui_format_mac(seq, llc->addr.sllc_mmac);
+	else
+		seq_printf(seq, "00:00:00:00:00:00");
+	seq_printf(seq, "@%02X ", llc->sap->laddr.lsap);
+	llc_ui_format_mac(seq, llc->addr.sllc_dmac);
+	seq_printf(seq, "@%02X %8d %8d %2d %3d %4d\n", llc->addr.sllc_dsap,
+		   atomic_read(&sk->wmem_alloc), atomic_read(&sk->rmem_alloc),
+		   sk->state, sk->socket ? SOCK_INODE(sk->socket)->i_uid : -1,
+		   llc->link);
+out:
+	return 0;
+}
+
+static char *llc_conn_state_names[] = {
+	[LLC_CONN_STATE_ADM] =        "adm", 
+	[LLC_CONN_STATE_SETUP] =      "setup", 
+	[LLC_CONN_STATE_NORMAL] =     "normal",
+	[LLC_CONN_STATE_BUSY] =       "busy", 
+	[LLC_CONN_STATE_REJ] =        "rej", 
+	[LLC_CONN_STATE_AWAIT] =      "await", 
+	[LLC_CONN_STATE_AWAIT_BUSY] = "await_busy",
+	[LLC_CONN_STATE_AWAIT_REJ] =  "await_rej",
+	[LLC_CONN_STATE_D_CONN]	=     "d_conn",
+	[LLC_CONN_STATE_RESET] =      "reset", 
+	[LLC_CONN_STATE_ERROR] =      "error", 
+	[LLC_CONN_STATE_TEMP] =       "temp", 
+};
+
+static int llc_seq_core_show(struct seq_file *seq, void *v)
+{
+	struct sock* sk;
+	struct llc_opt *llc;
+
+	if (v == (void *)1) {
+		seq_puts(seq, "Connection list:\n"
+			      "dsap state      retr txw rxw pf ff sf df rs cs "
+			      "tack tpfc trs tbs blog busr\n");
+		goto out;
+	}
+	sk = v;
+	llc = llc_sk(sk);
+
+	seq_printf(seq, " %02X  %-10s %3d  %3d %3d %2d %2d %2d %2d %2d %2d "
+			"%4d %4d %3d %3d %4d %4d\n",
+		   llc->daddr.lsap, llc_conn_state_names[llc->state],
+		   llc->retry_count, llc->k, llc->rw, llc->p_flag, llc->f_flag,
+		   llc->s_flag, llc->data_flag, llc->remote_busy_flag,
+		   llc->cause_flag, timer_pending(&llc->ack_timer.timer),
+		   timer_pending(&llc->pf_cycle_timer.timer),
+		   timer_pending(&llc->rej_sent_timer.timer),
+		   timer_pending(&llc->busy_state_timer.timer),
+		   !!sk->backlog.tail, sk->lock.users);
+out:
+	return 0;
+}
+
+struct seq_operations llc_seq_socket_ops = {
+	.start  = llc_seq_start,
+	.next   = llc_seq_next,
+	.stop   = llc_seq_stop,
+	.show   = llc_seq_socket_show,
+};
+
+struct seq_operations llc_seq_core_ops = {
+	.start  = llc_seq_start,
+	.next   = llc_seq_next,
+	.stop   = llc_seq_stop,
+	.show   = llc_seq_core_show,
+};
+
+static int llc_seq_socket_open(struct inode *inode, struct file *file)
+{
+	return seq_open(file, &llc_seq_socket_ops);
+}
+
+static int llc_seq_core_open(struct inode *inode, struct file *file)
+{
+	return seq_open(file, &llc_seq_core_ops);
+}
+
+static int llc_proc_perms(struct inode* inode, int op)
+{
+	return 0;
+}
+
+static struct file_operations llc_seq_socket_fops = {
+	.open		= llc_seq_socket_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= seq_release,
+};
+
+static struct file_operations llc_seq_core_fops = {
+	.open		= llc_seq_core_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= seq_release,
+};
+
+static struct inode_operations llc_seq_inode = {
+	.permission	= llc_proc_perms,
+};
+
+static struct proc_dir_entry *llc_proc_dir;
+
+int __init llc_proc_init(void)
+{
+	int rc = -ENOMEM;
+	struct proc_dir_entry *p;
+
+	llc_proc_dir = proc_mkdir("llc", proc_net);
+	if (!llc_proc_dir)
+		goto out;
+
+	p = create_proc_entry("socket", 0, llc_proc_dir);
+	if (!p)
+		goto out_socket;
+
+	p->proc_fops = &llc_seq_socket_fops;
+	p->proc_iops = &llc_seq_inode;
+
+	p = create_proc_entry("core", 0, llc_proc_dir);
+	if (!p)
+		goto out_core;
+
+	p->proc_fops = &llc_seq_core_fops;
+	p->proc_iops = &llc_seq_inode;
+
+	rc = 0;
+out:
+	return rc;
+out_core:
+	remove_proc_entry("socket", llc_proc_dir);
+out_socket:
+	remove_proc_entry("llc", proc_net);
+	goto out;
+}
+
+void __exit llc_proc_exit(void)
+{
+	remove_proc_entry("socket", llc_proc_dir);
+	remove_proc_entry("core", llc_proc_dir);
+	remove_proc_entry("llc", proc_net);
+}
+#else /* CONFIG_PROC_FS */
+int __init llc_proc_init(void)
+{
+	return 0;
+}
+
+void __exit llc_proc_exit(void)
+{
+}
+#endif /* CONFIG_PROC_FS */
diff -Nru a/net/llc/llc_sap.c b/net/llc/llc_sap.c
--- a/net/llc/llc_sap.c	Mon Oct  7 11:25:36 2002
+++ b/net/llc/llc_sap.c	Mon Oct  7 11:25:36 2002
@@ -20,17 +20,9 @@
 #include <net/sock.h>
 #include <linux/tcp.h>
 #include <net/llc_main.h>
-#include <net/llc_mac.h>
 #include <net/llc_pdu.h>
 #include <linux/if_tr.h>
 
-static int llc_sap_next_state(struct llc_sap *sap, struct sk_buff *skb);
-static int llc_exec_sap_trans_actions(struct llc_sap *sap,
-				      struct llc_sap_state_trans *trans,
-				      struct sk_buff *skb);
-static struct llc_sap_state_trans *llc_find_sap_trans(struct llc_sap *sap,
-						      struct sk_buff *skb);
-
 /**
  *	llc_sap_assign_sock - adds a connection to a SAP
  *	@sap: pointer to SAP.
@@ -76,51 +68,15 @@
 }
 
 /**
- *	llc_sap_state_process - sends event to SAP state machine
- *	@sap: sap to use
- *	@skb: pointer to occurred event
- *
- *	After executing actions of the event, upper layer will be indicated
- *	if needed(on receiving an UI frame). sk can be null for the
- *	datalink_proto case.
- */
-void llc_sap_state_process(struct llc_sap *sap, struct sk_buff *skb)
-{
-	struct llc_sap_state_ev *ev = llc_sap_ev(skb);
-
-	/*
-	 * We have to hold the skb, because llc_sap_next_state
-	 * will kfree it in the sending path and we need to
-	 * look at the skb->cb, where we encode llc_sap_state_ev.
-	 */
-	skb_get(skb);
-	ev->ind_cfm_flag = 0;
-	llc_sap_next_state(sap, skb);
-	if (ev->ind_cfm_flag == LLC_IND) {
-		if (skb->sk->state == TCP_LISTEN)
-			kfree_skb(skb);
-		else {
-			llc_save_primitive(skb, ev->prim);
-
-			/* queue skb to the user. */
-			if (sock_queue_rcv_skb(skb->sk, skb))
-				kfree_skb(skb);
-		}
-	} 
-	kfree_skb(skb);
-}
-
-/**
  *	llc_sap_rtn_pdu - Informs upper layer on rx of an UI, XID or TEST pdu.
  *	@sap: pointer to SAP
  *	@skb: received pdu
  */
 void llc_sap_rtn_pdu(struct llc_sap *sap, struct sk_buff *skb)
 {
-	struct llc_pdu_un *pdu;
 	struct llc_sap_state_ev *ev = llc_sap_ev(skb);
+	struct llc_pdu_un *pdu = llc_pdu_un_hdr(skb);
 
-	pdu = llc_pdu_un_hdr(skb);
 	switch (LLC_U_PDU_RSP(pdu)) {
 	case LLC_1_PDU_CMD_TEST:
 		ev->prim = LLC_TEST_PRIM;	break;
@@ -133,38 +89,6 @@
 }
 
 /**
- *	llc_sap_next_state - finds transition, execs actions & change SAP state
- *	@sap: pointer to SAP
- *	@skb: happened event
- *
- *	This function finds transition that matches with happened event, then
- *	executes related actions and finally changes state of SAP. It returns
- *	0 on success and 1 for failure.
- */
-static int llc_sap_next_state(struct llc_sap *sap, struct sk_buff *skb)
-{
-	int rc = 1;
-	struct llc_sap_state_trans *trans;
-
-	if (sap->state <= LLC_NR_SAP_STATES) {
-		trans = llc_find_sap_trans(sap, skb);
-		if (trans) {
-			/* got the state to which we next transition; perform
-			 * the actions associated with this transition before
-			 * actually transitioning to the next state
-			 */
-			rc = llc_exec_sap_trans_actions(sap, trans, skb);
-			if (!rc)
-				/* transition SAP to next state if all actions
-				 * execute successfully
-				 */
-				sap->state = trans->next_state;
-		}
-	}
-	return rc;
-}
-
-/**
  *	llc_find_sap_trans - finds transition for event
  *	@sap: pointer to SAP
  *	@skb: happened event
@@ -180,13 +104,13 @@
 	struct llc_sap_state_trans *rc = NULL;
 	struct llc_sap_state_trans **next_trans;
 	struct llc_sap_state *curr_state = &llc_sap_state_table[sap->state - 1];
-	/* search thru events for this state until list exhausted or until
+	/*
+	 * Search thru events for this state until list exhausted or until
 	 * its obvious the event is not valid for the current state
 	 */
 	for (next_trans = curr_state->transitions; next_trans[i]->ev; i++)
 		if (!next_trans[i]->ev(sap, skb)) {
-			/* got event match; return it */
-			rc = next_trans[i];
+			rc = next_trans[i]; /* got event match; return it */
 			break;
 		}
 	return rc;
@@ -212,4 +136,74 @@
 		if ((*next_action)(sap, skb))
 			rc = 1;
 	return rc;
+}
+
+/**
+ *	llc_sap_next_state - finds transition, execs actions & change SAP state
+ *	@sap: pointer to SAP
+ *	@skb: happened event
+ *
+ *	This function finds transition that matches with happened event, then
+ *	executes related actions and finally changes state of SAP. It returns
+ *	0 on success and 1 for failure.
+ */
+static int llc_sap_next_state(struct llc_sap *sap, struct sk_buff *skb)
+{
+	int rc = 1;
+	struct llc_sap_state_trans *trans;
+
+	if (sap->state > LLC_NR_SAP_STATES)
+		goto out;
+	trans = llc_find_sap_trans(sap, skb);
+	if (!trans)
+		goto out;
+	/*
+	 * Got the state to which we next transition; perform the actions
+	 * associated with this transition before actually transitioning to the
+	 * next state
+	 */
+	rc = llc_exec_sap_trans_actions(sap, trans, skb);
+	if (rc)
+		goto out;
+	/*
+	 * Transition SAP to next state if all actions execute successfully
+	 */
+	sap->state = trans->next_state;
+out:
+	return rc;
+}
+
+/**
+ *	llc_sap_state_process - sends event to SAP state machine
+ *	@sap: sap to use
+ *	@skb: pointer to occurred event
+ *
+ *	After executing actions of the event, upper layer will be indicated
+ *	if needed(on receiving an UI frame). sk can be null for the
+ *	datalink_proto case.
+ */
+void llc_sap_state_process(struct llc_sap *sap, struct sk_buff *skb)
+{
+	struct llc_sap_state_ev *ev = llc_sap_ev(skb);
+
+	/*
+	 * We have to hold the skb, because llc_sap_next_state
+	 * will kfree it in the sending path and we need to
+	 * look at the skb->cb, where we encode llc_sap_state_ev.
+	 */
+	skb_get(skb);
+	ev->ind_cfm_flag = 0;
+	llc_sap_next_state(sap, skb);
+	if (ev->ind_cfm_flag == LLC_IND) {
+		if (skb->sk->state == TCP_LISTEN)
+			kfree_skb(skb);
+		else {
+			llc_save_primitive(skb, ev->prim);
+
+			/* queue skb to the user. */
+			if (sock_queue_rcv_skb(skb->sk, skb))
+				kfree_skb(skb);
+		}
+	} 
+	kfree_skb(skb);
 }
diff -Nru a/net/netsyms.c b/net/netsyms.c
--- a/net/netsyms.c	Mon Oct  7 11:25:32 2002
+++ b/net/netsyms.c	Mon Oct  7 11:25:32 2002
@@ -587,7 +587,6 @@
 
 EXPORT_SYMBOL(register_gifconf);
 
-EXPORT_SYMBOL(net_call_rx_atomic);
 EXPORT_SYMBOL(softnet_data);
 
 #if defined(CONFIG_NET_RADIO) || defined(CONFIG_NET_PCMCIA_RADIO)
diff -Nru a/net/packet/af_packet.c b/net/packet/af_packet.c
--- a/net/packet/af_packet.c	Mon Oct  7 11:25:36 2002
+++ b/net/packet/af_packet.c	Mon Oct  7 11:25:36 2002
@@ -1458,16 +1458,6 @@
 			spin_unlock_bh(&sk->receive_queue.lock);
 			return put_user(amount, (int *)arg);
 		}
-		case FIOSETOWN:
-		case SIOCSPGRP: {
-			int pid;
-			if (get_user(pid, (int *) arg))
-				return -EFAULT; 
-			return f_setown(sock->file, pid, 1);
-		}
-		case FIOGETOWN:
-		case SIOCGPGRP:
-			return put_user(sock->file->f_owner.pid, (int *)arg);
 		case SIOCGSTAMP:
 			if(sk->stamp.tv_sec==0)
 				return -ENOENT;
@@ -1542,14 +1532,6 @@
 #endif
 
 		default:
-			if ((cmd >= SIOCDEVPRIVATE) &&
-			    (cmd <= (SIOCDEVPRIVATE + 15)))
-				return(dev_ioctl(cmd,(void *) arg));
-
-#ifdef CONFIG_NET_RADIO
-			if((cmd >= SIOCIWFIRST) && (cmd <= SIOCIWLAST))
-				return(dev_ioctl(cmd,(void *) arg));
-#endif
 			return -EOPNOTSUPP;
 	}
 	return 0;
diff -Nru a/net/sched/sch_htb.c b/net/sched/sch_htb.c
--- a/net/sched/sch_htb.c	Mon Oct  7 11:25:36 2002
+++ b/net/sched/sch_htb.c	Mon Oct  7 11:25:36 2002
@@ -15,9 +15,11 @@
  *			helped a lot to locate nasty class stall bug
  *		Andi Kleen, Jamal Hadi, Bert Hubert
  *			code review and helpful comments on shaping
+ *		Tomasz Wrona, <tw@eter.tym.pl>
+ *			created test case so that I was able to fix nasty bug
  *		and many others. thanks.
  *
- * $Id: sch_htb.c,v 1.13 2002/05/25 09:04:50 devik Exp $
+ * $Id: sch_htb.c,v 1.14 2002/09/28 12:55:22 devik Exp devik $
  */
 #include <linux/config.h>
 #include <linux/module.h>
@@ -69,7 +71,7 @@
 #define HTB_HYSTERESIS 1/* whether to use mode hysteresis for speedup */
 #define HTB_QLOCK(S) spin_lock_bh(&(S)->dev->queue_lock)
 #define HTB_QUNLOCK(S) spin_unlock_bh(&(S)->dev->queue_lock)
-#define HTB_VER 0x30006	/* major must be matched with number suplied by TC as version */
+#define HTB_VER 0x30007	/* major must be matched with number suplied by TC as version */
 
 #if HTB_VER >> 16 != TC_HTB_PROTOVER
 #error "Mismatched sch_htb.c and pkt_sch.h"
@@ -1496,19 +1498,23 @@
 #endif
 	} else sch_tree_lock(sch);
 
-	cl->un.leaf.quantum = rtab->rate.rate / q->rate2quantum;
-	if (!hopt->quantum && cl->un.leaf.quantum < 1000) {
-		printk(KERN_WARNING "HTB: quantum of class %X is small. Consider r2q change.", cl->classid);
-		cl->un.leaf.quantum = 1000;
-	}
-	if (!hopt->quantum && cl->un.leaf.quantum > 200000) {
-		printk(KERN_WARNING "HTB: quantum of class %X is big. Consider r2q change.", cl->classid);
-		cl->un.leaf.quantum = 200000;
-	}
-	if (hopt->quantum)
-		cl->un.leaf.quantum = hopt->quantum;
-	if ((cl->un.leaf.prio = hopt->prio) >= TC_HTB_NUMPRIO)
-		cl->un.leaf.prio = TC_HTB_NUMPRIO - 1;
+	/* it used to be a nasty bug here, we have to check that node
+           is really leaf before changing cl->un.leaf ! */
+	if (!cl->level) {
+		cl->un.leaf.quantum = rtab->rate.rate / q->rate2quantum;
+		if (!hopt->quantum && cl->un.leaf.quantum < 1000) {
+			printk(KERN_WARNING "HTB: quantum of class %X is small. Consider r2q change.", cl->classid);
+			cl->un.leaf.quantum = 1000;
+		}
+		if (!hopt->quantum && cl->un.leaf.quantum > 200000) {
+			printk(KERN_WARNING "HTB: quantum of class %X is big. Consider r2q change.", cl->classid);
+			cl->un.leaf.quantum = 200000;
+		}
+		if (hopt->quantum)
+			cl->un.leaf.quantum = hopt->quantum;
+		if ((cl->un.leaf.prio = hopt->prio) >= TC_HTB_NUMPRIO)
+			cl->un.leaf.prio = TC_HTB_NUMPRIO - 1;
+	}
 
 	cl->buffer = hopt->buffer;
 	cl->cbuffer = hopt->cbuffer;
diff -Nru a/net/sctp/debug.c b/net/sctp/debug.c
--- a/net/sctp/debug.c	Mon Oct  7 11:25:29 2002
+++ b/net/sctp/debug.c	Mon Oct  7 11:25:29 2002
@@ -97,7 +97,7 @@
 /* These are printable form of variable-length parameters. */
 const char *sctp_param_tbl[SCTP_PARAM_ECN_CAPABLE + 1] = {
 	"",
-	"PARAM_HEATBEAT_INFO",
+	"PARAM_HEARTBEAT_INFO",
 	"",
 	"",
 	"",
diff -Nru a/net/sctp/inqueue.c b/net/sctp/inqueue.c
--- a/net/sctp/inqueue.c	Mon Oct  7 11:25:26 2002
+++ b/net/sctp/inqueue.c	Mon Oct  7 11:25:26 2002
@@ -54,10 +54,7 @@
 	queue->in_progress = NULL;
 
 	/* Create a task for delivering data.  */
-	INIT_LIST_HEAD(&queue->immediate.list);
-	queue->immediate.sync = 0;
-	queue->immediate.routine = NULL;
-	queue->immediate.data = NULL;
+	INIT_WORK(&queue->immediate, NULL, NULL);
 
 	queue->malloced = 0;
 }
@@ -109,7 +106,7 @@
 	 * on the BH related data structures.
 	 */
 	skb_queue_tail(&(q->in), (struct sk_buff *) packet);
-	q->immediate.routine(q->immediate.data);
+	q->immediate.func(q->immediate.data);
 }
 
 /* Extract a chunk from an SCTP inqueue.
@@ -193,7 +190,6 @@
 void sctp_inqueue_set_th_handler(sctp_inqueue_t *q,
 				 void (*callback)(void *), void *arg)
 {
-	q->immediate.routine = callback;
-	q->immediate.data = arg;
+	INIT_WORK(&q->immediate, callback, arg);
 }
 
diff -Nru a/net/sctp/outqueue.c b/net/sctp/outqueue.c
--- a/net/sctp/outqueue.c	Mon Oct  7 11:25:29 2002
+++ b/net/sctp/outqueue.c	Mon Oct  7 11:25:29 2002
@@ -104,7 +104,7 @@
 void sctp_outqueue_teardown(sctp_outqueue_t *q)
 {
 	sctp_transport_t *transport;
-	struct list_head *lchunk, *pos;
+	struct list_head *lchunk, *pos, *temp;
 	sctp_chunk_t *chunk;
 
 	/* Throw away unacknowledged chunks. */
@@ -115,6 +115,13 @@
 					   transmitted_list);
 			sctp_free_chunk(chunk);
 		}
+	}
+
+	/* Throw away chunks that have been gap ACKed.  */
+	list_for_each_safe(lchunk, temp, &q->sacked) {
+		list_del(lchunk);
+		chunk = list_entry(lchunk, sctp_chunk_t, transmitted_list);
+		sctp_free_chunk(chunk);
 	}
 
 	/* Throw away any leftover chunks. */
diff -Nru a/net/sctp/protocol.c b/net/sctp/protocol.c
--- a/net/sctp/protocol.c	Mon Oct  7 11:25:35 2002
+++ b/net/sctp/protocol.c	Mon Oct  7 11:25:35 2002
@@ -119,8 +119,7 @@
 
 	for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) {
 		/* Add the address to the local list.  */
-		/* XXX BUG: sleeping allocation with lock held -DaveM */
-		addr = t_new(struct sockaddr_storage_list, GFP_KERNEL);
+		addr = t_new(struct sockaddr_storage_list, GFP_ATOMIC);
 		if (addr) {
 			INIT_LIST_HEAD(&addr->list);
 			addr->a.v4.sin_family = AF_INET;
@@ -157,8 +156,7 @@
 	read_lock_bh(&in6_dev->lock);
 	for (ifp = in6_dev->addr_list; ifp; ifp = ifp->if_next) {
 		/* Add the address to the local list.  */
-		/* XXX BUG: sleeping allocation with lock held -DaveM */
-		addr = t_new(struct sockaddr_storage_list, GFP_KERNEL);
+		addr = t_new(struct sockaddr_storage_list, GFP_ATOMIC);
 		if (addr) {
 			addr->a.v6.sin6_family = AF_INET6;
 			addr->a.v6.sin6_port = 0;
diff -Nru a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c
--- a/net/sctp/sm_make_chunk.c	Mon Oct  7 11:25:26 2002
+++ b/net/sctp/sm_make_chunk.c	Mon Oct  7 11:25:26 2002
@@ -223,7 +223,7 @@
 
 sctp_chunk_t *sctp_make_init_ack(const sctp_association_t *asoc,
 				 const sctp_chunk_t *chunk,
-				 int priority)
+				 int priority, int unkparam_len)
 {
 	sctp_inithdr_t initack;
 	sctp_chunk_t *retval;
@@ -278,7 +278,10 @@
 	if (!cookie)
 		goto nomem_cookie;
 
-	chunksize = sizeof(initack) + addrs_len + cookie_len;
+	/* Calculate the total size of allocation, include the reserved
+	 * space for reporting unknown parameters if it is specified.
+	 */
+	chunksize = sizeof(initack) + addrs_len + cookie_len + unkparam_len;
 
         /* Tell peer that we'll do ECN only if peer advertised such cap.  */
 	if (asoc->peer.ecn_capable)
@@ -883,25 +886,27 @@
 	return retval;
 }
 
-/* Create an Operation Error chunk.  */
-sctp_chunk_t *sctp_make_op_error(const sctp_association_t *asoc,
-				 const sctp_chunk_t *chunk,
-				 __u16 cause_code, const void *payload,
-				 size_t paylen)
+/* Create an Operation Error chunk with the specified space reserved.
+ * This routine can be used for containing multiple causes in the chunk.
+ */
+sctp_chunk_t *sctp_make_op_error_space(const sctp_association_t *asoc,
+				       const sctp_chunk_t *chunk,
+				       size_t size)
 {
-	sctp_chunk_t *retval = sctp_make_chunk(asoc, SCTP_CID_ERROR, 0,
-					       sizeof(sctp_errhdr_t) + paylen);
+	sctp_chunk_t *retval;
 
+	retval = sctp_make_chunk(asoc, SCTP_CID_ERROR, 0,
+				 sizeof(sctp_errhdr_t) + size);
 	if (!retval)
 		goto nodata;
-	sctp_init_cause(retval, cause_code, payload, paylen);
 
-        /* RFC 2960 6.4 Multi-homed SCTP Endpoints
+	/* RFC 2960 6.4 Multi-homed SCTP Endpoints
 	 *
 	 * An endpoint SHOULD transmit reply chunks (e.g., SACK,
-	 * HEARTBEAT ACK, * etc.) to the same destination transport
-	 * address from which it * received the DATA or control chunk
+	 * HEARTBEAT ACK, etc.) to the same destination transport
+	 * address from which it received the DATA or control chunk
 	 * to which it is replying.
+	 *
 	 */
 	if (chunk)
 		retval->transport = chunk->transport;
@@ -910,6 +915,23 @@
 	return retval;
 }
 
+/* Create an Operation Error chunk.  */
+sctp_chunk_t *sctp_make_op_error(const sctp_association_t *asoc,
+				 const sctp_chunk_t *chunk,
+				 __u16 cause_code, const void *payload,
+				 size_t paylen)
+{
+	sctp_chunk_t *retval = sctp_make_op_error_space(asoc, chunk, paylen);
+
+	if (!retval)
+		goto nodata;
+
+	sctp_init_cause(retval, cause_code, payload, paylen);
+
+nodata:
+	return retval;
+}
+
 /********************************************************************
  * 2nd Level Abstractions
  ********************************************************************/
@@ -1405,6 +1427,162 @@
  * 3rd Level Abstractions
  ********************************************************************/
 
+/* Verify the INIT packet before we process it.  */
+int sctp_verify_init(const sctp_association_t *asoc,
+		     sctp_cid_t cid,
+		     sctp_init_chunk_t *peer_init,
+		     sctp_chunk_t *chunk,
+		     sctp_chunk_t **err_chk_p)
+{
+	sctpParam_t param;
+	uint8_t *end;
+
+	/* FIXME - Verify the fixed fields of the INIT chunk. Also, verify
+	 * the mandatory parameters somewhere here and generate either the
+	 * "Missing mandatory parameter" error or the "Invalid mandatory
+	 * parameter" error. */
+
+	/* Find unrecognized parameters. */
+
+	end = ((uint8_t *)peer_init + ntohs(peer_init->chunk_hdr.length));
+
+	for (param.v = peer_init->init_hdr.params;
+	     param.v < end;
+	     param.v += WORD_ROUND(ntohs(param.p->length))) {
+
+		if (!sctp_verify_param(asoc, param, cid, chunk, err_chk_p))
+			return 0;
+
+	} /* for (loop through all parameters) */
+	 
+	return 1;
+}
+
+
+/* Find unrecognized parameters in the chunk.
+ * Return values:
+ * 	0 - discard the chunk
+ * 	1 - continue with the chunk
+ */
+int sctp_verify_param(const sctp_association_t *asoc,
+		      sctpParam_t param,
+		      sctp_cid_t cid,
+		      sctp_chunk_t *chunk,
+		      sctp_chunk_t **err_chk_p)
+{
+	int retval = 1;
+
+	/* FIXME - This routine is not looking at each parameter per the
+	 * chunk type, i.e., unrecognized parameters should be further
+	 * identified based on the chunk id.
+	 */
+
+	switch (param.p->type) {
+	case SCTP_PARAM_IPV4_ADDRESS:
+	case SCTP_PARAM_IPV6_ADDRESS:
+	case SCTP_PARAM_COOKIE_PRESERVATIVE:
+	/* FIXME - If we don't support the host name parameter, we should
+	 * generate an error for this - Unresolvable address.
+	 */
+	case SCTP_PARAM_HOST_NAME_ADDRESS:
+	case SCTP_PARAM_SUPPORTED_ADDRESS_TYPES:
+	case SCTP_PARAM_STATE_COOKIE:
+	case SCTP_PARAM_HEARTBEAT_INFO:
+	case SCTP_PARAM_UNRECOGNIZED_PARAMETERS:
+	case SCTP_PARAM_ECN_CAPABLE:
+		break;
+	default:
+		SCTP_DEBUG_PRINTK("Unrecognized param: %d for chunk %d.\n",
+				ntohs(param.p->type), cid);
+		return sctp_process_unk_param(asoc, param, chunk, err_chk_p);
+
+		break;
+	}
+	return retval;
+}
+
+/* RFC 3.2.1 & the Implementers Guide 2.2.
+ *
+ * The Parameter Types are encoded such that the
+ * highest-order two bits specify the action that must be
+ * taken if the processing endpoint does not recognize the
+ * Parameter Type.
+ *
+ * 00 - Stop processing this SCTP chunk and discard it,
+ *	do not process any further chunks within it.
+ *
+ * 01 - Stop processing this SCTP chunk and discard it,
+ *	do not process any further chunks within it, and report
+ *	the unrecognized parameter in an 'Unrecognized
+ *	Parameter Type' (in either an ERROR or in the INIT ACK).
+ *
+ * 10 - Skip this parameter and continue processing.
+ *
+ * 11 - Skip this parameter and continue processing but
+ *	report the unrecognized parameter in an
+ *	'Unrecognized Parameter Type' (in either an ERROR or in
+ *	the INIT ACK).
+ *
+ * Return value:
+ * 	0 - discard the chunk
+ * 	1 - continue with the chunk
+ */
+int sctp_process_unk_param(const sctp_association_t *asoc,
+			   sctpParam_t param,
+			   sctp_chunk_t *chunk,
+			   sctp_chunk_t **err_chk_p)
+{
+	int retval = 1;
+
+	switch (param.p->type & SCTP_PARAM_ACTION_MASK) {
+	case SCTP_PARAM_ACTION_DISCARD:
+		retval =  0;
+		break;
+	case SCTP_PARAM_ACTION_DISCARD_ERR:
+		retval =  0;
+		/* Make an ERROR chunk, preparing enough room for
+		 * returning multiple unknown parameters.
+		 */
+		if (NULL == *err_chk_p)
+			*err_chk_p = sctp_make_op_error_space(asoc, chunk,
+					ntohs(chunk->chunk_hdr->length));
+
+		if (*err_chk_p)
+			sctp_init_cause(*err_chk_p, SCTP_ERROR_UNKNOWN_PARAM,
+					(const void *)param.p,
+					WORD_ROUND(ntohs(param.p->length)));
+
+		break;
+	case SCTP_PARAM_ACTION_SKIP:
+		break;
+	case SCTP_PARAM_ACTION_SKIP_ERR:
+		/* Make an ERROR chunk, preparing enough room for
+		 * returning multiple unknown parameters.
+		 */
+		if (NULL == *err_chk_p)
+			*err_chk_p = sctp_make_op_error_space(asoc, chunk,
+					ntohs(chunk->chunk_hdr->length));
+
+		if (*err_chk_p) {
+			sctp_init_cause(*err_chk_p, SCTP_ERROR_UNKNOWN_PARAM,
+					(const void *)param.p,
+					WORD_ROUND(ntohs(param.p->length)));
+		} else {
+			/* If there is no memory for generating the ERROR
+			 * report as specified, an ABORT will be triggered
+			 * to the peer and the association won't be established.
+			 */
+			retval = 0;
+		}
+
+		break;
+	default:
+		break;
+	}
+
+	return retval;
+}
+
 /* Unpack the parameters in an INIT packet.
  * FIXME:  There is no return status to allow callers to do
  * error handling.
@@ -1609,9 +1787,9 @@
 		asoc->peer.cookie = param.cookie->body;
 		break;
 
-	case SCTP_PARAM_HEATBEAT_INFO:
+	case SCTP_PARAM_HEARTBEAT_INFO:
 		SCTP_DEBUG_PRINTK("unimplemented "
-				  "SCTP_PARAM_HEATBEAT_INFO\n");
+				  "SCTP_PARAM_HEARTBEAT_INFO\n");
 		break;
 
 	case SCTP_PARAM_UNRECOGNIZED_PARAMETERS:
@@ -1624,14 +1802,13 @@
 		break;
 
 	default:
+		/* Any unrecognized parameters should have been caught
+		 * and handled by sctp_verify_param() which should be
+		 * called prior to this routine.  Simply log the error
+		 * here.
+		 */
 		SCTP_DEBUG_PRINTK("Ignoring param: %d for association %p.\n",
 				  ntohs(param.p->type), asoc);
-		/* FIXME:  The entire parameter processing really needs
-		 * redesigned.  For now, always return success as doing
-		 * otherwise craters the system.
-		 */
-		retval = 1;
-
 		break;
 	};
 
diff -Nru a/net/sctp/sm_sideeffect.c b/net/sctp/sm_sideeffect.c
--- a/net/sctp/sm_sideeffect.c	Mon Oct  7 11:25:36 2002
+++ b/net/sctp/sm_sideeffect.c	Mon Oct  7 11:25:36 2002
@@ -327,7 +327,8 @@
 
 		case SCTP_CMD_GEN_INIT_ACK:
 			/* Generate an INIT ACK chunk.  */
-			new_obj = sctp_make_init_ack(asoc, chunk, GFP_ATOMIC);
+			new_obj = sctp_make_init_ack(asoc, chunk, GFP_ATOMIC,
+						     0);
 			if (!new_obj)
 				goto nomem;
 
@@ -344,10 +345,20 @@
 		case SCTP_CMD_GEN_COOKIE_ECHO:
 			/* Generate a COOKIE ECHO chunk.  */
 			new_obj = sctp_make_cookie_echo(asoc, chunk);
-			if (!new_obj)
+			if (!new_obj) {
+				if (command->obj.ptr)
+					sctp_free_chunk(command->obj.ptr);
 				goto nomem;
+			}
 			sctp_add_cmd_sf(commands, SCTP_CMD_REPLY,
 					SCTP_CHUNK(new_obj));
+
+			/* If there is an ERROR chunk to be sent along with
+			 * the COOKIE_ECHO, send it, too.
+			 */
+			if (command->obj.ptr)
+				sctp_add_cmd_sf(commands, SCTP_CMD_REPLY,
+						SCTP_CHUNK(command->obj.ptr));
 			break;
 
 		case SCTP_CMD_GEN_SHUTDOWN:
@@ -397,8 +408,7 @@
 			/* Send a full packet to our peer.  */
 			packet = command->obj.ptr;
 			sctp_packet_transmit(packet);
-			sctp_transport_free(packet->transport);
-			sctp_packet_free(packet);
+			sctp_ootb_pkt_free(packet);
 			break;
 
 		case SCTP_CMD_RETRAN:
diff -Nru a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c
--- a/net/sctp/sm_statefuns.c	Mon Oct  7 11:25:25 2002
+++ b/net/sctp/sm_statefuns.c	Mon Oct  7 11:25:25 2002
@@ -194,6 +194,10 @@
 	sctp_chunk_t *chunk = arg;
 	sctp_chunk_t *repl;
 	sctp_association_t *new_asoc;
+	sctp_chunk_t *err_chunk;
+	sctp_packet_t *packet;
+	sctp_unrecognized_param_t *unk_param;
+	int len;
 
 	/* If the packet is an OOTB packet which is temporarily on the
 	 * control endpoint, responding with an ABORT.
@@ -208,6 +212,36 @@
 	if (!chunk->singleton)
 		return SCTP_DISPOSITION_VIOLATION;
 
+	/* Verify the INIT chunk before processing it. */
+	err_chunk = NULL;
+	if (!sctp_verify_init(asoc, chunk->chunk_hdr->type,
+			      (sctp_init_chunk_t *)chunk->chunk_hdr, chunk,
+			      &err_chunk)) {
+		/* This chunk contains fatal error. It is to be discarded.
+		 * Send an ABORT, with causes if there is any.
+		 */
+		if (err_chunk) {
+			packet = sctp_abort_pkt_new(ep, asoc, arg,
+					(__u8 *)(err_chunk->chunk_hdr) +
+					sizeof(sctp_chunkhdr_t),
+					ntohs(err_chunk->chunk_hdr->length) -
+					sizeof(sctp_chunkhdr_t));
+
+			sctp_free_chunk(err_chunk);
+
+			if (packet) {
+				sctp_add_cmd_sf(commands, SCTP_CMD_SEND_PKT,
+						SCTP_PACKET(packet));
+				return SCTP_DISPOSITION_CONSUME;
+			} else {
+				return SCTP_DISPOSITION_NOMEM;
+			}
+		} else {
+			return sctp_sf_tabort_8_4_8(ep, asoc, type, arg,
+						    commands);
+		}
+	}
+
         /* Grab the INIT header.  */
 	chunk->subh.init_hdr = (sctp_inithdr_t *)chunk->skb->data;
 
@@ -230,10 +264,41 @@
 	sctp_add_cmd_sf(commands, SCTP_CMD_NEW_ASOC, SCTP_ASOC(new_asoc));
 
 	/* B) "Z" shall respond immediately with an INIT ACK chunk.  */
-	repl = sctp_make_init_ack(new_asoc, chunk, GFP_ATOMIC);
+
+	/* If there are errors need to be reported for unknown parameters,
+	 * make sure to reserve enough room in the INIT ACK for them.
+	 */
+	len = 0;
+	if (err_chunk)
+		len = ntohs(err_chunk->chunk_hdr->length) -
+			sizeof(sctp_chunkhdr_t);
+
+	repl = sctp_make_init_ack(new_asoc, chunk, GFP_ATOMIC, len);
 	if (!repl)
 		goto nomem_ack;
 
+	/* If there are errors need to be reported for unknown parameters,
+	 * include them in the outgoing INIT ACK as "Unrecognized parameter"
+	 * parameter.
+	 */
+	if (err_chunk) {
+		/* Get the "Unrecognized parameter" parameter(s) out of the
+		 * ERROR chunk generated by sctp_verify_init(). Since the
+		 * error cause code for "unknown parameter" and the
+		 * "Unrecognized parameter" type is the same, we can
+		 * construct the parameters in INIT ACK by copying the
+		 * ERROR causes over.
+		 */
+		unk_param = (sctp_unrecognized_param_t *)
+			    ((__u8 *)(err_chunk->chunk_hdr) +
+			    sizeof(sctp_chunkhdr_t));
+		/* Replace the cause code with the "Unrecognized parameter"
+		 * parameter type.
+		 */
+		sctp_addto_chunk(repl, len, unk_param);
+		sctp_free_chunk(err_chunk);
+	}
+
 	sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(repl));
 
 	/*
@@ -248,6 +313,9 @@
 
 nomem_ack:
 	sctp_association_free(new_asoc);
+	if (err_chunk)
+		sctp_free_chunk(err_chunk);
+
 nomem:
 	return SCTP_DISPOSITION_NOMEM;
 }
@@ -289,6 +357,10 @@
 	sctp_chunk_t *chunk = arg;
 	sctp_init_chunk_t *initchunk;
 	__u32 init_tag;
+	sctp_chunk_t *err_chunk;
+	sctp_packet_t *packet;
+	sctp_disposition_t ret;
+
 
 	/* 6.10 Bundling
 	 * An endpoint MUST NOT bundle INIT, INIT ACK or
@@ -319,6 +391,49 @@
 		return SCTP_DISPOSITION_DELETE_TCB;
 	}
 
+	/* Verify the INIT chunk before processing it. */
+	err_chunk = NULL;
+	if (!sctp_verify_init(asoc, chunk->chunk_hdr->type,
+			      (sctp_init_chunk_t *)chunk->chunk_hdr, chunk,
+			      &err_chunk)) {
+		/* This chunk contains fatal error. It is to be discarded.
+		 * Send an ABORT, with causes if there is any.
+		 */
+		if (err_chunk) {
+			packet = sctp_abort_pkt_new(ep, asoc, arg,
+					(__u8 *)(err_chunk->chunk_hdr) +
+					sizeof(sctp_chunkhdr_t),
+					ntohs(err_chunk->chunk_hdr->length) -
+					sizeof(sctp_chunkhdr_t));
+
+			sctp_free_chunk(err_chunk);
+
+			if (packet) {
+				sctp_add_cmd_sf(commands, SCTP_CMD_SEND_PKT,
+						SCTP_PACKET(packet));
+				sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE,
+						SCTP_STATE(SCTP_STATE_CLOSED));
+				sctp_add_cmd_sf(commands, SCTP_CMD_DELETE_TCB,
+						SCTP_NULL());
+				return SCTP_DISPOSITION_CONSUME;
+			} else {
+				sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE,
+						SCTP_STATE(SCTP_STATE_CLOSED));
+				sctp_add_cmd_sf(commands, SCTP_CMD_DELETE_TCB,
+						SCTP_NULL());
+				return SCTP_DISPOSITION_NOMEM;
+			}
+		} else {
+			ret = sctp_sf_tabort_8_4_8(ep, asoc, type, arg,
+						   commands);
+			sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE,
+					SCTP_STATE(SCTP_STATE_CLOSED));
+			sctp_add_cmd_sf(commands, SCTP_CMD_DELETE_TCB,
+					SCTP_NULL());
+			return ret;
+		}
+	}
+
 	/* Tag the variable length paramters.  Note that we never
 	 * convert the parameters in an INIT chunk.
 	 */
@@ -345,7 +460,12 @@
 	/* 5.1 C) "A" shall then send the State Cookie received in the
 	 * INIT ACK chunk in a COOKIE ECHO chunk, ...
 	 */
-	sctp_add_cmd_sf(commands, SCTP_CMD_GEN_COOKIE_ECHO, SCTP_NULL());
+	/* If there is any errors to report, send the ERROR chunk generated
+	 * for unknown parameters as well.
+	 */
+	sctp_add_cmd_sf(commands, SCTP_CMD_GEN_COOKIE_ECHO,
+			SCTP_CHUNK(err_chunk));
+
 	return SCTP_DISPOSITION_CONSUME;
 
 nomem:
@@ -579,7 +699,7 @@
 	 * HEARTBEAT is sent (see Section 8.3).
 	 */
 
-	hbinfo.param_hdr.type = SCTP_PARAM_HEATBEAT_INFO;
+	hbinfo.param_hdr.type = SCTP_PARAM_HEARTBEAT_INFO;
 	hbinfo.param_hdr.length = htons(sizeof(sctp_sender_hb_info_t));
 	hbinfo.daddr = transport->ipaddr;
 	hbinfo.sent_at = jiffies;
@@ -852,6 +972,11 @@
 	sctp_chunk_t *chunk = arg;
 	sctp_chunk_t *repl;
 	sctp_association_t *new_asoc;
+	sctp_chunk_t *err_chunk;
+	sctp_packet_t *packet;
+	sctp_unrecognized_param_t *unk_param;
+	int len;
+
 
 	/* 6.10 Bundling
 	 * An endpoint MUST NOT bundle INIT, INIT ACK or
@@ -866,6 +991,36 @@
 	/* Tag the variable length parameters.  */
 	chunk->param_hdr.v = skb_pull(chunk->skb, sizeof(sctp_inithdr_t));
 
+	/* Verify the INIT chunk before processing it. */
+	err_chunk = NULL;
+	if (!sctp_verify_init(asoc, chunk->chunk_hdr->type,
+			      (sctp_init_chunk_t *)chunk->chunk_hdr, chunk,
+			      &err_chunk)) {
+		/* This chunk contains fatal error. It is to be discarded.
+		 * Send an ABORT, with causes if there is any.
+		 */
+		if (err_chunk) {
+			packet = sctp_abort_pkt_new(ep, asoc, arg,
+					(__u8 *)(err_chunk->chunk_hdr) +
+					sizeof(sctp_chunkhdr_t),
+					ntohs(err_chunk->chunk_hdr->length) -
+					sizeof(sctp_chunkhdr_t));
+
+			sctp_free_chunk(err_chunk);
+
+			if (packet) {
+				sctp_add_cmd_sf(commands, SCTP_CMD_SEND_PKT,
+						SCTP_PACKET(packet));
+				return SCTP_DISPOSITION_CONSUME;
+			} else {
+				return SCTP_DISPOSITION_NOMEM;
+			}
+		} else {
+			return sctp_sf_tabort_8_4_8(ep, asoc, type, arg,
+						    commands);
+		}
+	}
+
 	/*
 	 * Other parameters for the endpoint SHOULD be copied from the
 	 * existing parameters of the association (e.g. number of
@@ -887,10 +1042,41 @@
 	sctp_tietags_populate(new_asoc, asoc);
 
 	/* B) "Z" shall respond immediately with an INIT ACK chunk.  */
-	repl = sctp_make_init_ack(new_asoc, chunk, GFP_ATOMIC);
+
+	/* If there are errors need to be reported for unknown parameters,
+	 * make sure to reserve enough room in the INIT ACK for them.
+	 */
+	len = 0;
+	if (err_chunk) {
+		len = ntohs(err_chunk->chunk_hdr->length) -
+			sizeof(sctp_chunkhdr_t);
+	}
+	repl = sctp_make_init_ack(new_asoc, chunk, GFP_ATOMIC, len);
 	if (!repl)
 		goto nomem;
 
+	/* If there are errors need to be reported for unknown parameters,
+	 * include them in the outgoing INIT ACK as "Unrecognized parameter"
+	 * parameter.
+	 */
+	if (err_chunk) {
+		/* Get the "Unrecognized parameter" parameter(s) out of the
+		 * ERROR chunk generated by sctp_verify_init(). Since the
+		 * error cause code for "unknown parameter" and the
+		 * "Unrecognized parameter" type is the same, we can
+		 * construct the parameters in INIT ACK by copying the
+		 * ERROR causes over.
+		 */
+		unk_param = (sctp_unrecognized_param_t *)
+			    ((__u8 *)(err_chunk->chunk_hdr) +
+			    sizeof(sctp_chunkhdr_t));
+		/* Replace the cause code with the "Unrecognized parameter"
+		 * parameter type.
+		 */
+		sctp_addto_chunk(repl, len, unk_param);
+		sctp_free_chunk(err_chunk);
+	}
+
 	sctp_add_cmd_sf(commands, SCTP_CMD_NEW_ASOC, SCTP_ASOC(new_asoc));
 	sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(repl));
 
@@ -903,6 +1089,9 @@
 	return SCTP_DISPOSITION_CONSUME;
 
 nomem:
+	if (err_chunk)
+		sctp_free_chunk(err_chunk);
+
 	return SCTP_DISPOSITION_NOMEM;
 }
 
@@ -1948,17 +2137,16 @@
 
 	/* This is a new TSN.  */
 
-	/* If we don't have any room in our receive window, discard.
-	 * Actually, allow a little bit of overflow (up to a MTU of
-	 * of overflow).
+	/* Discard if there is no room in the receive window.
+	 * Actually, allow a little bit of overflow (up to a MTU).
 	 */
 	datalen = ntohs(chunk->chunk_hdr->length);
 	datalen -= sizeof(sctp_data_chunk_t);
 
-	if (!asoc->rwnd || (datalen > asoc->frag_point)) {
+	if (asoc->rwnd_over || (datalen > asoc->rwnd + asoc->frag_point)) {
 		SCTP_DEBUG_PRINTK("Discarding tsn: %u datalen: %Zd, "
 				  "rwnd: %d\n", tsn, datalen, asoc->rwnd);
-		goto discard_noforce;
+		goto discard_force;
 	}
 
 	/*
@@ -2330,59 +2518,32 @@
 					sctp_cmd_seq_t *commands)
 {
 	sctp_packet_t *packet = NULL;
-	sctp_transport_t *transport = NULL;
 	sctp_chunk_t *chunk = arg;
 	sctp_chunk_t *abort;
-	__u16 sport;
-	__u16 dport;
-	__u32 vtag;
-
-	/* Grub in chunk and endpoint for kewl bitz. */
-	sport = ntohs(chunk->sctp_hdr->dest);
-	dport = ntohs(chunk->sctp_hdr->source);
-	/* -- Make sure the ABORT packet's V-tag is the same as the
-	 *    inbound packet if no association exists, otherwise use
-	 *    the peer's vtag.
-	 */
-	if (asoc)
-		vtag = asoc->peer.i.init_tag;
-	else
-		vtag = ntohl(chunk->sctp_hdr->vtag);
 
-	/* Make a transport for the bucket, Eliza... */
-	transport = sctp_transport_new(sctp_source(chunk), GFP_ATOMIC);
-	if (!transport)
-		goto nomem;
+	packet = sctp_ootb_pkt_new(asoc, chunk);
 
-	/* Make a packet for the ABORT to go into. */
-	packet = t_new(sctp_packet_t, GFP_ATOMIC);
-	if (!packet)
-		goto nomem_packet;
-
-	packet = sctp_packet_init(packet, transport, sport, dport);
-	packet = sctp_packet_config(packet, vtag, 0, NULL);
-
-	/* Make an ABORT.
-	 * This will set the T bit since we have no association.
-	 */
-	abort = sctp_make_abort(NULL, chunk, 0);
-	if (!abort)
-		goto nomem_chunk;
+	if (packet) {
+		/* Make an ABORT. The T bit will be set if the asoc
+		 * is NULL.
+         	 */
+        	abort = sctp_make_abort(asoc, chunk, 0);
+		if (!abort) {
+			sctp_ootb_pkt_free(packet);
+			return SCTP_DISPOSITION_NOMEM;
+		}
 
-	/* Set the skb to the belonging sock for accounting.  */
-	abort->skb->sk = ep->base.sk;
+		/* Set the skb to the belonging sock for accounting.  */
+		abort->skb->sk = ep->base.sk;
 
-	sctp_packet_append_chunk(packet, abort);
-	sctp_add_cmd_sf(commands, SCTP_CMD_SEND_PKT, SCTP_PACKET(packet));
-	return SCTP_DISPOSITION_DISCARD;
+		sctp_packet_append_chunk(packet, abort);
 
-nomem_chunk:
-	sctp_packet_free(packet);
+		sctp_add_cmd_sf(commands, SCTP_CMD_SEND_PKT,
+				SCTP_PACKET(packet));
 
-nomem_packet:
-	sctp_transport_free(transport);
+		return SCTP_DISPOSITION_CONSUME;
+	}
 
-nomem:
 	return SCTP_DISPOSITION_NOMEM;
 }
 
@@ -2560,60 +2721,35 @@
 				      sctp_cmd_seq_t *commands)
 {
 	sctp_packet_t *packet = NULL;
-	sctp_transport_t *transport = NULL;
 	sctp_chunk_t *chunk = arg;
 	sctp_chunk_t *shut;
-	__u16 sport;
-	__u16 dport;
-	__u32 vtag;
-
-	/* Grub in chunk and endpoint for kewl bitz. */
-	sport = ntohs(chunk->sctp_hdr->dest);
-	dport = ntohs(chunk->sctp_hdr->source);
-
-	/* Make sure the ABORT packet's V-tag is the same as the
-	 * inbound packet if no association exists, otherwise use
-	 * the peer's vtag.
-	 */
-	vtag = ntohl(chunk->sctp_hdr->vtag);
-
-	/* Make a transport for the bucket, Eliza... */
-	transport = sctp_transport_new(sctp_source(chunk), GFP_ATOMIC);
-	if (!transport)
-		goto nomem;
-
-	/* Make a packet for the ABORT to go into. */
-	packet = t_new(sctp_packet_t, GFP_ATOMIC);
-	if (!packet)
-		goto nomem_packet;
-
-	packet = sctp_packet_init(packet, transport, sport, dport);
-	packet = sctp_packet_config(packet, vtag, 0, NULL);
 
-	/* Make an ABORT.
-	 * This will set the T bit since we have no association.
-	 */
-	shut = sctp_make_shutdown_complete(NULL, chunk);
-	if (!shut)
-		goto nomem_chunk;
+	packet = sctp_ootb_pkt_new(asoc, chunk);
 
-	/* Set the skb to the belonging sock for accounting.  */
-	shut->skb->sk = ep->base.sk;
+	if (packet) {
+		/* Make an SHUTDOWN_COMPLETE.
+         	 * The T bit will be set if the asoc is NULL.
+         	 */
+		shut = sctp_make_shutdown_complete(asoc, chunk);
+		if (!shut) {
+			sctp_ootb_pkt_free(packet);
+			return SCTP_DISPOSITION_NOMEM;
+		}
 
-	sctp_packet_append_chunk(packet, shut);
-	sctp_add_cmd_sf(commands, SCTP_CMD_SEND_PKT, SCTP_PACKET(packet));
+		/* Set the skb to the belonging sock for accounting.  */
+		shut->skb->sk = ep->base.sk;
 
-	return SCTP_DISPOSITION_CONSUME;
+		sctp_packet_append_chunk(packet, shut);
 
-nomem_chunk:
-	sctp_packet_free(packet);
+		sctp_add_cmd_sf(commands, SCTP_CMD_SEND_PKT,
+				SCTP_PACKET(packet));
 
-nomem_packet:
-	sctp_transport_free(transport);
+		return SCTP_DISPOSITION_CONSUME;
+	}
 
-nomem:
 	return SCTP_DISPOSITION_NOMEM;
 }
+
 /*
  * Process an unknown chunk.
  *
@@ -3948,4 +4084,94 @@
 
 	skb_pull(chunk->skb, (num_blocks + num_dup_tsns) * sizeof(__u32));
 	return sack;
+}
+
+/* Create an ABORT packet to be sent as a response, with the specified
+ * error causes.
+ */
+sctp_packet_t *sctp_abort_pkt_new(const sctp_endpoint_t *ep,
+				 const sctp_association_t *asoc,
+				 sctp_chunk_t *chunk,
+				 const void *payload,
+				 size_t paylen)
+{
+	sctp_packet_t *packet;
+	sctp_chunk_t *abort;
+
+	packet = sctp_ootb_pkt_new(asoc, chunk);
+
+	if (packet) {
+		/* Make an ABORT.
+		 * The T bit will be set if the asoc is NULL.
+		 */
+		abort = sctp_make_abort(asoc, chunk, paylen);
+		if (!abort) {
+			sctp_ootb_pkt_free(packet);
+			return NULL;
+		}
+		/* Add specified error causes, i.e., payload, to the
+		 * end of the chunk.
+		 */
+		sctp_addto_chunk(abort, paylen, payload);
+
+		/* Set the skb to the belonging sock for accounting.  */
+		abort->skb->sk = ep->base.sk;
+
+		sctp_packet_append_chunk(packet, abort);
+
+	}
+
+	return packet;
+}
+
+/* Allocate a packet for responding in the OOTB conditions.  */
+sctp_packet_t *sctp_ootb_pkt_new(const sctp_association_t *asoc,
+					const sctp_chunk_t *chunk)
+{
+	sctp_packet_t *packet;
+	sctp_transport_t *transport;
+	__u16 sport;
+	__u16 dport;
+	__u32 vtag;
+
+	/* Get the source and destination port from the inbound packet.  */
+	sport = ntohs(chunk->sctp_hdr->dest);
+	dport = ntohs(chunk->sctp_hdr->source);
+
+	/* The V-tag is going to be the same as the inbound packet if no
+	 * association exists, otherwise, use the peer's vtag.
+	 */
+	if (asoc) {
+		vtag = asoc->peer.i.init_tag;
+	} else {
+		vtag = ntohl(chunk->sctp_hdr->vtag);
+	}
+
+	/* Make a transport for the bucket, Eliza... */
+	transport = sctp_transport_new(sctp_source(chunk), GFP_ATOMIC);
+
+	if (!transport)
+		goto nomem;
+
+	/* Allocate a new packet for sending the response. */
+	packet = t_new(sctp_packet_t, GFP_ATOMIC);
+	if (!packet)
+		goto nomem_packet;
+
+	packet = sctp_packet_init(packet, transport, sport, dport);
+	packet = sctp_packet_config(packet, vtag, 0, NULL);
+
+	return packet;
+
+nomem_packet:
+	sctp_transport_free(transport);
+nomem:
+	return NULL;
+}
+
+/* Free the packet allocated earlier for responding in the OOTB condition.  */
+void sctp_ootb_pkt_free(sctp_packet_t *packet)
+{
+	sctp_transport_free(packet->transport);
+	sctp_packet_free(packet);
 }
diff -Nru a/net/sctp/socket.c b/net/sctp/socket.c
--- a/net/sctp/socket.c	Mon Oct  7 11:25:40 2002
+++ b/net/sctp/socket.c	Mon Oct  7 11:25:40 2002
@@ -4,41 +4,41 @@
  * Copyright (c) 2001-2002 Intel Corp.
  * Copyright (c) 2001-2002 Nokia, Inc.
  * Copyright (c) 2001 La Monte H.P. Yarroll
- * 
+ *
  * This file is part of the SCTP kernel reference Implementation
- * 
+ *
  * These functions interface with the sockets layer to implement the
  * SCTP Extensions for the Sockets API.
- * 
+ *
  * Note that the descriptions from the specification are USER level
  * functions--this file is the functions which populate the struct proto
  * for SCTP which is the BOTTOM of the sockets interface.
- * 
- * The SCTP reference implementation is free software; 
- * you can redistribute it and/or modify it under the terms of 
+ *
+ * The SCTP reference implementation is free software;
+ * you can redistribute it and/or modify it under the terms of
  * the GNU General Public License as published by
  * the Free Software Foundation; either version 2, or (at your option)
  * any later version.
- * 
- * The SCTP reference implementation is distributed in the hope that it 
+ *
+ * The SCTP reference implementation 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 GNU CC; see the file COPYING.  If not, write to
  * the Free Software Foundation, 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.  
- * 
+ * Boston, MA 02111-1307, USA.
+ *
  * Please send any bug reports or fixes you make to the
  * email address(es):
  *    lksctp developers <lksctp-developers@lists.sourceforge.net>
- * 
+ *
  * Or submit a bug report through the following website:
  *    http://www.sf.net/projects/lksctp
  *
- * Written or modified by: 
+ * Written or modified by:
  *    La Monte H.P. Yarroll <piggy@acm.org>
  *    Narasimha Budihal     <narsi@refcode.org>
  *    Karl Knutson          <karl@athena.chicago.il.us>
@@ -47,7 +47,7 @@
  *    Daisy Chang           <daisyc@us.ibm.com>
  *    Sridhar Samudrala     <samudrala@us.ibm.com>
  *    Inaky Perez-Gonzalez  <inaky.gonzalez@intel.com>
- * 
+ *
  * Any bugs reported given to us we will try to fix... any fixes shared will
  * be incorporated into the next SCTP release.
  */
@@ -67,14 +67,18 @@
 #include <net/icmp.h>
 #include <net/route.h>
 #include <net/ipv6.h>
-#include <net/inet_common.h> 
+#include <net/inet_common.h>
 
 #include <linux/socket.h> /* for sa_family_t */
 #include <net/sock.h>
-#include <net/sctp/sctp.h> 
+#include <net/sctp/sctp.h>
+
+/* WARNING:  Please do not remove the SCTP_STATIC attribute to
+ * any of the functions below as they are used to export functions
+ * used by a project regression testsuite.
+ */
 
 /* Forward declarations for internal helper functions. */
-static void __sctp_write_space(sctp_association_t *asoc);
 static int sctp_writeable(struct sock *sk);
 static inline int sctp_wspace(sctp_association_t *asoc);
 static inline void sctp_set_owner_w(sctp_chunk_t *chunk);
@@ -92,8 +96,7 @@
 static int sctp_bindx_rem(struct sock *, struct sockaddr_storage *, int);
 static int sctp_do_bind(struct sock *, sockaddr_storage_t *, int);
 static int sctp_autobind(struct sock *sk);
-static sctp_bind_bucket_t *sctp_bucket_create(sctp_bind_hashbucket_t *head,
-					      unsigned short snum);
+
 
 /* API 3.1.2 bind() - UDP Style Syntax
  * The syntax of bind() is,
@@ -132,7 +135,8 @@
 static long sctp_get_port_local(struct sock *, unsigned short);
 
 /* Bind a local address either to an endpoint or to an association.  */
-static int sctp_do_bind(struct sock *sk, sockaddr_storage_t *newaddr, int addr_len)
+SCTP_STATIC int sctp_do_bind(struct sock *sk, sockaddr_storage_t *newaddr,
+			     int addr_len)
 {
 	sctp_opt_t *sp = sctp_sk(sk);
 	sctp_endpoint_t *ep = sp->ep;
@@ -151,7 +155,7 @@
 	if (PF_INET == sk->family) {
 		if (sa_family != AF_INET)
 			return -EINVAL;
-	}	
+	}
 
 	/* Make a local copy of the new address.  */
 	tmpaddr = *newaddr;
@@ -317,7 +321,7 @@
  * sctp_bindx() for a lock-protected call.
  */
 
-static int __sctp_bindx(struct sock *sk, struct sockaddr_storage *addrs, 
+static int __sctp_bindx(struct sock *sk, struct sockaddr_storage *addrs,
 			int addrcnt, int flags)
 {
 	int retval = 0;
@@ -603,9 +607,9 @@
  *
  * Returns 0 if ok, <0 errno code on error.
  */
-static int sctp_setsockopt_bindx(struct sock* sk,
-				 struct sockaddr_storage *addrs,
-				 int addrssize, int op)
+SCTP_STATIC int sctp_setsockopt_bindx(struct sock* sk,
+				      struct sockaddr_storage *addrs,
+				      int addrssize, int op)
 {
 	struct sockaddr_storage *kaddrs;
 	int err;
@@ -659,7 +663,7 @@
  * If sd in the close() call is a branched-off socket representing only
  * one association, the shutdown is performed on that association only.
  */
-static void sctp_close(struct sock *sk, long timeout)
+SCTP_STATIC void sctp_close(struct sock *sk, long timeout)
 {
 	sctp_endpoint_t *ep;
 	sctp_association_t *asoc;
@@ -727,9 +731,9 @@
 /* BUG:  We do not implement timeouts.  */
 /* BUG:  We do not implement the equivalent of wait_for_tcp_memory(). */
 
-static int sctp_msghdr_parse(const struct msghdr *, sctp_cmsgs_t *);
+SCTP_STATIC int sctp_msghdr_parse(const struct msghdr *, sctp_cmsgs_t *);
 
-static int sctp_sendmsg(struct sock *sk, struct msghdr *msg, int size)
+SCTP_STATIC int sctp_sendmsg(struct sock *sk, struct msghdr *msg, int size)
 {
 	sctp_opt_t *sp;
 	sctp_endpoint_t *ep;
@@ -931,7 +935,7 @@
 					= sinit->sinit_max_attempts;
 			}
 			if (sinit->sinit_max_init_timeo) {
-				asoc->max_init_timeo 
+				asoc->max_init_timeo
 					= sinit->sinit_max_init_timeo * HZ;
 			}
 		}
@@ -1094,8 +1098,8 @@
  */
 static struct sk_buff *sctp_skb_recv_datagram(struct sock *, int, int, int *);
 
-static int sctp_recvmsg(struct sock *sk, struct msghdr *msg, int len,
-			int noblock, int flags, int *addr_len)
+SCTP_STATIC int sctp_recvmsg(struct sock *sk, struct msghdr *msg, int len,
+			     int noblock, int flags, int *addr_len)
 {
 	sctp_ulpevent_t *event = NULL;
 	sctp_opt_t *sp = sctp_sk(sk);
@@ -1170,7 +1174,7 @@
 	return err;
 }
 
-static inline int sctp_setsockopt_disable_fragments(struct sock *sk, 
+static inline int sctp_setsockopt_disable_fragments(struct sock *sk,
 						    char *optval, int optlen)
 {
 	int val;
@@ -1186,7 +1190,7 @@
 	return 0;
 }
 
-static inline int sctp_setsockopt_set_events(struct sock *sk, char *optval, 
+static inline int sctp_setsockopt_set_events(struct sock *sk, char *optval,
 					     int optlen)
 {
 	if (optlen != sizeof(struct sctp_event_subscribe))
@@ -1196,7 +1200,7 @@
 	return 0;
 }
 
-static inline int sctp_setsockopt_autoclose(struct sock *sk, char *optval, 
+static inline int sctp_setsockopt_autoclose(struct sock *sk, char *optval,
 					    int optlen)
 {
 	sctp_opt_t *sp = sctp_sk(sk);
@@ -1229,7 +1233,7 @@
  *   optval  - the buffer to store the value of the option.
  *   optlen  - the size of the buffer.
  */
-static int sctp_setsockopt(struct sock *sk, int level, int optname,
+SCTP_STATIC int sctp_setsockopt(struct sock *sk, int level, int optname,
 			   char *optval, int optlen)
 {
 	int retval = 0;
@@ -1313,19 +1317,20 @@
 }
 
 /* FIXME: Write comments. */
-static int sctp_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
+SCTP_STATIC int sctp_connect(struct sock *sk, struct sockaddr *uaddr,
+			     int addr_len)
 {
 	return -EOPNOTSUPP; /* STUB */
 }
 
 /* FIXME: Write comments. */
-static int sctp_disconnect(struct sock *sk, int flags)
+SCTP_STATIC int sctp_disconnect(struct sock *sk, int flags)
 {
 	return -EOPNOTSUPP; /* STUB */
 }
 
 /* FIXME: Write comments. */
-static struct sock *sctp_accept(struct sock *sk, int flags, int *err)
+SCTP_STATIC struct sock *sctp_accept(struct sock *sk, int flags, int *err)
 {
 	int error = -EOPNOTSUPP;
 
@@ -1334,7 +1339,7 @@
 }
 
 /* FIXME: Write Comments. */
-static int sctp_ioctl(struct sock *sk, int cmd, unsigned long arg)
+SCTP_STATIC int sctp_ioctl(struct sock *sk, int cmd, unsigned long arg)
 {
 	return -EOPNOTSUPP; /* STUB */
 }
@@ -1343,7 +1348,7 @@
  * initialized the SCTP-specific portion of the sock.
  * The sock structure should already be zero-filled memory.
  */
-static int sctp_init_sock(struct sock *sk)
+SCTP_STATIC int sctp_init_sock(struct sock *sk)
 {
 	sctp_endpoint_t *ep;
 	sctp_protocol_t *proto;
@@ -1428,7 +1433,7 @@
 }
 
 /* Cleanup any SCTP per socket resources.  */
-static int sctp_destroy_sock(struct sock *sk)
+SCTP_STATIC int sctp_destroy_sock(struct sock *sk)
 {
 	sctp_endpoint_t *ep;
 
@@ -1442,7 +1447,7 @@
 }
 
 /* FIXME: Comments needed.  */
-static void sctp_shutdown(struct sock *sk, int how)
+SCTP_STATIC void sctp_shutdown(struct sock *sk, int how)
 {
 	/* UDP-style sockets do not support shutdown. */
 	/* STUB */
@@ -1563,7 +1568,7 @@
 }
 
 /* Helper routine to branch off an association to a new socket.  */
-static int sctp_do_peeloff(sctp_association_t *assoc, struct socket **newsock)
+SCTP_STATIC int sctp_do_peeloff(sctp_association_t *assoc, struct socket **newsock)
 {
 	struct sock *oldsk = assoc->base.sk;
 	struct sock *newsk;
@@ -1652,8 +1657,8 @@
 	return 0;
 }
 
-static int sctp_getsockopt(struct sock *sk, int level, int optname,
-			   char *optval, int *optlen)
+SCTP_STATIC int sctp_getsockopt(struct sock *sk, int level, int optname,
+				char *optval, int *optlen)
 {
 	int retval = 0;
 	sctp_protocol_t *proto = sctp_get_protocol();
@@ -1734,6 +1739,8 @@
  * link to the socket (struct sock) that uses it, the port number and
  * a fastreuse flag (FIXME: NPI ipg).
  */
+static sctp_bind_bucket_t *sctp_bucket_create(sctp_bind_hashbucket_t *head,
+					      unsigned short snum);
 static long sctp_get_port_local(struct sock *sk, unsigned short snum)
 {
 	sctp_bind_hashbucket_t *head; /* hash list */
@@ -1927,7 +1934,7 @@
  *   An application uses listen() to mark a socket as being able to
  *   accept new associations.
  */
-static int sctp_seqpacket_listen(struct sock *sk, int backlog)
+SCTP_STATIC int sctp_seqpacket_listen(struct sock *sk, int backlog)
 {
 	sctp_opt_t *sp = sctp_sk(sk);
 	sctp_endpoint_t *ep = sp->ep;
@@ -2184,7 +2191,8 @@
  * msg_control
  * points here
  */
-static int sctp_msghdr_parse(const struct msghdr *msg, sctp_cmsgs_t *cmsgs)
+SCTP_STATIC int sctp_msghdr_parse(const struct msghdr *msg,
+				  sctp_cmsgs_t *cmsgs)
 {
 	struct cmsghdr *cmsg;
 
@@ -2500,6 +2508,31 @@
 	sk->wmem_queued += SCTP_DATA_SNDSIZE(chunk);
 }
 
+/* If sndbuf has changed, wake up per association sndbuf waiters.  */
+static void __sctp_write_space(sctp_association_t *asoc)
+{
+	struct sock *sk = asoc->base.sk;
+	struct socket *sock = sk->socket;
+
+	if ((sctp_wspace(asoc) > 0) && sock) {
+		if (waitqueue_active(&asoc->wait))
+			wake_up_interruptible(&asoc->wait);
+
+		if (sctp_writeable(sk)) {
+			if (sk->sleep && waitqueue_active(sk->sleep))
+				wake_up_interruptible(sk->sleep);
+
+			/* Note that we try to include the Async I/O support
+			 * here by modeling from the current TCP/UDP code.
+			 * We have not tested with it yet.
+			 */
+			if (sock->fasync_list &&
+			    !(sk->shutdown & SEND_SHUTDOWN))
+				sock_wake_async(sock, 2, POLL_OUT);
+		}
+	}
+}
+
 /* Do accounting for the sndbuf space.
  * Decrement the used sndbuf space of the corresponding association by the
  * data size which was just transmitted(freed).
@@ -2522,7 +2555,8 @@
 }
 
 /* Helper function to wait for space in the sndbuf.  */
-static int sctp_wait_for_sndbuf(sctp_association_t *asoc, long *timeo_p, int msg_len)
+static int sctp_wait_for_sndbuf(sctp_association_t *asoc, long *timeo_p,
+				int msg_len)
 {
 	struct sock *sk = asoc->base.sk;
 	int err = 0;
@@ -2579,31 +2613,6 @@
 do_nonblock:
 	err = -EAGAIN;
 	goto out;
-}
-
-/* If sndbuf has changed, wake up per association sndbuf waiters.  */
-static void __sctp_write_space(sctp_association_t *asoc)
-{
-	struct sock *sk = asoc->base.sk;
-	struct socket *sock = sk->socket;
-
-	if ((sctp_wspace(asoc) > 0) && sock) {
-		if (waitqueue_active(&asoc->wait))
-			wake_up_interruptible(&asoc->wait);
-
-		if (sctp_writeable(sk)) {
-			if (sk->sleep && waitqueue_active(sk->sleep))
-				wake_up_interruptible(sk->sleep);
-
-			/* Note that we try to include the Async I/O support
-			 * here by modeling from the current TCP/UDP code.
-			 * We have not tested with it yet.
-			 */
-			if (sock->fasync_list &&
-			    !(sk->shutdown & SEND_SHUTDOWN))
-				sock_wake_async(sock, 2, POLL_OUT);
-		}
-	}
 }
 
 /* If socket sndbuf has changed, wake up all per association waiters.  */
diff -Nru a/net/socket.c b/net/socket.c
--- a/net/socket.c	Mon Oct  7 11:25:31 2002
+++ b/net/socket.c	Mon Oct  7 11:25:31 2002
@@ -79,6 +79,10 @@
 #include <linux/kmod.h>
 #endif
 
+#if defined(CONFIG_NET_RADIO) || defined(CONFIG_NET_PCMCIA_RADIO)
+#include <linux/wireless.h>		/* Note : will define WIRELESS_EXT */
+#endif	/* CONFIG_NET_RADIO || CONFIG_NET_PCMCIA_RADIO */
+
 #include <asm/uaccess.h>
 
 #include <net/sock.h>
@@ -675,19 +679,42 @@
 }
 
 /*
- *	With an ioctl arg may well be a user mode pointer, but we don't know what to do
- *	with it - that's up to the protocol still.
+ *	With an ioctl, arg may well be a user mode pointer, but we don't know
+ *	what to do with it - that's up to the protocol still.
  */
 
 int sock_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
 	   unsigned long arg)
 {
 	struct socket *sock;
-	int err;
+	int pid, err;
 
 	unlock_kernel();
 	sock = SOCKET_I(inode);
-	err = sock->ops->ioctl(sock, cmd, arg);
+	if (cmd >= SIOCDEVPRIVATE && cmd <= (SIOCDEVPRIVATE + 15)) {
+		err = dev_ioctl(cmd, (void *)arg);
+	} else
+#ifdef WIRELESS_EXT
+	if (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST) {
+		err = dev_ioctl(cmd, (void *)arg);
+	} else
+#endif	/* WIRELESS_EXT */
+	switch (cmd) {
+		case FIOSETOWN:
+		case SIOCSPGRP:
+			err = -EFAULT;
+			if (get_user(pid, (int *)arg))
+				break;
+			err = f_setown(sock->file, pid, 1);
+			break;
+		case FIOGETOWN:
+		case SIOCGPGRP:
+			err = put_user(sock->file->f_owner.pid, (int *)arg);
+			break;
+		default:
+			err = sock->ops->ioctl(sock, cmd, arg);
+			break;
+	}
 	lock_kernel();
 
 	return err;
diff -Nru a/net/wanrouter/af_wanpipe.c b/net/wanrouter/af_wanpipe.c
--- a/net/wanrouter/af_wanpipe.c	Mon Oct  7 11:25:39 2002
+++ b/net/wanrouter/af_wanpipe.c	Mon Oct  7 11:25:39 2002
@@ -1867,19 +1867,9 @@
 {
 	struct sock *sk = sock->sk;
 	int err;
-	int pid;
 
 	switch(cmd) 
 	{
-		case FIOSETOWN:
-		case SIOCSPGRP:
-			err = get_user(pid, (int *) arg);
-			if (err)
-				return err; 
-			return f_setown(sock->file, pid, 1);
-		case FIOGETOWN:
-		case SIOCGPGRP:
-			return put_user(sock->file->f_owner.pid, (int *)arg);
 		case SIOCGSTAMP:
 			if(sk->stamp.tv_sec==0)
 				return -ENOENT;
@@ -1979,14 +1969,6 @@
 #endif
 
 		default:
-			if ((cmd >= SIOCDEVPRIVATE) &&
-			    (cmd <= (SIOCDEVPRIVATE + 15)))
-				return(dev_ioctl(cmd,(void *) arg));
-
-#ifdef CONFIG_NET_RADIO
-			if((cmd >= SIOCIWFIRST) && (cmd <= SIOCIWLAST))
-				return(dev_ioctl(cmd,(void *) arg));
-#endif
 			return -EOPNOTSUPP;
 	}
 	/*NOTREACHED*/
diff -Nru a/net/wanrouter/wanproc.c b/net/wanrouter/wanproc.c
--- a/net/wanrouter/wanproc.c	Mon Oct  7 11:25:35 2002
+++ b/net/wanrouter/wanproc.c	Mon Oct  7 11:25:35 2002
@@ -61,9 +61,6 @@
 
 #ifdef CONFIG_PROC_FS
 
-/* Proc filesystem interface */
-static int router_proc_perms(struct inode *, int);
-
 /* Miscellaneous */
 
 /*
@@ -79,11 +76,6 @@
  *	Generic /proc/net/router/<file> file and inode operations 
  */
 
-static struct inode_operations router_inode =
-{
-	.permission =	router_proc_perms,
-};
-
 /*
  *	/proc/net/router 
  */
@@ -99,15 +91,6 @@
 /****** Proc filesystem entry points ****************************************/
 
 /*
- *	Verify access rights.
- */
-
-static int router_proc_perms (struct inode* inode, int op)
-{
-	return 0;
-}
-
-/*
  *	Iterator
  */
 static void *r_start(struct seq_file *m, loff_t *pos)
@@ -320,16 +303,14 @@
 	if (!proc_router)
 		goto fail;
 
-	p = create_proc_entry("config",0,proc_router);
+	p = create_proc_entry("config", S_IRUGO, proc_router);
 	if (!p)
 		goto fail_config;
 	p->proc_fops = &config_fops;
-	p->proc_iops = &router_inode;
-	p = create_proc_entry("status",0,proc_router);
+	p = create_proc_entry("status", S_IRUGO, proc_router);
 	if (!p)
 		goto fail_stat;
 	p->proc_fops = &status_fops;
-	p->proc_iops = &router_inode;
 	return 0;
 fail_stat:
 	remove_proc_entry("config", proc_router);
@@ -359,11 +340,10 @@
 	if (wandev->magic != ROUTER_MAGIC)
 		return -EINVAL;
 		
-	wandev->dent = create_proc_entry(wandev->name, 0, proc_router);
+	wandev->dent = create_proc_entry(wandev->name, S_IRUGO, proc_router);
 	if (!wandev->dent)
 		return -ENOMEM;
 	wandev->dent->proc_fops	= &wandev_fops;
-	wandev->dent->proc_iops	= &router_inode;
 	wandev->dent->data	= wandev;
 	return 0;
 }
diff -Nru a/net/x25/Makefile b/net/x25/Makefile
--- a/net/x25/Makefile	Mon Oct  7 11:25:26 2002
+++ b/net/x25/Makefile	Mon Oct  7 11:25:26 2002
@@ -6,7 +6,7 @@
 
 x25-y			:= af_x25.o x25_dev.o x25_facilities.o x25_in.o \
 			   x25_link.o x25_out.o x25_route.o x25_subr.o \
-			   x25_timer.o
+			   x25_timer.o x25_proc.o
 x25-$(CONFIG_SYSCTL)	+= sysctl_net_x25.o
 x25-objs		:= $(x25-y)
 
diff -Nru a/net/x25/af_x25.c b/net/x25/af_x25.c
--- a/net/x25/af_x25.c	Mon Oct  7 11:25:39 2002
+++ b/net/x25/af_x25.c	Mon Oct  7 11:25:39 2002
@@ -27,6 +27,8 @@
  *	2000-10-02	Henner Eisen	Made x25_kick() single threaded per socket.
  *	2000-10-27	Henner Eisen    MSG_DONTWAIT for fragment allocation.
  *	2000-11-14	Henner Eisen    Closing datalink from NETDEV_GOING_DOWN
+ *	2002-10-06	Arnaldo C. Melo Get rid of cli/sti, move proc stuff to
+ *					x25_proc.c, using seq_file
  */
 
 #include <linux/config.h>
@@ -55,7 +57,6 @@
 #include <linux/mm.h>
 #include <linux/interrupt.h>
 #include <linux/notifier.h>
-#include <linux/proc_fs.h>
 #include <linux/init.h>
 #include <net/x25.h>
 
@@ -65,8 +66,8 @@
 int sysctl_x25_clear_request_timeout   = X25_DEFAULT_T23;
 int sysctl_x25_ack_holdback_timeout    = X25_DEFAULT_T2;
 
-static struct sock *x25_list;
-static rwlock_t x25_list_lock = RW_LOCK_UNLOCKED;
+struct sock *x25_list;
+rwlock_t x25_list_lock = RW_LOCK_UNLOCKED;
 
 static struct proto_ops x25_proto_ops;
 
@@ -246,8 +247,10 @@
 	read_lock_bh(&x25_list_lock);
 
 	for (s = x25_list; s; s = s->next)
-		if ((!strcmp(addr->x25_addr, x25_sk(s)->source_addr.x25_addr) ||
-		     !strcmp(addr->x25_addr, null_x25_address.x25_addr)) &&
+		if ((!strcmp(addr->x25_addr,
+			     x25_sk(s)->source_addr.x25_addr) ||
+		     !strcmp(addr->x25_addr,
+			     null_x25_address.x25_addr)) &&
 		     s->state == TCP_LISTEN)
 			break;
 
@@ -318,12 +321,13 @@
 }
 
 /*
- *	This is called from user mode and the timers. Thus it protects itself against
- *	interrupt users but doesn't worry about being called during work.
- *	Once it is removed from the queue no interrupt or bottom half will
- *	touch it and we are (fairly 8-) ) safe.
+ *	This is called from user mode and the timers. Thus it protects itself
+ *	against interrupt users but doesn't worry about being called during
+ *	work. Once it is removed from the queue no interrupt or bottom half
+ *	will touch it and we are (fairly 8-) ) safe.
+ *	Not static as it's used by the timer
  */
-void x25_destroy_socket(struct sock *sk)	/* Not static as it's used by the timer */
+void x25_destroy_socket(struct sock *sk)
 {
 	struct sk_buff *skb;
 
@@ -337,7 +341,10 @@
 
 	while ((skb = skb_dequeue(&sk->receive_queue)) != NULL) {
 		if (skb->sk != sk) {		/* A pending connection */
-			skb->sk->dead = 1;	/* Queue the unaccepted socket for death */
+			/*
+			 * Queue the unaccepted socket for death
+			 */
+			skb->sk->dead = 1;
 			x25_start_heartbeat(skb->sk);
 			x25_sk(skb->sk)->state = X25_STATE_0;
 		}
@@ -631,7 +638,8 @@
 	return rc;
 }
 
-static int x25_connect(struct socket *sock, struct sockaddr *uaddr, int addr_len, int flags)
+static int x25_connect(struct socket *sock, struct sockaddr *uaddr,
+		       int addr_len, int flags)
 {
 	struct sock *sk = sock->sk;
 	struct x25_opt *x25 = x25_sk(sk);
@@ -642,7 +650,7 @@
 	lock_sock(sk);
 	if (sk->state == TCP_ESTABLISHED && sock->state == SS_CONNECTING) {
 		sock->state = SS_CONNECTED;
-		goto out;	/* Connect completed during a ERESTARTSYS event */
+		goto out; /* Connect completed during a ERESTARTSYS event */
 	}
 
 	rc = -ECONNREFUSED;
@@ -679,7 +687,7 @@
 		goto out_put_neigh;
 
 	rc = -EINVAL;
-	if (sk->zapped)		/* Must bind first - autobinding does not work */
+	if (sk->zapped)	/* Must bind first - autobinding does not work */
 		goto out_put_neigh;
 
 	if (!strcmp(x25->source_addr.x25_addr, null_x25_address.x25_addr))
@@ -785,7 +793,8 @@
 	return rc;
 }
 
-static int x25_getname(struct socket *sock, struct sockaddr *uaddr, int *uaddr_len, int peer)
+static int x25_getname(struct socket *sock, struct sockaddr *uaddr,
+		       int *uaddr_len, int peer)
 {
 	struct sockaddr_x25 *sx25 = (struct sockaddr_x25 *)uaddr;
 	struct sock *sk = sock->sk;
@@ -804,7 +813,8 @@
 	return 0;
 }
  
-int x25_rx_call_request(struct sk_buff *skb, struct x25_neigh *nb, unsigned int lci)
+int x25_rx_call_request(struct sk_buff *skb, struct x25_neigh *nb,
+			unsigned int lci)
 {
 	struct sock *sk;
 	struct sock *make;
@@ -906,7 +916,8 @@
 	goto out;
 }
 
-static int x25_sendmsg(struct socket *sock, struct msghdr *msg, int len, struct scm_cookie *scm)
+static int x25_sendmsg(struct socket *sock, struct msghdr *msg, int len,
+		       struct scm_cookie *scm)
 {
 	struct sock *sk = sock->sk;
 	struct x25_opt *x25 = x25_sk(sk);
@@ -1118,7 +1129,8 @@
 		msg->msg_flags |= MSG_OOB;
 	} else {
 		/* Now we can treat all alike */
-		skb = skb_recv_datagram(sk, flags & ~MSG_DONTWAIT, flags & MSG_DONTWAIT, &rc);
+		skb = skb_recv_datagram(sk, flags & ~MSG_DONTWAIT,
+					flags & MSG_DONTWAIT, &rc);
 		if (!skb)
 			goto out;
 
@@ -1236,14 +1248,16 @@
 			break;
 		case SIOCX25GFACILITIES: {
 			struct x25_facilities fac = x25->facilities;
-			rc = copy_to_user((void *)arg, &fac, sizeof(fac)) ? -EFAULT : 0;
+			rc = copy_to_user((void *)arg, &fac,
+					  sizeof(fac)) ? -EFAULT : 0;
 			break;
 		}
 
 		case SIOCX25SFACILITIES: {
 			struct x25_facilities facilities;
 			rc = -EFAULT;
-			if (copy_from_user(&facilities, (void *)arg, sizeof(facilities)))
+			if (copy_from_user(&facilities, (void *)arg,
+					   sizeof(facilities)))
 				break;
 			rc = -EINVAL;
 			if (sk->state != TCP_LISTEN && sk->state != TCP_CLOSE)
@@ -1269,7 +1283,8 @@
 
 		case SIOCX25GCALLUSERDATA: {
 			struct x25_calluserdata cud = x25->calluserdata;
-			rc = copy_to_user((void *)arg, &cud, sizeof(cud)) ? -EFAULT : 0;
+			rc = copy_to_user((void *)arg, &cud,
+					  sizeof(cud)) ? -EFAULT : 0;
 			break;
 		}
 
@@ -1277,7 +1292,8 @@
 			struct x25_calluserdata calluserdata;
 
 			rc = -EFAULT;
-			if (copy_from_user(&calluserdata, (void *)arg, sizeof(calluserdata)))
+			if (copy_from_user(&calluserdata, (void *)arg,
+					   sizeof(calluserdata)))
 				break;
 			rc = -EINVAL;
 			if (calluserdata.cudlength > X25_MAX_CUD_LEN)
@@ -1290,7 +1306,8 @@
 		case SIOCX25GCAUSEDIAG: {
 			struct x25_causediag causediag;
 			causediag = x25->causediag;
-			rc = copy_to_user((void *)arg, &causediag, sizeof(causediag)) ? -EFAULT : 0;
+			rc = copy_to_user((void *)arg, &causediag,
+					  sizeof(causediag)) ? -EFAULT : 0;
 			break;
 		}
 
@@ -1302,70 +1319,6 @@
 	return rc;
 }
 
-static int x25_get_info(char *buffer, char **start, off_t offset, int length)
-{
-	struct sock *s;
-	struct net_device *dev;
-	const char *devname;
-	off_t pos = 0;
-	off_t begin = 0;
-	int len = sprintf(buffer, "dest_addr  src_addr   dev   lci st vs vr "
-				  "va   t  t2 t21 t22 t23 Snd-Q Rcv-Q inode\n");
-
-	read_lock_bh(&x25_list_lock);
-
-	for (s = x25_list; s; s = s->next) {
-		struct x25_opt *x25 = x25_sk(s);
-
-		if (!x25->neighbour || (dev = x25->neighbour->dev) == NULL)
-			devname = "???";
-		else
-			devname = x25->neighbour->dev->name;
-
-		len += sprintf(buffer + len, "%-10s %-10s %-5s %3.3X  %d  %d  "
-					     "%d  %d %3lu %3lu %3lu %3lu %3lu "
-					     "%5d %5d %ld\n",
-			!x25->dest_addr.x25_addr[0] ? "*" :
-						x25->dest_addr.x25_addr,
-			!x25->source_addr.x25_addr[0] ? "*" :
-						x25->source_addr.x25_addr,
-			devname, 
-			x25->lci & 0x0FFF,
-			x25->state,
-			x25->vs,
-			x25->vr,
-			x25->va,
-			x25_display_timer(s) / HZ,
-			x25->t2  / HZ,
-			x25->t21 / HZ,
-			x25->t22 / HZ,
-			x25->t23 / HZ,
-			atomic_read(&s->wmem_alloc),
-			atomic_read(&s->rmem_alloc),
-			s->socket ? SOCK_INODE(s->socket)->i_ino : 0L);
-
-		pos = begin + len;
-
-		if (pos < offset) {
-			len   = 0;
-			begin = pos;
-		}
-
-		if (pos > offset + length)
-			break;
-	}
-
-	read_unlock_bh(&x25_list_lock);
-
-	*start = buffer + (offset - begin);
-	len   -= (offset - begin);
-
-	if (len > length)
-		len = length;
-
-	return len;
-} 
-
 struct net_proto_family x25_family_ops = {
 	.family =	AF_X25,
 	.create =	x25_create,
@@ -1395,7 +1348,6 @@
 #include <linux/smp_lock.h>
 SOCKOPS_WRAP(x25_proto, AF_X25);
 
-
 static struct packet_type x25_packet_type = {
 	.type =	__constant_htons(ETH_P_X25),
 	.func =	x25_lapb_receive_frame,
@@ -1435,9 +1387,7 @@
 	x25_register_sysctl();
 #endif
 
-	proc_net_create("x25", 0, x25_get_info);
-	proc_net_create("x25_routes", 0, x25_routes_get_info);
-
+	x25_proc_init();
 #ifdef MODULE
 	/*
 	 *	Register any pre existing devices.
@@ -1457,18 +1407,9 @@
 }
 module_init(x25_init);
 
-
-
-MODULE_AUTHOR("Jonathan Naylor <g4klx@g4klx.demon.co.uk>");
-MODULE_DESCRIPTION("The X.25 Packet Layer network layer protocol");
-MODULE_LICENSE("GPL");
-
 static void __exit x25_exit(void)
 {
-
-	proc_net_remove("x25");
-	proc_net_remove("x25_routes");
-
+	x25_proc_exit();
 	x25_link_free();
 	x25_route_free();
 
@@ -1484,3 +1425,6 @@
 }
 module_exit(x25_exit);
 
+MODULE_AUTHOR("Jonathan Naylor <g4klx@g4klx.demon.co.uk>");
+MODULE_DESCRIPTION("The X.25 Packet Layer network layer protocol");
+MODULE_LICENSE("GPL");
diff -Nru a/net/x25/x25_proc.c b/net/x25/x25_proc.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/net/x25/x25_proc.c	Mon Oct  7 11:25:40 2002
@@ -0,0 +1,252 @@
+/*
+ *	X.25 Packet Layer release 002
+ *
+ *	This is ALPHA test software. This code may break your machine,
+ *	randomly fail to work with new releases, misbehave and/or generally
+ *	screw up. It might even work. 
+ *
+ *	This code REQUIRES 2.4 with seq_file support
+ *
+ *	This module:
+ *		This module 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.
+ *
+ *	History
+ *	2002/10/06	Arnaldo Carvalho de Melo  seq_file support
+ */
+
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <net/sock.h>
+#include <net/x25.h>
+
+#ifdef CONFIG_PROC_FS
+static __inline__ struct x25_route *x25_get_route_idx(loff_t pos)
+{
+	struct list_head *route_entry;
+	struct x25_route *rt = NULL;
+
+	list_for_each(route_entry, &x25_route_list) {
+		rt = list_entry(route_entry, struct x25_route, node);
+		if (--pos)
+			break;
+	}
+
+	return rt;
+}
+
+static void *x25_seq_route_start(struct seq_file *seq, loff_t *pos)
+{
+	loff_t l = *pos;
+
+	read_lock_bh(&x25_route_list_lock);
+	return l ? x25_get_route_idx(--l) : (void *)1;
+}
+
+static void *x25_seq_route_next(struct seq_file *seq, void *v, loff_t *pos)
+{
+	struct x25_route *rt;
+
+	++*pos;
+	if (v == (void *)1) {
+		rt = NULL;
+		if (!list_empty(&x25_route_list))
+			rt = list_entry(x25_route_list.next,
+					struct x25_route, node);
+		goto out;
+	}
+	rt = v;
+	if (rt->node.next != &x25_route_list)
+		rt = list_entry(rt->node.next, struct x25_route, node);
+	else 
+		rt = NULL;
+out:
+	return rt;
+}
+
+static void x25_seq_route_stop(struct seq_file *seq, void *v)
+{
+	read_unlock_bh(&x25_route_list_lock);
+}
+
+static int x25_seq_route_show(struct seq_file *seq, void *v)
+{
+	struct x25_route *rt;
+
+	if (v == (void *)1) {
+		seq_puts(seq, "Address          Digits  Device\n");
+		goto out;
+	}
+
+	rt = v;
+	seq_printf(seq, "%-15s  %-6d  %-5s\n",
+		   rt->address.x25_addr, rt->sigdigits,
+		   rt->dev ? rt->dev->name : "???");
+out:
+	return 0;
+} 
+
+static __inline__ struct sock *x25_get_socket_idx(loff_t pos)
+{
+	struct sock *s;
+
+	for (s = x25_list; pos && s; s = s->next)
+		--pos;
+
+	return s;
+}
+
+static void *x25_seq_socket_start(struct seq_file *seq, loff_t *pos)
+{
+	loff_t l = *pos;
+
+	read_lock_bh(&x25_list_lock);
+	return l ? x25_get_socket_idx(--l) : (void *)1;
+}
+
+static void *x25_seq_socket_next(struct seq_file *seq, void *v, loff_t *pos)
+{
+	struct sock *s;
+
+	++*pos;
+	if (v == (void *)1) {
+		s = NULL;
+		if (x25_list)
+			s = x25_list;
+		goto out;
+	}
+	s = v;
+	s = s->next;
+out:
+	return s;
+}
+
+static void x25_seq_socket_stop(struct seq_file *seq, void *v)
+{
+	read_unlock_bh(&x25_list_lock);
+}
+
+static int x25_seq_socket_show(struct seq_file *seq, void *v)
+{
+	struct sock *s;
+	struct x25_opt *x25;
+	struct net_device *dev;
+	const char *devname;
+
+	if (v == (void *)1) {
+		seq_printf(seq, "dest_addr  src_addr   dev   lci st vs vr "
+				"va   t  t2 t21 t22 t23 Snd-Q Rcv-Q inode\n");
+		goto out;
+	}
+
+	s = v;
+	x25 = x25_sk(s);
+
+	if (!x25->neighbour || (dev = x25->neighbour->dev) == NULL)
+		devname = "???";
+	else
+		devname = x25->neighbour->dev->name;
+
+	seq_printf(seq, "%-10s %-10s %-5s %3.3X  %d  %d  %d  %d %3lu %3lu "
+			"%3lu %3lu %3lu %5d %5d %ld\n",
+		   !x25->dest_addr.x25_addr[0] ? "*" : x25->dest_addr.x25_addr,
+		   !x25->source_addr.x25_addr[0] ? "*" : x25->source_addr.x25_addr,
+		   devname, x25->lci & 0x0FFF, x25->state, x25->vs, x25->vr,
+		   x25->va, x25_display_timer(s) / HZ, x25->t2  / HZ,
+		   x25->t21 / HZ, x25->t22 / HZ, x25->t23 / HZ,
+		   atomic_read(&s->wmem_alloc), atomic_read(&s->rmem_alloc),
+		   s->socket ? SOCK_INODE(s->socket)->i_ino : 0L);
+out:
+	return 0;
+} 
+
+struct seq_operations x25_seq_route_ops = {
+	.start  = x25_seq_route_start,
+	.next   = x25_seq_route_next,
+	.stop   = x25_seq_route_stop,
+	.show   = x25_seq_route_show,
+};
+
+struct seq_operations x25_seq_socket_ops = {
+	.start  = x25_seq_socket_start,
+	.next   = x25_seq_socket_next,
+	.stop   = x25_seq_socket_stop,
+	.show   = x25_seq_socket_show,
+};
+
+static int x25_seq_socket_open(struct inode *inode, struct file *file)
+{
+	return seq_open(file, &x25_seq_socket_ops);
+}
+
+static int x25_seq_route_open(struct inode *inode, struct file *file)
+{
+	return seq_open(file, &x25_seq_route_ops);
+}
+
+static struct file_operations x25_seq_socket_fops = {
+	.open		= x25_seq_socket_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= seq_release,
+};
+
+static struct file_operations x25_seq_route_fops = {
+	.open		= x25_seq_route_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= seq_release,
+};
+
+static struct proc_dir_entry *x25_proc_dir;
+
+int __init x25_proc_init(void)
+{
+	struct proc_dir_entry *p;
+	int rc = -ENOMEM;
+
+	x25_proc_dir = proc_mkdir("x25", proc_net);
+	if (!x25_proc_dir)
+		goto out;
+
+	p = create_proc_entry("route", S_IRUGO, x25_proc_dir);
+	if (!p)
+		goto out_route;
+	p->proc_fops = &x25_seq_route_fops;
+
+	p = create_proc_entry("socket", S_IRUGO, x25_proc_dir);
+	if (!p)
+		goto out_socket;
+	p->proc_fops = &x25_seq_socket_fops;
+	rc = 0;
+out:
+	return rc;
+out_socket:
+	remove_proc_entry("route", x25_proc_dir);
+out_route:
+	remove_proc_entry("x25", proc_net);
+	goto out;
+}
+
+void __exit x25_proc_exit(void)
+{
+	remove_proc_entry("route", x25_proc_dir);
+	remove_proc_entry("socket", x25_proc_dir);
+	remove_proc_entry("x25", proc_net);
+}
+
+#else /* CONFIG_PROC_FS */
+
+int __init x25_proc_init(void)
+{
+	return 0;
+}
+
+void __exit x25_proc_exit(void)
+{
+}
+#endif /* CONFIG_PROC_FS */
diff -Nru a/net/x25/x25_route.c b/net/x25/x25_route.c
--- a/net/x25/x25_route.c	Mon Oct  7 11:25:36 2002
+++ b/net/x25/x25_route.c	Mon Oct  7 11:25:36 2002
@@ -18,34 +18,12 @@
  */
 
 #include <linux/config.h>
-#include <linux/errno.h>
-#include <linux/types.h>
-#include <linux/socket.h>
-#include <linux/in.h>
-#include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/timer.h>
-#include <linux/string.h>
-#include <linux/sockios.h>
-#include <linux/net.h>
-#include <linux/inet.h>
-#include <linux/netdevice.h>
-#include <net/arp.h>
 #include <linux/if_arp.h>
-#include <linux/skbuff.h>
-#include <net/sock.h>
-#include <asm/system.h>
-#include <asm/uaccess.h>
-#include <linux/fcntl.h>
-#include <linux/termios.h>	/* For TIOCINQ/OUTQ */
-#include <linux/mm.h>
-#include <linux/interrupt.h>
-#include <linux/notifier.h>
 #include <linux/init.h>
 #include <net/x25.h>
 
-static struct list_head x25_route_list = LIST_HEAD_INIT(x25_route_list);
-static rwlock_t x25_route_list_lock = RW_LOCK_UNLOCKED;
+struct list_head x25_route_list = LIST_HEAD_INIT(x25_route_list);
+rwlock_t x25_route_list_lock = RW_LOCK_UNLOCKED;
 
 /*
  *	Add a new route.
@@ -225,45 +203,6 @@
 out:
 	return rc;
 }
-
-int x25_routes_get_info(char *buffer, char **start, off_t offset, int length)
-{
-	struct x25_route *rt;
-	struct list_head *entry;
-	off_t pos   = 0;
-	off_t begin = 0;
-	int len = sprintf(buffer, "address          digits  device\n");
-
-	read_lock_bh(&x25_route_list_lock);
-
-	list_for_each(entry, &x25_route_list) {
-		rt = list_entry(entry, struct x25_route, node);
-		len += sprintf(buffer + len, "%-15s  %-6d  %-5s\n",
-			       rt->address.x25_addr,
-			       rt->sigdigits,
-			       rt->dev ? rt->dev->name : "???");
-
-		pos = begin + len;
-
-		if (pos < offset) {
-			len   = 0;
-			begin = pos;
-		}
-
-		if (pos > offset + length)
-			break;
-	}
-
-	read_unlock_bh(&x25_route_list_lock);
-
-	*start = buffer + (offset - begin);
-	len   -= (offset - begin);
-
-	if (len > length)
-		len = length;
-
-	return len;
-} 
 
 /*
  *	Release all memory associated with X.25 routing structures.
diff -Nru a/scripts/Makefile b/scripts/Makefile
--- a/scripts/Makefile	Mon Oct  7 11:25:35 2002
+++ b/scripts/Makefile	Mon Oct  7 11:25:35 2002
@@ -9,18 +9,18 @@
 # conmakehash:	 Create arrays for initializing the kernel console tables
 # tkparse: 	 Used by xconfig
 
-all: fixdep split-include docproc conmakehash __chmod
+EXTRA_TARGETS := fixdep split-include docproc conmakehash
+
+# Yikes. We need to build this stuff here even if the user only wants
+# modules.
+
+KBUILD_BUILTIN := 1
 
 # The following temporary rule will make sure that people's
 # trees get updated to the right permissions, since patch(1)
 # can't do it
 # ---------------------------------------------------------------------------
 
-.PHONY: __chmod
-
-__chmod: kernel-doc mkcompile_h makelst
-	@chmod a+x $^
-
 host-progs := fixdep split-include conmakehash docproc tkparse
 tkparse-objs := tkparse.o tkcond.o tkgen.o
 
@@ -30,7 +30,7 @@
 # but it is not worth the effort to generate the dependencies.
 # The alternative solution to always generate it is fairly fast.
 # FORCE it to remake
-$(obj)/kconfig.tk: $(srctree)/arch/$(ARCH)/config.in tkparse FORCE
+$(obj)/kconfig.tk: $(srctree)/arch/$(ARCH)/config.in $(obj)/tkparse FORCE
 	@echo '  Generating $@'
 	@(                                                      \
 	if [ -f /usr/local/bin/wish ];        then              \
@@ -51,15 +51,13 @@
 # Targets hardcoded and wellknow in top-level makefile
 .PHONY: lxdialog
 lxdialog:
-	$(MAKE) -C lxdialog all
+	$(call descend,scripts/lxdialog,)
 
 # fixdep is needed to compile other host programs
 $(obj)/split-include $(obj)/docproc $(addprefix $(obj)/,$(tkparse-objs)) \
 $(obj)/conmakehash lxdialog: $(obj)/fixdep
 
 mrproper:
-	@echo 'Making mrproper (scripts)'
-	@rm -f $(tkparse-objs) $(obj)/kconfig.tk
-	@rm -f core $(host-progs)
-	@$(MAKE) -C lxdialog mrproper
+	@rm -f $(host-progs) $(addprefix $(obj)/,$(tkparse-objs) kconfig.tk core)
+	@$(call descend,scripts/lxdialog,mrproper)
 
diff -Nru a/scripts/fixdep.c b/scripts/fixdep.c
--- a/scripts/fixdep.c	Mon Oct  7 11:25:35 2002
+++ b/scripts/fixdep.c	Mon Oct  7 11:25:35 2002
@@ -207,7 +207,7 @@
 		else
 			*p = tolower((unsigned char)*p);
 	}
-	printf("    $(wildcard %s/include/config/%s.h) \\\n", topdir, s);
+	printf("    $(wildcard include/config/%s.h) \\\n", s);
 }
 
 void parse_config_file(char *map, size_t len)
diff -Nru a/scripts/lxdialog/Makefile b/scripts/lxdialog/Makefile
--- a/scripts/lxdialog/Makefile	Mon Oct  7 11:25:37 2002
+++ b/scripts/lxdialog/Makefile	Mon Oct  7 11:25:37 2002
@@ -20,10 +20,14 @@
 lxdialog-objs := checklist.o menubox.o textbox.o yesno.o inputbox.o \
 		 util.o lxdialog.o msgbox.o
 
-all: ncurses lxdialog
+EXTRA_TARGETS := lxdialog
+
+first_rule: ncurses
 
 include $(TOPDIR)/Rules.make
 
+.PHONY: ncurses
+
 ncurses:
 	@echo "main() {}" > lxtemp.c
 	@if $(HOSTCC) lxtemp.c  $(HOST_LOADLIBES); then \
@@ -40,4 +44,4 @@
 	fi
 
 mrproper:
-	@rm -f core $(host-progs) $(lxdialog-objs) ncurses
+	@rm -f $(host-progs) $(addprefix $(obj)/,$(lxdialog-objs))
diff -Nru a/scripts/mkcompile_h b/scripts/mkcompile_h
--- a/scripts/mkcompile_h	Mon Oct  7 11:25:40 2002
+++ b/scripts/mkcompile_h	Mon Oct  7 11:25:40 2002
@@ -9,7 +9,7 @@
 # So "sudo make install" won't change the "compiled by <user>"
 # do "compiled by root"
 
-if [ -r $TARGET -a ! -O ../include/linux/autoconf.h ]; then
+if [ -r $TARGET -a ! -O include/linux/autoconf.h ]; then
   echo ' (not modified)'
   exit 0
 fi
@@ -17,11 +17,11 @@
 # Do not expand names
 set -f
 
-if [ -r ../.version ]; then
-  VERSION=`cat ../.version`
+if [ -r .version ]; then
+  VERSION=`cat .version`
 else
   VERSION=0
-  echo 0 > ../.version
+  echo 0 > .version
 fi
 
 
@@ -76,4 +76,4 @@
    echo ' (updated)'
    mv -f .tmpcompile $TARGET
 fi
-rm -f .tmpver.1 .tmpver.2
+#rm -f .tmpver.1 .tmpver.2
diff -Nru a/sound/Config.in b/sound/Config.in
--- a/sound/Config.in	Mon Oct  7 11:25:31 2002
+++ b/sound/Config.in	Mon Oct  7 11:25:31 2002
@@ -31,11 +31,15 @@
 if [ "$CONFIG_SND" != "n" -a "$CONFIG_ARM" = "y" ]; then
   source sound/arm/Config.in
 fi
-if [ "$CONFIG_SND" != "n" -a "$CONFIG_SPARC32" = "y" ]; then
-  source sound/sparc/Config.in
+# the following will depenend on the order of config.
+# here assuming USB is defined before ALSA
+if [ "$CONFIG_SND" != "n" -a "$CONFIG_USB" != "n" ]; then
+  source sound/usb/Config.in
 fi
-if [ "$CONFIG_SND" != "n" -a "$CONFIG_SPARC64" = "y" ]; then
-  source sound/sparc/Config.in
+if [ "$CONFIG_SND" != "n" ]; then
+  if [ "$CONFIG_SPARC32" = "y" -o "$CONFIG_SPARC64" = "y" ]; then
+    source sound/sparc/Config.in
+  fi
 fi
 
 endmenu
diff -Nru a/sound/Makefile b/sound/Makefile
--- a/sound/Makefile	Mon Oct  7 11:25:29 2002
+++ b/sound/Makefile	Mon Oct  7 11:25:29 2002
@@ -5,7 +5,7 @@
 
 obj-$(CONFIG_SOUND) += soundcore.o
 obj-$(CONFIG_SOUND_PRIME) += oss/
-obj-$(CONFIG_SND) += core/ i2c/ drivers/ isa/ pci/ ppc/ arm/ synth/ sparc/
+obj-$(CONFIG_SND) += core/ i2c/ drivers/ isa/ pci/ ppc/ arm/ synth/ usb/ sparc/
 
 ifeq ($(CONFIG_SND),y)
   obj-y += last.o
diff -Nru a/sound/arm/sa11xx-uda1341.c b/sound/arm/sa11xx-uda1341.c
--- a/sound/arm/sa11xx-uda1341.c	Mon Oct  7 11:25:25 2002
+++ b/sound/arm/sa11xx-uda1341.c	Mon Oct  7 11:25:25 2002
@@ -15,7 +15,7 @@
  * 2002-04-04   Tomas Kasparek  better rates handling (allow non-standard rates)
  */
 
-/* $Id: sa11xx-uda1341.c,v 1.3 2002/05/25 10:26:06 perex Exp $ */
+/* $Id: sa11xx-uda1341.c,v 1.4 2002/08/06 18:03:25 perex Exp $ */
 
 #include <sound/driver.h>
 #include <linux/module.h>
diff -Nru a/sound/core/Makefile b/sound/core/Makefile
--- a/sound/core/Makefile	Mon Oct  7 11:25:26 2002
+++ b/sound/core/Makefile	Mon Oct  7 11:25:26 2002
@@ -3,17 +3,23 @@
 # Copyright (c) 1999,2001 by Jaroslav Kysela <perex@suse.cz>
 #
 
-export-objs  := sound.o pcm.o pcm_lib.o rawmidi.o timer.o hwdep.o \
-                pcm_sgbuf.o
+export-objs  := sound.o pcm.o pcm_lib.o rawmidi.o timer.o hwdep.o
 
-snd-objs     := sound.o init.o isadma.o memory.o info.o control.o misc.o \
+snd-objs     := sound.o init.o memory.o info.o control.o misc.o \
                 device.o wrappers.o
+ifeq ($(CONFIG_ISA),y)
+snd-objs     += isadma.o
+endif
 ifeq ($(CONFIG_SND_OSSEMUL),y)
 snd-objs     += sound_oss.o info_oss.o
 endif
 
 snd-pcm-objs := pcm.o pcm_native.o pcm_lib.o pcm_timer.o pcm_misc.o \
-		pcm_memory.o pcm_sgbuf.o
+		pcm_memory.o
+ifeq ($(CONFIG_PCI),y)
+snd-pcm-objs += pcm_sgbuf.o
+export-objs  += pcm_sgbuf.o
+endif
 
 snd-rawmidi-objs  := rawmidi.o
 snd-timer-objs    := timer.o
@@ -78,8 +84,7 @@
 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
-obj-$(CONFIG_SND_VIA8233) += snd-pcm.o snd-timer.o snd.o
+obj-$(CONFIG_SND_VIA82XX) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o
 obj-$(CONFIG_SND_ALI5451) += snd.o snd-rawmidi.o snd-timer.o snd-pcm.o
 obj-$(CONFIG_SND_CS46XX) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o
 obj-$(CONFIG_SND_EMU10K1) += snd-pcm.o snd-timer.o snd.o snd-rawmidi.o snd-hwdep.o
@@ -94,6 +99,10 @@
 ifeq ($(CONFIG_SND_SB16_CSP),y)
   obj-$(CONFIG_SND_SB16) += snd-hwdep.o
   obj-$(CONFIG_SND_SBAWE) += snd-hwdep.o
+endif
+obj-$(CONFIG_SND_USB_AUDIO) += snd-pcm.o snd-timer.o snd.o
+ifeq ($(subst m,y,$(CONFIG_SND_SEQUENCER)),y)
+obj-$(CONFIG_SND_USB_AUDIO) += snd-rawmidi.o
 endif
 
 obj-m := $(sort $(obj-m))
diff -Nru a/sound/core/control.c b/sound/core/control.c
--- a/sound/core/control.c	Mon Oct  7 11:25:26 2002
+++ b/sound/core/control.c	Mon Oct  7 11:25:26 2002
@@ -19,7 +19,6 @@
  *
  */
 
-#define __NO_VERSION__
 #include <sound/driver.h>
 #include <linux/threads.h>
 #include <linux/interrupt.h>
@@ -161,7 +160,7 @@
 				goto _found;
 			}
 		}
-		ev = snd_kcalloc(sizeof(*ev), in_interrupt() ? GFP_ATOMIC : GFP_KERNEL);
+		ev = snd_kcalloc(sizeof(*ev), GFP_ATOMIC);
 		if (ev) {
 			ev->id = *id;
 			ev->mask = mask;
@@ -487,12 +486,15 @@
 			}
 			read_unlock(&card->control_owner_lock);
 			if (result > 0) {
-				result = 0;
+				read_unlock(&card->control_rwlock);
 				snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id);
+				result = 0;
+				goto __unlocked;
 			}
 		}
 	}
 	read_unlock(&card->control_rwlock);
+      __unlocked:
 	if (result >= 0)
 		if (copy_to_user(_control, &control, sizeof(control)))
 			return -EFAULT;
@@ -767,20 +769,20 @@
 static struct file_operations snd_ctl_f_ops =
 {
 #ifndef LINUX_2_2
-	owner:		THIS_MODULE,
+	.owner =	THIS_MODULE,
 #endif
-	read:		snd_ctl_read,
-	open:		snd_ctl_open,
-	release:	snd_ctl_release,
-	poll:		snd_ctl_poll,
-	ioctl:		snd_ctl_ioctl,
-	fasync:		snd_ctl_fasync,
+	.read =		snd_ctl_read,
+	.open =		snd_ctl_open,
+	.release =	snd_ctl_release,
+	.poll =		snd_ctl_poll,
+	.ioctl =	snd_ctl_ioctl,
+	.fasync =	snd_ctl_fasync,
 };
 
 static snd_minor_t snd_ctl_reg =
 {
-	comment:	"ctl",
-	f_ops:		&snd_ctl_f_ops,
+	.comment =	"ctl",
+	.f_ops =	&snd_ctl_f_ops,
 };
 
 int snd_ctl_register(snd_card_t *card)
diff -Nru a/sound/core/device.c b/sound/core/device.c
--- a/sound/core/device.c	Mon Oct  7 11:25:36 2002
+++ b/sound/core/device.c	Mon Oct  7 11:25:36 2002
@@ -19,7 +19,6 @@
  *
  */
 
-#define __NO_VERSION__
 #include <sound/driver.h>
 #include <linux/slab.h>
 #include <linux/time.h>
diff -Nru a/sound/core/hwdep.c b/sound/core/hwdep.c
--- a/sound/core/hwdep.c	Mon Oct  7 11:25:25 2002
+++ b/sound/core/hwdep.c	Mon Oct  7 11:25:25 2002
@@ -238,22 +238,22 @@
 static struct file_operations snd_hwdep_f_ops =
 {
 #ifndef LINUX_2_2
-	owner:		THIS_MODULE,
+	.owner = 	THIS_MODULE,
 #endif
-	llseek:		snd_hwdep_llseek,
-	read:		snd_hwdep_read,
-	write:		snd_hwdep_write,
-	open:		snd_hwdep_open,
-	release:	snd_hwdep_release,
-	poll:		snd_hwdep_poll,
-	ioctl:		snd_hwdep_ioctl,
-	mmap:		snd_hwdep_mmap,
+	.llseek =	snd_hwdep_llseek,
+	.read = 	snd_hwdep_read,
+	.write =	snd_hwdep_write,
+	.open =		snd_hwdep_open,
+	.release =	snd_hwdep_release,
+	.poll =		snd_hwdep_poll,
+	.ioctl =	snd_hwdep_ioctl,
+	.mmap =		snd_hwdep_mmap,
 };
 
 static snd_minor_t snd_hwdep_reg =
 {
-	comment:	"hardware dependent",
-	f_ops:		&snd_hwdep_f_ops,
+	.comment =	"hardware dependent",
+	.f_ops =	&snd_hwdep_f_ops,
 };
 
 int snd_hwdep_new(snd_card_t * card, char *id, int device, snd_hwdep_t ** rhwdep)
@@ -261,9 +261,9 @@
 	snd_hwdep_t *hwdep;
 	int err;
 	static snd_device_ops_t ops = {
-		dev_free:	snd_hwdep_dev_free,
-		dev_register:	snd_hwdep_dev_register,
-		dev_unregister:	snd_hwdep_dev_unregister
+		.dev_free = snd_hwdep_dev_free,
+		.dev_register = snd_hwdep_dev_register,
+		.dev_unregister = snd_hwdep_dev_unregister
 	};
 
 	snd_assert(rhwdep != NULL, return -EINVAL);
@@ -338,10 +338,8 @@
 						    &snd_hwdep_reg, hwdep->oss_dev) < 0) {
 				snd_printk(KERN_ERR "unable to register OSS compatibility device %i:%i\n",
 					   hwdep->card->number, hwdep->device);
-			} else {
-				snd_oss_info_register(SNDRV_OSS_INFO_DEV_SYNTH, hwdep->card->number, hwdep->name);
+			} else
 				hwdep->ossreg = 1;
-			}
 		}
 	}
 #endif
@@ -362,10 +360,8 @@
 		return -EINVAL;
 	}
 #ifdef CONFIG_SND_OSSEMUL
-	if (hwdep->ossreg) {
+	if (hwdep->ossreg)
 		snd_unregister_oss_device(hwdep->oss_type, hwdep->card, hwdep->device);
-		snd_oss_info_unregister(SNDRV_OSS_INFO_DEV_SYNTH, hwdep->card->number);
-	}
 #endif
 	snd_unregister_device(SNDRV_DEVICE_TYPE_HWDEP, hwdep->card, hwdep->device);
 	snd_hwdep_devices[idx] = NULL;
diff -Nru a/sound/core/info.c b/sound/core/info.c
--- a/sound/core/info.c	Mon Oct  7 11:25:33 2002
+++ b/sound/core/info.c	Mon Oct  7 11:25:33 2002
@@ -19,7 +19,6 @@
  *
  */
 
-#define __NO_VERSION__
 #include <sound/driver.h>
 #include <linux/init.h>
 #include <linux/vmalloc.h>
@@ -532,16 +531,16 @@
 static struct file_operations snd_info_entry_operations =
 {
 #ifndef LINUX_2_2
-	owner:		THIS_MODULE,
+	.owner =	THIS_MODULE,
 #endif
-	llseek:		snd_info_entry_llseek,
-	read:		snd_info_entry_read,
-	write:		snd_info_entry_write,
-	poll:		snd_info_entry_poll,
-	ioctl:		snd_info_entry_ioctl,
-	mmap:		snd_info_entry_mmap,
-	open:		snd_info_entry_open,
-	release:	snd_info_entry_release,
+	.llseek =	snd_info_entry_llseek,
+	.read =		snd_info_entry_read,
+	.write =	snd_info_entry_write,
+	.poll =		snd_info_entry_poll,
+	.ioctl =	snd_info_entry_ioctl,
+	.mmap =		snd_info_entry_mmap,
+	.open =		snd_info_entry_open,
+	.release =	snd_info_entry_release,
 };
 
 #ifdef LINUX_2_2
@@ -603,10 +602,10 @@
 struct inode_operations snd_info_card_link_inode_operations =
 {
 #ifdef LINUX_2_2
-	default_file_ops:	&snd_info_card_link_operations,
+	.default_file_ops =	&snd_info_card_link_operations,
 #endif
-	readlink:		snd_info_card_readlink,
-	follow_link:		snd_info_card_followlink,
+	.readlink =		snd_info_card_readlink,
+	.follow_link =		snd_info_card_followlink,
 };
 
 struct proc_dir_entry *snd_create_proc_entry(const char *name, mode_t mode,
@@ -961,7 +960,6 @@
 {
 #ifdef CONFIG_DEVFS_FS
 	char dname[32];
-	devfs_handle_t master;
 #endif
 
 	snd_runtime_check(entry, return);
@@ -971,12 +969,7 @@
 #ifdef CONFIG_DEVFS_FS
 	if (entry->p && strncmp(entry->name, "controlC", 8)) {
 		sprintf(dname, "snd/%s", entry->name);
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)
-		master = devfs_find_handle(NULL, dname, strlen(dname), 0, 0, DEVFS_SPECIAL_CHR, 0);
-		devfs_unregister(master);
-#else
 		devfs_find_and_unregister(NULL, dname, 0, 0, DEVFS_SPECIAL_CHR, 0);
-#endif
 	}
 #endif
 	snd_info_free_entry(entry);
diff -Nru a/sound/core/info_oss.c b/sound/core/info_oss.c
--- a/sound/core/info_oss.c	Mon Oct  7 11:25:26 2002
+++ b/sound/core/info_oss.c	Mon Oct  7 11:25:26 2002
@@ -19,7 +19,6 @@
  *
  */
 
-#define __NO_VERSION__
 #include <sound/driver.h>
 #include <linux/slab.h>
 #include <linux/time.h>
diff -Nru a/sound/core/init.c b/sound/core/init.c
--- a/sound/core/init.c	Mon Oct  7 11:25:26 2002
+++ b/sound/core/init.c	Mon Oct  7 11:25:26 2002
@@ -19,7 +19,6 @@
  *
  */
 
-#define __NO_VERSION__
 #include <sound/driver.h>
 #include <linux/init.h>
 #include <linux/slab.h>
diff -Nru a/sound/core/ioctl32/hwdep32.c b/sound/core/ioctl32/hwdep32.c
--- a/sound/core/ioctl32/hwdep32.c	Mon Oct  7 11:25:39 2002
+++ b/sound/core/ioctl32/hwdep32.c	Mon Oct  7 11:25:39 2002
@@ -18,7 +18,6 @@
  *
  */
 
-#define __NO_VERSION__
 #include <sound/driver.h>
 #include <linux/time.h>
 #include <sound/core.h>
@@ -29,7 +28,5 @@
 struct ioctl32_mapper hwdep_mappers[] = {
 	{ SNDRV_HWDEP_IOCTL_PVERSION, NULL },
 	{ SNDRV_HWDEP_IOCTL_INFO, NULL },
-	{ SNDRV_CTL_IOCTL_HWDEP_NEXT_DEVICE, NULL },
-	{ SNDRV_CTL_IOCTL_HWDEP_INFO, NULL },
 	{ 0 },
 };
diff -Nru a/sound/core/ioctl32/ioctl32.c b/sound/core/ioctl32/ioctl32.c
--- a/sound/core/ioctl32/ioctl32.c	Mon Oct  7 11:25:26 2002
+++ b/sound/core/ioctl32/ioctl32.c	Mon Oct  7 11:25:26 2002
@@ -18,7 +18,6 @@
  *
  */
 
-#define __NO_VERSION__
 #include <sound/driver.h>
 #include <linux/sched.h>
 #include <linux/smp_lock.h>
@@ -48,14 +47,10 @@
 	int err;
 	struct ioctl32_mapper *m;
 
-	lock_kernel();
 	for (m = mappers; m->cmd; m++) {
 		err = register_ioctl32_conversion(m->cmd, m->handler);
-		if (err < 0) {
-			unlock_kernel();
-			return err;
-		}
-		m->registered++;
+		if (err >= 0)
+			m->registered++;
 	}
 	return 0;
 }
@@ -64,14 +59,12 @@
 {
 	struct ioctl32_mapper *m;
 
-	lock_kernel();
 	for (m = mappers; m->cmd; m++) {
 		if (m->registered) {
 			unregister_ioctl32_conversion(m->cmd);
 			m->registered = 0;
 		}
 	}
-	unlock_kernel();
 }
 
 
@@ -101,36 +94,32 @@
 {
 	struct sndrv_ctl_elem_list32 data32;
 	struct sndrv_ctl_elem_list data;
-	mm_segment_t oldseg = get_fs();
+	mm_segment_t oldseg;
 	int err;
 
-	set_fs(KERNEL_DS);
-	if (copy_from_user(&data32, (void*)arg, sizeof(data32))) {
-		err = -EFAULT;
-		goto __err;
-	}
+	if (copy_from_user(&data32, (void*)arg, sizeof(data32)))
+		return -EFAULT;
 	memset(&data, 0, sizeof(data));
 	data.offset = data32.offset;
 	data.space = data32.space;
 	data.used = data32.used;
 	data.count = data32.count;
 	data.pids = A(data32.pids);
+	oldseg = get_fs();
+	set_fs(KERNEL_DS);
 	err = file->f_op->ioctl(file->f_dentry->d_inode, file, native_ctl, (unsigned long)&data);
+	set_fs(oldseg);
 	if (err < 0)
-		goto __err;
+		return err;
 	/* copy the result */
 	data32.offset = data.offset;
 	data32.space = data.space;
 	data32.used = data.used;
 	data32.count = data.count;
 	//data.pids = data.pids;
-	if (copy_to_user((void*)arg, &data32, sizeof(data32))) {
-		err = -EFAULT;
-		goto __err;
-	}
- __err:
-	set_fs(oldseg);
-	return err;
+	if (copy_to_user((void*)arg, &data32, sizeof(data32)))
+		return -EFAULT;
+	return 0;
 }
 
 DEFINE_ALSA_IOCTL_ENTRY(ctl_elem_list, ctl_elem_list, SNDRV_CTL_IOCTL_ELEM_LIST);
@@ -172,22 +161,22 @@
 	struct sndrv_ctl_elem_info data;
 	struct sndrv_ctl_elem_info32 data32;
 	int err;
-	mm_segment_t oldseg = get_fs();
+	mm_segment_t oldseg;
 
-	set_fs(KERNEL_DS);
-	if (copy_from_user(&data32, (void*)arg, sizeof(data32))) {
-		err = -EFAULT;
-		goto __err;
-	}
+	if (copy_from_user(&data32, (void*)arg, sizeof(data32)))
+		return -EFAULT;
 	memset(&data, 0, sizeof(data));
 	data.id = data32.id;
 	/* we need to copy the item index.
 	 * hope this doesn't break anything..
 	 */
 	data.value.enumerated.item = data32.value.enumerated.item;
+	oldseg = get_fs();
+	set_fs(KERNEL_DS);
 	err = file->f_op->ioctl(file->f_dentry->d_inode, file, native_ctl, (unsigned long)&data);
+	set_fs(oldseg);
 	if (err < 0)
-		goto __err;
+		return err;
 	/* restore info to 32bit */
 	data32.id = data.id;
 	data32.type = data.type;
@@ -216,10 +205,8 @@
 		break;
 	}
 	if (copy_to_user((void*)arg, &data32, sizeof(data32)))
-		err = -EFAULT;
- __err:
-	set_fs(oldseg);
-	return err;
+		return -EFAULT;
+	return 0;
 }
 
 DEFINE_ALSA_IOCTL_ENTRY(ctl_elem_info, ctl_elem_info, SNDRV_CTL_IOCTL_ELEM_INFO);
@@ -282,26 +269,20 @@
 	struct sndrv_ctl_elem_value32 data32;
 	int err, i;
 	int type;
-	mm_segment_t oldseg = get_fs();
-
-	set_fs(KERNEL_DS);
+	mm_segment_t oldseg;
 
 	/* FIXME: check the sane ioctl.. */
 
-	if (copy_from_user(&data32, (void*)arg, sizeof(data32))) {
-		err = -EFAULT;
-		goto __err;
-	}
+	if (copy_from_user(&data32, (void*)arg, sizeof(data32)))
+		return -EFAULT;
 	memset(&data, 0, sizeof(data));
 	data.id = data32.id;
 	data.indirect = data32.indirect;
 	if (data.indirect) /* FIXME: this is not correct for long arrays */
 		data.value.integer.value_ptr = (void*)TO_PTR(data32.value.integer.value_ptr);
 	type = get_ctl_type(file, &data.id);
-	if (type < 0) {
-		err = type;
-		goto __err;
-	}
+	if (type < 0)
+		return type;
 	if (! data.indirect) {
 		switch (type) {
 		case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
@@ -330,9 +311,12 @@
 		}
 	}
 
+	oldseg = get_fs();
+	set_fs(KERNEL_DS);
 	err = file->f_op->ioctl(file->f_dentry->d_inode, file, native_ctl, (unsigned long)&data);
+	set_fs(oldseg);
 	if (err < 0)
-		goto __err;
+		return err;
 	/* restore info to 32bit */
 	if (! data.indirect) {
 		switch (type) {
@@ -361,10 +345,8 @@
 		}
 	}
 	if (copy_to_user((void*)arg, &data32, sizeof(data32)))
-		err = -EFAULT;
- __err:
-	set_fs(oldseg);
-	return err;
+		return -EFAULT;
+	return 0;
 }
 
 DEFINE_ALSA_IOCTL_ENTRY(ctl_elem_read, ctl_elem_value, SNDRV_CTL_IOCTL_ELEM_READ);
@@ -393,6 +375,7 @@
 	{ SNDRV_CTL_IOCTL_ELEM_LOCK, NULL },
 	{ SNDRV_CTL_IOCTL_ELEM_UNLOCK, NULL },
 	{ SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS, NULL },
+	{ SNDRV_CTL_IOCTL_HWDEP_INFO, NULL },
 	{ SNDRV_CTL_IOCTL_HWDEP_NEXT_DEVICE, NULL },
 	{ SNDRV_CTL_IOCTL_PCM_NEXT_DEVICE, NULL },
 	{ SNDRV_CTL_IOCTL_PCM_INFO, NULL },
@@ -428,39 +411,14 @@
 
 static int __init snd_ioctl32_init(void)
 {
-	int err;
-	
-	err = snd_ioctl32_register(control_mappers);
-	if (err < 0)
-		return err;
-	err = snd_ioctl32_register(pcm_mappers);
-	if (err < 0) {
-		snd_ioctl32_done();
-		return err;
-	}
-	err = snd_ioctl32_register(rawmidi_mappers);
-	if (err < 0) {
-		snd_ioctl32_done();
-		return err;
-	}
-	err = snd_ioctl32_register(timer_mappers);
-	if (err < 0) {
-		snd_ioctl32_done();
-		return err;
-	}
-	err = snd_ioctl32_register(hwdep_mappers);
-	if (err < 0) {
-		snd_ioctl32_done();
-		return err;
-	}
+	snd_ioctl32_register(control_mappers);
+	snd_ioctl32_register(pcm_mappers);
+	snd_ioctl32_register(rawmidi_mappers);
+	snd_ioctl32_register(timer_mappers);
+	snd_ioctl32_register(hwdep_mappers);
 #ifdef CONFIG_SND_SEQUENCER
-	err = snd_ioctl32_register(seq_mappers);
-	if (err < 0) {
-		snd_ioctl32_done();
-		return err;
-	}
+	snd_ioctl32_register(seq_mappers);
 #endif
-
 	return 0;
 }
 
diff -Nru a/sound/core/ioctl32/ioctl32.h b/sound/core/ioctl32/ioctl32.h
--- a/sound/core/ioctl32/ioctl32.h	Mon Oct  7 11:25:33 2002
+++ b/sound/core/ioctl32/ioctl32.h	Mon Oct  7 11:25:33 2002
@@ -60,27 +60,23 @@
 {\
 	struct sndrv_##type##32 data32;\
 	struct sndrv_##type data;\
-	mm_segment_t oldseg = get_fs();\
+	mm_segment_t oldseg;\
 	int err;\
-	set_fs(KERNEL_DS);\
-	if (copy_from_user(&data32, (void*)arg, sizeof(data32))) {\
-		err = -EFAULT;\
-		goto __err;\
-	}\
+	if (copy_from_user(&data32, (void*)arg, sizeof(data32)))\
+		return -EFAULT;\
 	memset(&data, 0, sizeof(data));\
 	convert_from_32(type, &data, &data32);\
+	oldseg = get_fs();\
+	set_fs(KERNEL_DS);\
 	err = file->f_op->ioctl(file->f_dentry->d_inode, file, native_ctl, (unsigned long)&data);\
 	if (err < 0) \
-		goto __err;\
+		return err;\
 	if (native_ctl & (_IOC_READ << _IOC_DIRSHIFT)) {\
 		convert_to_32(type, &data32, &data);\
-		if (copy_to_user((void*)arg, &data32, sizeof(data32))) {\
-			err = -EFAULT;\
-			goto __err;\
-		}\
+		if (copy_to_user((void*)arg, &data32, sizeof(data32)))\
+			return -EFAULT;\
 	}\
- __err: set_fs(oldseg);\
-	return err;\
+	return 0;\
 }
 
 #define DEFINE_ALSA_IOCTL_ENTRY(name,type,native_ctl) \
diff -Nru a/sound/core/ioctl32/pcm32.c b/sound/core/ioctl32/pcm32.c
--- a/sound/core/ioctl32/pcm32.c	Mon Oct  7 11:25:36 2002
+++ b/sound/core/ioctl32/pcm32.c	Mon Oct  7 11:25:36 2002
@@ -18,7 +18,6 @@
  *
  */
 
-#define __NO_VERSION__
 #include <sound/driver.h>
 #include <linux/time.h>
 #include <sound/core.h>
@@ -190,30 +189,26 @@
 {
 	struct sndrv_xferi32 data32;
 	struct sndrv_xferi data;
-	mm_segment_t oldseg = get_fs();
+	mm_segment_t oldseg;
 	int err;
 
-	set_fs(KERNEL_DS);
-	if (copy_from_user(&data32, (void*)arg, sizeof(data32))) {
-		err = -EFAULT;
-		goto __err;
-	}
+	if (copy_from_user(&data32, (void*)arg, sizeof(data32)))
+		return -EFAULT;
 	memset(&data, 0, sizeof(data));
 	data.result = data32.result;
 	data.buf = A(data32.buf);
 	data.frames = data32.frames;
+	oldseg = get_fs();
+	set_fs(KERNEL_DS);
 	err = file->f_op->ioctl(file->f_dentry->d_inode, file, native_ctl, (unsigned long)&data);
+	set_fs(oldseg);
 	if (err < 0)
-		goto __err;
+		return err;
 	/* copy the result */
 	data32.result = data.result;
-	if (copy_to_user((void*)arg, &data32, sizeof(data32))) {
-		err = -EFAULT;
-		goto __err;
-	}
- __err:
-	set_fs(oldseg);
-	return err;
+	if (copy_to_user((void*)arg, &data32, sizeof(data32)))
+		return -EFAULT;
+	return 0;
 }
 
 
@@ -238,9 +233,7 @@
 	void *bufs[128];
 	int err = 0, ch, i;
 	u32 *bufptr;
-	mm_segment_t oldseg = get_fs();
-
-	set_fs(KERNEL_DS);
+	mm_segment_t oldseg;
 
 	/* FIXME: need to check whether fop->ioctl is sane */
 
@@ -251,41 +244,31 @@
 	/* check validty of the command */
 	switch (native_ctl) {
 	case SNDRV_PCM_IOCTL_WRITEN_FRAMES:
-		if (substream->stream  != SNDRV_PCM_STREAM_PLAYBACK) {
-			err = -EINVAL;
-			goto __err;
-		}
-		if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
-			err = -EBADFD;
-			goto __err;
-		}
+		if (substream->stream  != SNDRV_PCM_STREAM_PLAYBACK)
+			return -EINVAL;
+		if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN)
+			return -EBADFD;
 		break;
 	case SNDRV_PCM_IOCTL_READN_FRAMES:
-		if (substream->stream  != SNDRV_PCM_STREAM_CAPTURE) {
-			err = -EINVAL;
-			goto __err;
-		}
+		if (substream->stream  != SNDRV_PCM_STREAM_CAPTURE)
+			return -EINVAL;
 		break;
 	}
-	if ((ch = substream->runtime->channels) > 128) {
-		err = -EINVAL;
-		goto __err;
-	}
-	if (get_user(data32.frames, &srcptr->frames)) {
-		err = -EFAULT;
-		goto __err;
-	}
+	if ((ch = substream->runtime->channels) > 128)
+		return -EINVAL;
+	if (get_user(data32.frames, &srcptr->frames))
+		return -EFAULT;
 	__get_user(data32.bufs, &srcptr->bufs);
 	bufptr = (u32*)TO_PTR(data32.bufs);
 	for (i = 0; i < ch; i++) {
 		u32 ptr;
-		if (get_user(ptr, bufptr)) {
-			err = -EFAULT;
-			goto __err;
-		}
+		if (get_user(ptr, bufptr))
+			return -EFAULT;
 		bufs[ch] = (void*)TO_PTR(ptr);
 		bufptr++;
 	}
+	oldseg = get_fs();
+	set_fs(KERNEL_DS);
 	switch (native_ctl) {
 	case SNDRV_PCM_IOCTL_WRITEN_FRAMES:
 		err = snd_pcm_lib_writev(substream, bufs, data32.frames);
@@ -294,14 +277,12 @@
 		err = snd_pcm_lib_readv(substream, bufs, data32.frames);
 		break;
 	}
-	
+	set_fs(oldseg);
 	if (err < 0)
-		goto __err;
+		return err;
 	if (put_user(err, &srcptr->result))
-		err = -EFAULT;
- __err:
-	set_fs(oldseg);
-	return err < 0 ? err : 0;
+		return -EFAULT;
+	return 0;
 }
 
 
@@ -364,24 +345,22 @@
 {
 	struct sndrv_pcm_hw_params_old32 data32;
 	struct sndrv_pcm_hw_params data;
-	mm_segment_t oldseg = get_fs();
+	mm_segment_t oldseg;
 	int err;
-	set_fs(KERNEL_DS);
-	if (copy_from_user(&data32, (void*)arg, sizeof(data32))) {
-		err = -EFAULT;
-		goto __err;
-	}
+
+	if (copy_from_user(&data32, (void*)arg, sizeof(data32)))
+		return -EFAULT;
 	snd_pcm_hw_convert_from_old_params(&data, &data32);
+	oldseg = get_fs();
+	set_fs(KERNEL_DS);
 	err = file->f_op->ioctl(file->f_dentry->d_inode, file, native_ctl, (unsigned long)&data);
+	set_fs(oldseg);
 	if (err < 0)
-		goto __err;
+		return err;
 	snd_pcm_hw_convert_to_old_params(&data32, &data);
-	if (copy_to_user((void*)arg, &data32, sizeof(data32))) {
-	  err = -EFAULT;
-	  goto __err;
-	}
- __err: set_fs(oldseg);
-	return err;
+	if (copy_to_user((void*)arg, &data32, sizeof(data32)))
+		return  -EFAULT;
+	return 0;
 }
 
 
@@ -451,10 +430,6 @@
 	{ SNDRV_PCM_IOCTL_READN_FRAMES32, AP(pcm_readn) },
 	{ SNDRV_PCM_IOCTL_LINK, NULL },
 	{ SNDRV_PCM_IOCTL_UNLINK, NULL },
-
-	{ SNDRV_CTL_IOCTL_PCM_NEXT_DEVICE, NULL },
-	{ SNDRV_CTL_IOCTL_PCM_INFO, NULL },
-	{ SNDRV_CTL_IOCTL_PCM_PREFER_SUBDEVICE, NULL },
 
 	{ 0 },
 };
diff -Nru a/sound/core/ioctl32/rawmidi32.c b/sound/core/ioctl32/rawmidi32.c
--- a/sound/core/ioctl32/rawmidi32.c	Mon Oct  7 11:25:35 2002
+++ b/sound/core/ioctl32/rawmidi32.c	Mon Oct  7 11:25:35 2002
@@ -18,7 +18,6 @@
  *
  */
 
-#define __NO_VERSION__
 #include <sound/driver.h>
 #include <linux/time.h>
 #include <sound/core.h>
diff -Nru a/sound/core/ioctl32/seq32.c b/sound/core/ioctl32/seq32.c
--- a/sound/core/ioctl32/seq32.c	Mon Oct  7 11:25:39 2002
+++ b/sound/core/ioctl32/seq32.c	Mon Oct  7 11:25:39 2002
@@ -18,7 +18,6 @@
  *
  */
 
-#define __NO_VERSION__
 #include <sound/driver.h>
 #include <linux/time.h>
 #include <sound/core.h>
diff -Nru a/sound/core/ioctl32/timer32.c b/sound/core/ioctl32/timer32.c
--- a/sound/core/ioctl32/timer32.c	Mon Oct  7 11:25:35 2002
+++ b/sound/core/ioctl32/timer32.c	Mon Oct  7 11:25:35 2002
@@ -18,7 +18,6 @@
  *
  */
 
-#define __NO_VERSION__
 #include <sound/driver.h>
 #include <linux/time.h>
 #include <sound/core.h>
diff -Nru a/sound/core/isadma.c b/sound/core/isadma.c
--- a/sound/core/isadma.c	Mon Oct  7 11:25:35 2002
+++ b/sound/core/isadma.c	Mon Oct  7 11:25:35 2002
@@ -26,13 +26,10 @@
 
 #undef HAVE_REALLY_SLOW_DMA_CONTROLLER
 
-#define __NO_VERSION__
 #include <sound/driver.h>
 #include <sound/core.h>
 #include <asm/dma.h>
 
-#ifdef CONFIG_ISA
-
 /*
  *
  */
@@ -79,5 +76,3 @@
 	release_dma_lock(flags);
 	return result;
 }
-
-#endif /* CONFIG_ISA */
diff -Nru a/sound/core/memory.c b/sound/core/memory.c
--- a/sound/core/memory.c	Mon Oct  7 11:25:25 2002
+++ b/sound/core/memory.c	Mon Oct  7 11:25:25 2002
@@ -19,7 +19,6 @@
  *
  */
 
-#define __NO_VERSION__
 #include <sound/driver.h>
 #include <asm/io.h>
 #include <asm/uaccess.h>
@@ -539,42 +538,3 @@
 	return 0;
 #endif
 }
-
-#ifdef HACK_PCI_ALLOC_CONSISTENT
-/*
- * A dirty hack... when the kernel code is fixed this should be removed.
- *
- * since pci_alloc_consistent always tries GFP_DMA when the requested
- * pci memory region is below 32bit, it happens quite often that even
- * 2 order or pages cannot be allocated.
- *
- * so in the following, GFP_DMA is used only when the first allocation
- * doesn't match the requested region.
- */
-#ifdef __i386__
-#define get_phys_addr(x) virt_to_phys(x)
-#else /* ppc and x86-64 */
-#define get_phys_addr(x) virt_to_bus(x)
-#endif
-void *snd_pci_hack_alloc_consistent(struct pci_dev *hwdev, size_t size,
-				    dma_addr_t *dma_handle)
-{
-	void *ret;
-	int gfp = GFP_ATOMIC;
-
-	if (hwdev == NULL)
-		gfp |= GFP_DMA;
-	ret = (void *)__get_free_pages(gfp, get_order(size));
-	if (ret) {
-		if (hwdev && ((get_phys_addr(ret) + size - 1) & ~hwdev->dma_mask)) {
-			free_pages((unsigned long)ret, get_order(size));
-			ret = (void *)__get_free_pages(gfp | GFP_DMA, get_order(size));
-		}
-	}
-	if (ret) {
-		memset(ret, 0, size);
-		*dma_handle = get_phys_addr(ret);
-	}
-	return ret;
-}
-#endif /* hack */
diff -Nru a/sound/core/misc.c b/sound/core/misc.c
--- a/sound/core/misc.c	Mon Oct  7 11:25:28 2002
+++ b/sound/core/misc.c	Mon Oct  7 11:25:28 2002
@@ -19,7 +19,6 @@
  *
  */
 
-#define __NO_VERSION__
 #include <sound/driver.h>
 #include <linux/init.h>
 #include <linux/sched.h>
diff -Nru a/sound/core/oss/copy.c b/sound/core/oss/copy.c
--- a/sound/core/oss/copy.c	Mon Oct  7 11:25:29 2002
+++ b/sound/core/oss/copy.c	Mon Oct  7 11:25:29 2002
@@ -19,7 +19,6 @@
  *
  */
 
-#define __NO_VERSION__
 #include <sound/driver.h>
 #include <linux/time.h>
 #include <sound/core.h>
diff -Nru a/sound/core/oss/io.c b/sound/core/oss/io.c
--- a/sound/core/oss/io.c	Mon Oct  7 11:25:27 2002
+++ b/sound/core/oss/io.c	Mon Oct  7 11:25:27 2002
@@ -19,7 +19,6 @@
  *
  */
   
-#define __NO_VERSION__
 #include <sound/driver.h>
 #include <linux/time.h>
 #include <sound/core.h>
diff -Nru a/sound/core/oss/linear.c b/sound/core/oss/linear.c
--- a/sound/core/oss/linear.c	Mon Oct  7 11:25:35 2002
+++ b/sound/core/oss/linear.c	Mon Oct  7 11:25:35 2002
@@ -20,7 +20,6 @@
  *
  */
 
-#define __NO_VERSION__
 #include <sound/driver.h>
 #include <linux/time.h>
 #include <sound/core.h>
diff -Nru a/sound/core/oss/mixer_oss.c b/sound/core/oss/mixer_oss.c
--- a/sound/core/oss/mixer_oss.c	Mon Oct  7 11:25:36 2002
+++ b/sound/core/oss/mixer_oss.c	Mon Oct  7 11:25:36 2002
@@ -253,8 +253,8 @@
 	if (mixer == NULL || slot > 30)
 		return -EIO;
 	pslot = &mixer->slots[slot];
-	left = fmixer->volume[slot][0];
-	right = fmixer->volume[slot][1];
+	left = pslot->volume[0];
+	right = pslot->volume[1];
 	if (pslot->get_volume)
 		result = pslot->get_volume(fmixer, pslot, &left, &right);
 	if (!pslot->stereo)
@@ -262,8 +262,8 @@
 	snd_assert(left >= 0 && left <= 100, return -EIO);
 	snd_assert(right >= 0 && right <= 100, return -EIO);
 	if (result >= 0) {
-		fmixer->volume[slot][0] = left;
-		fmixer->volume[slot][1] = right;
+		pslot->volume[0] = left;
+		pslot->volume[1] = right;
 	 	result = (left & 0xff) | ((right & 0xff) << 8);
 	}
 	return result;
@@ -284,13 +284,13 @@
 	if (right > 100)
 		right = 100;
 	if (!pslot->stereo)
-		left = right = left;
+		right = left;
 	if (pslot->put_volume)
 		result = pslot->put_volume(fmixer, pslot, left, right);
 	if (result < 0)
 		return result;
-	fmixer->volume[slot][0] = left;
-	fmixer->volume[slot][1] = right;
+	pslot->volume[0] = left;
+	pslot->volume[1] = right;
  	return (left & 0xff) | ((right & 0xff) << 8);
 }
 
@@ -383,17 +383,17 @@
 static struct file_operations snd_mixer_oss_f_ops =
 {
 #ifndef LINUX_2_2
-	owner:		THIS_MODULE,
+	.owner =	THIS_MODULE,
 #endif
-	open:		snd_mixer_oss_open,
-	release:	snd_mixer_oss_release,
-	ioctl:		snd_mixer_oss_ioctl,
+	.open =		snd_mixer_oss_open,
+	.release =	snd_mixer_oss_release,
+	.ioctl =	snd_mixer_oss_ioctl,
 };
 
 static snd_minor_t snd_mixer_oss_reg =
 {
-	comment:	"mixer",
-	f_ops:		&snd_mixer_oss_f_ops,
+	.comment =	"mixer",
+	.f_ops =	&snd_mixer_oss_f_ops,
 };
 
 /*
@@ -409,6 +409,7 @@
 	return ((nrange * (val - omin)) + (orange / 2)) / orange + nmin;
 }
 
+/* convert from alsa native to oss values (0-100) */
 static long snd_mixer_oss_conv1(long val, long min, long max, int *old)
 {
 	if (val == snd_mixer_oss_conv(*old, 0, 100, min, max))
@@ -416,6 +417,7 @@
 	return snd_mixer_oss_conv(val, min, max, 0, 100);
 }
 
+/* convert from oss to alsa native values */
 static long snd_mixer_oss_conv2(long val, long min, long max)
 {
 	return snd_mixer_oss_conv(val, 0, 100, min, max);
@@ -472,6 +474,8 @@
 	int channels;
 	snd_kcontrol_t *kcontrol[SNDRV_MIXER_OSS_ITEM_COUNT];
 	unsigned int capture_item;
+	struct snd_mixer_oss_assign_table *assigned;
+	unsigned int allocated: 1;
 };
 
 static snd_kcontrol_t *snd_mixer_oss_test_id(snd_mixer_oss_t *mixer, const char *name, int index)
@@ -500,9 +504,9 @@
 	snd_runtime_check(!kctl->info(kctl, &uinfo), return);
 	snd_runtime_check(!kctl->get(kctl, &uctl), return);
 	snd_runtime_check(uinfo.type != SNDRV_CTL_ELEM_TYPE_BOOLEAN || uinfo.value.integer.min != 0 || uinfo.value.integer.max != 1, return);
-	*left = snd_mixer_oss_conv1(uctl.value.integer.value[0], uinfo.value.integer.min, uinfo.value.integer.max, &fmixer->volume[pslot->number][0]);
+	*left = snd_mixer_oss_conv1(uctl.value.integer.value[0], uinfo.value.integer.min, uinfo.value.integer.max, &pslot->volume[0]);
 	if (uinfo.count > 1)
-		*right = snd_mixer_oss_conv1(uctl.value.integer.value[1], uinfo.value.integer.min, uinfo.value.integer.max, &fmixer->volume[pslot->number][1]);
+		*right = snd_mixer_oss_conv1(uctl.value.integer.value[1], uinfo.value.integer.min, uinfo.value.integer.max, &pslot->volume[1]);
 }
 
 static void snd_mixer_oss_get_volume1_sw(snd_mixer_oss_file_t *fmixer,
@@ -809,10 +813,26 @@
 
 static void snd_mixer_oss_slot_free(snd_mixer_oss_slot_t *chn)
 {
-	kfree(chn->private_data);
+	struct slot *p = (struct slot *)chn->private_data;
+	if (p) {
+		if (p->allocated && p->assigned) {
+			kfree(p->assigned->name);
+			kfree(p->assigned);
+		}
+		kfree(p);
+	}
+}
+
+static void mixer_slot_clear(snd_mixer_oss_slot_t *rslot)
+{
+	int idx = rslot->number; /* remember this */
+	if (rslot->private_free)
+		rslot->private_free(rslot);
+	memset(rslot, 0, sizeof(*rslot));
+	rslot->number = idx;
 }
 
-static void snd_mixer_oss_build_input(snd_mixer_oss_t *mixer, struct snd_mixer_oss_assign_table *ptr)
+static int snd_mixer_oss_build_input(snd_mixer_oss_t *mixer, struct snd_mixer_oss_assign_table *ptr, int ptr_allocated)
 {
 	struct slot slot;
 	struct slot *pslot;
@@ -823,49 +843,49 @@
 	memset(&slot, 0, sizeof(slot));
 	if (snd_mixer_oss_build_test(mixer, &slot, ptr->name, ptr->index,
 				     SNDRV_MIXER_OSS_ITEM_GLOBAL))
-		return;
+		return 0;
 	sprintf(str, "%s Switch", ptr->name);
 	if (snd_mixer_oss_build_test(mixer, &slot, str, ptr->index,
 				     SNDRV_MIXER_OSS_ITEM_GSWITCH))
-		return;
+		return 0;
 	sprintf(str, "%s Route", ptr->name);
 	if (snd_mixer_oss_build_test(mixer, &slot, str, ptr->index,
 				     SNDRV_MIXER_OSS_ITEM_GROUTE))
-		return;
+		return 0;
 	sprintf(str, "%s Volume", ptr->name);
 	if (snd_mixer_oss_build_test(mixer, &slot, str, ptr->index,
 				     SNDRV_MIXER_OSS_ITEM_GVOLUME))
-		return;
+		return 0;
 	sprintf(str, "%s Playback Switch", ptr->name);
 	if (snd_mixer_oss_build_test(mixer, &slot, str, ptr->index,
 				     SNDRV_MIXER_OSS_ITEM_PSWITCH))
-		return;
+		return 0;
 	sprintf(str, "%s Playback Route", ptr->name);
 	if (snd_mixer_oss_build_test(mixer, &slot, str, ptr->index,
 				     SNDRV_MIXER_OSS_ITEM_PROUTE))
-		return;
+		return 0;
 	sprintf(str, "%s Playback Volume", ptr->name);
 	if (snd_mixer_oss_build_test(mixer, &slot, str, ptr->index,
 				     SNDRV_MIXER_OSS_ITEM_PVOLUME))
-		return;
+		return 0;
 	sprintf(str, "%s Capture Switch", ptr->name);
 	if (snd_mixer_oss_build_test(mixer, &slot, str, ptr->index,
 				     SNDRV_MIXER_OSS_ITEM_CSWITCH))
-		return;
+		return 0;
 	sprintf(str, "%s Capture Route", ptr->name);
 	if (snd_mixer_oss_build_test(mixer, &slot, str, ptr->index,
 				     SNDRV_MIXER_OSS_ITEM_CROUTE))
-		return;
+		return 0;
 	sprintf(str, "%s Capture Volume", ptr->name);
 	if (snd_mixer_oss_build_test(mixer, &slot, str, ptr->index,
 				     SNDRV_MIXER_OSS_ITEM_CVOLUME))
-		return;
+		return 0;
 	if (ptr->index == 0 && (kctl = snd_mixer_oss_test_id(mixer, "Capture Source", 0)) != NULL) {
 		snd_ctl_elem_info_t uinfo;
 
 		memset(&uinfo, 0, sizeof(uinfo));
 		if (kctl->info(kctl, &uinfo))
-			return;
+			return 0;
 		strcpy(str, ptr->name);
 		if (!strcmp(str, "Master"))
 			strcpy(str, "Mix");
@@ -878,7 +898,7 @@
 			for (slot.capture_item = 1; slot.capture_item < uinfo.value.enumerated.items; slot.capture_item++) {
 				uinfo.value.enumerated.item = slot.capture_item;
 				if (kctl->info(kctl, &uinfo))
-					return;
+					return 0;
 				if (!strcmp(uinfo.value.enumerated.name, str)) {
 					slot.present |= SNDRV_MIXER_OSS_PRESENT_CAPTURE;
 					break;
@@ -888,10 +908,13 @@
 	}
 	if (slot.present != 0) {
 		pslot = (struct slot *)kmalloc(sizeof(slot), GFP_KERNEL);
-		snd_runtime_check(pslot != NULL, return);
+		snd_runtime_check(pslot != NULL, return -ENOMEM);
 		*pslot = slot;
 		pslot->signature = SNDRV_MIXER_OSS_SIGNATURE;
+		pslot->assigned = ptr;
+		pslot->allocated = ptr_allocated;
 		rslot = &mixer->slots[ptr->oss_id];
+		mixer_slot_clear(rslot);
 		rslot->stereo = slot.channels > 1 ? 1 : 0;
 		rslot->get_volume = snd_mixer_oss_get_volume1;
 		rslot->put_volume = snd_mixer_oss_put_volume1;
@@ -907,6 +930,156 @@
 		}
 		rslot->private_data = pslot;
 		rslot->private_free = snd_mixer_oss_slot_free;
+		return 1;
+	}
+	return 0;
+}
+
+/*
+ */
+#define MIXER_VOL(name) [SOUND_MIXER_##name] = #name
+static char *oss_mixer_names[SNDRV_OSS_MAX_MIXERS] = {
+	MIXER_VOL(VOLUME),
+	MIXER_VOL(BASS),
+	MIXER_VOL(TREBLE),
+	MIXER_VOL(SYNTH),
+	MIXER_VOL(PCM),
+	MIXER_VOL(SPEAKER),
+	MIXER_VOL(LINE),
+	MIXER_VOL(MIC),
+	MIXER_VOL(CD),
+	MIXER_VOL(IMIX),
+	MIXER_VOL(ALTPCM),
+	MIXER_VOL(RECLEV),
+	MIXER_VOL(IGAIN),
+	MIXER_VOL(OGAIN),
+	MIXER_VOL(LINE1),
+	MIXER_VOL(LINE2),
+	MIXER_VOL(LINE3),
+	MIXER_VOL(DIGITAL1),
+	MIXER_VOL(DIGITAL2),
+	MIXER_VOL(DIGITAL3),
+	MIXER_VOL(PHONEIN),
+	MIXER_VOL(PHONEOUT),
+	MIXER_VOL(VIDEO),
+	MIXER_VOL(RADIO),
+	MIXER_VOL(MONITOR),
+};
+	
+/*
+ *  /proc interface
+ */
+
+static void snd_mixer_oss_proc_read(snd_info_entry_t *entry,
+				    snd_info_buffer_t * buffer)
+{
+	snd_mixer_oss_t *mixer = snd_magic_cast(snd_mixer_oss_t, entry->private_data, return);
+	int i;
+
+	down(&mixer->reg_mutex);
+	for (i = 0; i < SNDRV_OSS_MAX_MIXERS; i++) {
+		struct slot *p;
+
+		if (! oss_mixer_names[i])
+			continue;
+		p = (struct slot *)mixer->slots[i].private_data;
+		snd_iprintf(buffer, "%s ", oss_mixer_names[i]);
+		if (p && p->assigned)
+			snd_iprintf(buffer, "\"%s\" %d\n",
+				    p->assigned->name,
+				    p->assigned->index);
+		else
+			snd_iprintf(buffer, "\"\" 0\n");
+	}
+	up(&mixer->reg_mutex);
+}
+
+static void snd_mixer_oss_proc_write(snd_info_entry_t *entry,
+				     snd_info_buffer_t * buffer)
+{
+	snd_mixer_oss_t *mixer = snd_magic_cast(snd_mixer_oss_t, entry->private_data, return);
+	char line[128], str[32], idxstr[16], *cptr;
+	int ch, idx;
+	struct snd_mixer_oss_assign_table *tbl;
+	struct slot *slot;
+
+	while (!snd_info_get_line(buffer, line, sizeof(line))) {
+		cptr = snd_info_get_str(str, line, sizeof(str));
+		for (ch = 0; ch < SNDRV_OSS_MAX_MIXERS; ch++)
+			if (oss_mixer_names[ch] && strcmp(oss_mixer_names[ch], str) == 0)
+				break;
+		if (ch >= SNDRV_OSS_MAX_MIXERS) {
+			snd_printk(KERN_ERR "mixer_oss: invalid OSS volume '%s'\n", str);
+			continue;
+		}
+		cptr = snd_info_get_str(str, cptr, sizeof(str));
+		if (! *str) {
+			/* remove the entry */
+			down(&mixer->reg_mutex);
+			mixer_slot_clear(&mixer->slots[ch]);
+			up(&mixer->reg_mutex);
+			continue;
+		}
+		snd_info_get_str(idxstr, cptr, sizeof(idxstr));
+		idx = simple_strtoul(idxstr, NULL, 10);
+		if (idx >= 0x4000) { /* too big */
+			snd_printk(KERN_ERR "mixer_oss: invalid index %d\n", idx);
+			continue;
+		}
+		down(&mixer->reg_mutex);
+		slot = (struct slot *)mixer->slots[ch].private_data;
+		if (slot && slot->assigned &&
+		    slot->assigned->index == idx && ! strcmp(slot->assigned->name, str))
+			/* not changed */
+			goto __unlock;
+		tbl = kmalloc(sizeof(*tbl), GFP_KERNEL);
+		if (! tbl) {
+			snd_printk(KERN_ERR "mixer_oss: no memory\n");
+			goto __unlock;
+		}
+		tbl->oss_id = ch;
+		tbl->name = snd_kmalloc_strdup(str, GFP_KERNEL);
+		if (! tbl->name) {
+			kfree(tbl);
+			goto __unlock;
+		}
+		tbl->index = idx;
+		if (snd_mixer_oss_build_input(mixer, tbl, 1) <= 0) {
+			kfree(tbl->name);
+			kfree(tbl);
+		}
+	__unlock:
+		up(&mixer->reg_mutex);
+	}
+}
+
+static void snd_mixer_oss_proc_init(snd_mixer_oss_t *mixer)
+{
+	snd_info_entry_t *entry;
+
+	entry = snd_info_create_card_entry(mixer->card, "oss_mixer",
+					   mixer->card->proc_root);
+	if (! entry)
+		return;
+	entry->content = SNDRV_INFO_CONTENT_TEXT;
+	entry->mode = S_IFREG | S_IRUGO | S_IWUSR;
+	entry->c.text.read_size = 8192;
+	entry->c.text.read = snd_mixer_oss_proc_read;
+	entry->c.text.write_size = 8192;
+	entry->c.text.write = snd_mixer_oss_proc_write;
+	entry->private_data = mixer;
+	if (snd_info_register(entry) < 0) {
+		snd_info_free_entry(entry);
+		entry = NULL;
+	}
+	mixer->proc_entry = entry;
+}
+
+static void snd_mixer_oss_proc_done(snd_mixer_oss_t *mixer)
+{
+	if (mixer->proc_entry) {
+		snd_info_unregister(mixer->proc_entry);
+		mixer->proc_entry = NULL;
 	}
 }
 
@@ -945,9 +1118,9 @@
 	int idx;
 	
 	for (idx = 0; idx < sizeof(table) / sizeof(struct snd_mixer_oss_assign_table); idx++)
-		snd_mixer_oss_build_input(mixer, &table[idx]);
+		snd_mixer_oss_build_input(mixer, &table[idx], 0);
 	if (mixer->slots[SOUND_MIXER_SYNTH].get_volume == NULL)
-		snd_mixer_oss_build_input(mixer, &fm_table);
+		snd_mixer_oss_build_input(mixer, &fm_table, 0);
 	if (mixer->mask_recsrc) {
 		mixer->get_recsrc = snd_mixer_oss_get_recsrc2;
 		mixer->put_recsrc = snd_mixer_oss_put_recsrc2;
@@ -968,7 +1141,7 @@
 	card = mixer->card;
 	snd_assert(mixer == card->mixer_oss, return -ENXIO);
 	card->mixer_oss = NULL;
-	for (idx = 0; idx < 31; idx++) {
+	for (idx = 0; idx < SNDRV_OSS_MAX_MIXERS; idx++) {
 		snd_mixer_oss_slot_t *chn = &mixer->slots[idx];
 		if (chn->private_free)
 			chn->private_free(chn);
@@ -987,6 +1160,7 @@
 		mixer = snd_magic_kcalloc(snd_mixer_oss_t, sizeof(snd_mixer_oss_t), GFP_KERNEL);
 		if (mixer == NULL)
 			return -ENOMEM;
+		init_MUTEX(&mixer->reg_mutex);
 		sprintf(name, "mixer%i%i", card->number, 0);
 		if ((err = snd_register_oss_device(SNDRV_OSS_DEVICE_TYPE_MIXER,
 						   card, 0,
@@ -997,20 +1171,26 @@
 			return err;
 		}
 		mixer->card = card;
-		strcpy(mixer->name, name);
+		if (*card->mixername) {
+			strncpy(mixer->name, card->mixername, sizeof(mixer->name) - 1);
+			mixer->name[sizeof(mixer->name)-1] = 0;
+		} else
+			strcpy(mixer->name, name);
 		snd_oss_info_register(SNDRV_OSS_INFO_DEV_MIXERS,
 				      card->number,
-				      name);
-		for (idx = 0; idx < 31; idx++)
+				      mixer->name);
+		for (idx = 0; idx < SNDRV_OSS_MAX_MIXERS; idx++)
 			mixer->slots[idx].number = idx;
 		card->mixer_oss = mixer;
 		snd_mixer_oss_build(mixer);
+		snd_mixer_oss_proc_init(mixer);
 	} else {
 		snd_mixer_oss_t *mixer = card->mixer_oss;
 		if (mixer == NULL)
 			return 0;
 		snd_oss_info_unregister(SNDRV_OSS_INFO_DEV_MIXERS, mixer->card->number);
 		snd_unregister_oss_device(SNDRV_OSS_DEVICE_TYPE_MIXER, mixer->card, 0);
+		snd_mixer_oss_proc_done(mixer);
 		return snd_mixer_oss_free1(mixer);
 	}
 	return 0;
diff -Nru a/sound/core/oss/mulaw.c b/sound/core/oss/mulaw.c
--- a/sound/core/oss/mulaw.c	Mon Oct  7 11:25:40 2002
+++ b/sound/core/oss/mulaw.c	Mon Oct  7 11:25:40 2002
@@ -21,7 +21,6 @@
  *
  */
   
-#define __NO_VERSION__
 #include <sound/driver.h>
 #include <linux/time.h>
 #include <sound/core.h>
diff -Nru a/sound/core/oss/pcm_oss.c b/sound/core/oss/pcm_oss.c
--- a/sound/core/oss/pcm_oss.c	Mon Oct  7 11:25:37 2002
+++ b/sound/core/oss/pcm_oss.c	Mon Oct  7 11:25:37 2002
@@ -1851,8 +1851,8 @@
 	mask = 0;
 	if (psubstream != NULL) {
 		snd_pcm_runtime_t *runtime = psubstream->runtime;
-		spin_lock_irq(&runtime->lock);
 		poll_wait(file, &runtime->sleep, wait);
+		spin_lock_irq(&runtime->lock);
 		if (runtime->status->state != SNDRV_PCM_STATE_DRAINING &&
 		    (runtime->status->state != SNDRV_PCM_STATE_RUNNING ||
 		     snd_pcm_oss_playback_ready(psubstream)))
@@ -1861,8 +1861,8 @@
 	}
 	if (csubstream != NULL) {
 		snd_pcm_runtime_t *runtime = csubstream->runtime;
-		spin_lock_irq(&runtime->lock);
 		poll_wait(file, &runtime->sleep, wait);
+		spin_lock_irq(&runtime->lock);
 		if (runtime->status->state != SNDRV_PCM_STATE_RUNNING ||
 		    snd_pcm_oss_capture_ready(csubstream))
 			mask |= POLLIN | POLLRDNORM;
@@ -2083,21 +2083,21 @@
 static struct file_operations snd_pcm_oss_f_reg =
 {
 #ifndef LINUX_2_2
-	owner:		THIS_MODULE,
+	.owner =	THIS_MODULE,
 #endif
-	read:		snd_pcm_oss_read,
-	write:		snd_pcm_oss_write,
-	open:		snd_pcm_oss_open,
-	release:	snd_pcm_oss_release,
-	poll:		snd_pcm_oss_poll,
-	ioctl:		snd_pcm_oss_ioctl,
-	mmap:		snd_pcm_oss_mmap,
+	.read =		snd_pcm_oss_read,
+	.write =	snd_pcm_oss_write,
+	.open =		snd_pcm_oss_open,
+	.release =	snd_pcm_oss_release,
+	.poll =		snd_pcm_oss_poll,
+	.ioctl =	snd_pcm_oss_ioctl,
+	.mmap =		snd_pcm_oss_mmap,
 };
 
 static snd_minor_t snd_pcm_oss_reg =
 {
-	comment:	"digital audio",
-	f_ops:		&snd_pcm_oss_f_reg,
+	.comment =	"digital audio",
+	.f_ops =	&snd_pcm_oss_f_reg,
 };
 
 static void register_oss_dsp(unsigned short native_minor, snd_pcm_t *pcm, int index)
@@ -2159,8 +2159,8 @@
 
 static snd_pcm_notify_t snd_pcm_oss_notify =
 {
-	n_register:	snd_pcm_oss_register_minor,
-	n_unregister:	snd_pcm_oss_unregister_minor,
+	.n_register =	snd_pcm_oss_register_minor,
+	.n_unregister =	snd_pcm_oss_unregister_minor,
 };
 
 static int __init alsa_pcm_oss_init(void)
diff -Nru a/sound/core/oss/pcm_plugin.c b/sound/core/oss/pcm_plugin.c
--- a/sound/core/oss/pcm_plugin.c	Mon Oct  7 11:25:28 2002
+++ b/sound/core/oss/pcm_plugin.c	Mon Oct  7 11:25:28 2002
@@ -24,7 +24,6 @@
 #define PLUGIN_DEBUG
 #endif
 
-#define __NO_VERSION__
 #include <sound/driver.h>
 #include <linux/slab.h>
 #include <linux/time.h>
diff -Nru a/sound/core/oss/rate.c b/sound/core/oss/rate.c
--- a/sound/core/oss/rate.c	Mon Oct  7 11:25:35 2002
+++ b/sound/core/oss/rate.c	Mon Oct  7 11:25:35 2002
@@ -19,7 +19,6 @@
  *
  */
   
-#define __NO_VERSION__
 #include <sound/driver.h>
 #include <linux/time.h>
 #include <sound/core.h>
diff -Nru a/sound/core/oss/route.c b/sound/core/oss/route.c
--- a/sound/core/oss/route.c	Mon Oct  7 11:25:39 2002
+++ b/sound/core/oss/route.c	Mon Oct  7 11:25:39 2002
@@ -19,7 +19,6 @@
  *
  */
 
-#define __NO_VERSION__
 #include <sound/driver.h>
 #include <linux/slab.h>
 #include <linux/time.h>
diff -Nru a/sound/core/pcm.c b/sound/core/pcm.c
--- a/sound/core/pcm.c	Mon Oct  7 11:25:40 2002
+++ b/sound/core/pcm.c	Mon Oct  7 11:25:40 2002
@@ -541,12 +541,10 @@
 	return 0;
 }
 
-static int snd_pcm_new_stream(snd_pcm_t *pcm,
-			      snd_pcm_str_t *pstr,
-			      int substream_count,
-			      int stream)
+int snd_pcm_new_stream(snd_pcm_t *pcm, int stream, int substream_count)
 {
 	int idx, err;
+	snd_pcm_str_t *pstr = &pcm->streams[stream];
 	snd_pcm_substream_t *substream, *prev;
 
 #if defined(CONFIG_SND_PCM_OSS) || defined(CONFIG_SND_PCM_OSS_MODULE)
@@ -598,9 +596,9 @@
 	snd_pcm_t *pcm;
 	int err;
 	static snd_device_ops_t ops = {
-		dev_free:	snd_pcm_dev_free,
-		dev_register:	snd_pcm_dev_register,
-		dev_unregister:	snd_pcm_dev_unregister
+		.dev_free = snd_pcm_dev_free,
+		.dev_register =	snd_pcm_dev_register,
+		.dev_unregister = snd_pcm_dev_unregister
 	};
 
 	snd_assert(rpcm != NULL, return -EINVAL);
@@ -614,11 +612,11 @@
 	if (id) {
 		strncpy(pcm->id, id, sizeof(pcm->id) - 1);
 	}
-	if ((err = snd_pcm_new_stream(pcm, &pcm->streams[SNDRV_PCM_STREAM_PLAYBACK], playback_count, SNDRV_PCM_STREAM_PLAYBACK)) < 0) {
+	if ((err = snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_PLAYBACK, playback_count)) < 0) {
 		snd_pcm_free(pcm);
 		return err;
 	}
-	if ((err = snd_pcm_new_stream(pcm, &pcm->streams[SNDRV_PCM_STREAM_CAPTURE], capture_count, SNDRV_PCM_STREAM_CAPTURE)) < 0) {
+	if ((err = snd_pcm_new_stream(pcm, SNDRV_PCM_STREAM_CAPTURE, capture_count)) < 0) {
 		snd_pcm_free(pcm);
 		return err;
 	}
@@ -974,6 +972,7 @@
 EXPORT_SYMBOL(snd_pcm_lock);
 EXPORT_SYMBOL(snd_pcm_devices);
 EXPORT_SYMBOL(snd_pcm_new);
+EXPORT_SYMBOL(snd_pcm_new_stream);
 EXPORT_SYMBOL(snd_pcm_notify);
 EXPORT_SYMBOL(snd_pcm_open_substream);
 EXPORT_SYMBOL(snd_pcm_release_substream);
diff -Nru a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c
--- a/sound/core/pcm_lib.c	Mon Oct  7 11:25:32 2002
+++ b/sound/core/pcm_lib.c	Mon Oct  7 11:25:32 2002
@@ -20,7 +20,6 @@
  *
  */
 
-#define __NO_VERSION__
 #include <sound/driver.h>
 #include <linux/slab.h>
 #include <linux/time.h>
diff -Nru a/sound/core/pcm_memory.c b/sound/core/pcm_memory.c
--- a/sound/core/pcm_memory.c	Mon Oct  7 11:25:36 2002
+++ b/sound/core/pcm_memory.c	Mon Oct  7 11:25:36 2002
@@ -19,7 +19,6 @@
  *
  */
 
-#define __NO_VERSION__
 #include <sound/driver.h>
 #include <asm/io.h>
 #include <linux/time.h>
diff -Nru a/sound/core/pcm_misc.c b/sound/core/pcm_misc.c
--- a/sound/core/pcm_misc.c	Mon Oct  7 11:25:36 2002
+++ b/sound/core/pcm_misc.c	Mon Oct  7 11:25:36 2002
@@ -19,7 +19,6 @@
  *
  */
   
-#define __NO_VERSION__
 #include <sound/driver.h>
 #include <linux/time.h>
 #include <sound/core.h>
@@ -329,6 +328,18 @@
 		return 0x0000800000008000ULL;
 	case SNDRV_PCM_FORMAT_U32_BE:
 		return 0x0000008000000080ULL;
+	case SNDRV_PCM_FORMAT_U24_3LE:
+		return 0x0000800000800000ULL;
+	case SNDRV_PCM_FORMAT_U24_3BE:
+		return 0x0080000080000080ULL;
+	case SNDRV_PCM_FORMAT_U20_3LE:
+		return 0x0000080000080000ULL;
+	case SNDRV_PCM_FORMAT_U20_3BE:
+		return 0x0008000008000008ULL;
+	case SNDRV_PCM_FORMAT_U18_3LE:
+		return 0x0000020000020000ULL;
+	case SNDRV_PCM_FORMAT_U18_3BE:
+		return 0x0002000002000002ULL;
 #else
 	case SNDRV_PCM_FORMAT_U16_LE:
 		return 0x0080008000800080ULL;
@@ -342,16 +353,19 @@
 		return 0x0080000000800000ULL;
 	case SNDRV_PCM_FORMAT_U32_BE:
 		return 0x8000000080000000ULL;
-#endif
 	case SNDRV_PCM_FORMAT_U24_3LE:
+		return 0x0080000080000080ULL;
 	case SNDRV_PCM_FORMAT_U24_3BE:
 		return 0x0000800000800000ULL;
 	case SNDRV_PCM_FORMAT_U20_3LE:
+		return 0x0008000008000008ULL;
 	case SNDRV_PCM_FORMAT_U20_3BE:
 		return 0x0000080000080000ULL;
 	case SNDRV_PCM_FORMAT_U18_3LE:
+		return 0x0002000002000002ULL;
 	case SNDRV_PCM_FORMAT_U18_3BE:
 		return 0x0000020000020000ULL;
+#endif
 	case SNDRV_PCM_FORMAT_FLOAT_LE:
 	{
 		union {
@@ -471,11 +485,16 @@
 		if (! silence)
 			memset(data, 0, samples * 3);
 		else {
-			/* FIXME: rewrite in the more better way.. */
-			int i;
 			while (samples-- > 0) {
-				for (i = 0; i < 3; i++)
-					*((u_int8_t *)data)++ = silence >> (i * 8);
+#ifdef SNDRV_LITTLE_ENDIAN
+				*((u_int8_t *)data)++ = silence >> 0;
+				*((u_int8_t *)data)++ = silence >> 8;
+				*((u_int8_t *)data)++ = silence >> 16;
+#else
+				*((u_int8_t *)data)++ = silence >> 16;
+				*((u_int8_t *)data)++ = silence >> 8;
+				*((u_int8_t *)data)++ = silence >> 0;
+#endif
 			}
 		}
 	}
diff -Nru a/sound/core/pcm_native.c b/sound/core/pcm_native.c
--- a/sound/core/pcm_native.c	Mon Oct  7 11:25:29 2002
+++ b/sound/core/pcm_native.c	Mon Oct  7 11:25:29 2002
@@ -19,7 +19,6 @@
  *
  */
 
-#define __NO_VERSION__
 #include <sound/driver.h>
 #include <linux/mm.h>
 #include <linux/file.h>
@@ -174,11 +173,11 @@
 			continue;
 #ifdef RULES_DEBUG
 		printk("%s = ", snd_pcm_hw_param_names[k]);
-		printk("%x -> ", *m);
+		printk("%04x%04x%04x%04x -> ", m->bits[3], m->bits[2], m->bits[1], m->bits[0]);
 #endif
 		changed = snd_mask_refine(m, constrs_mask(constrs, k));
 #ifdef RULES_DEBUG
-		printk("%x\n", *m);
+		printk("%04x%04x%04x%04x\n", m->bits[3], m->bits[2], m->bits[1], m->bits[0]);
 #endif
 		if (changed)
 			params->cmask |= 1 << k;
@@ -439,8 +438,10 @@
 	}
 	if (atomic_read(&runtime->mmap_count))
 		return -EBADFD;
-	if (substream->ops->hw_free == NULL)
+	if (substream->ops->hw_free == NULL) {
+		runtime->status->state = SNDRV_PCM_STATE_OPEN;
 		return 0;
+	}
 	result = substream->ops->hw_free(substream);
 	runtime->status->state = SNDRV_PCM_STATE_OPEN;
 	return result;
@@ -464,6 +465,7 @@
 		return -EINVAL;
 	if (params->silence_threshold + params->silence_size > runtime->buffer_size)
 		return -EINVAL;
+	spin_lock_irq(&runtime->lock);
 	runtime->tstamp_mode = params->tstamp_mode;
 	runtime->sleep_min = params->sleep_min;
 	runtime->period_step = params->period_step;
@@ -474,7 +476,6 @@
 	runtime->silence_size = params->silence_size;
 	runtime->xfer_align = params->xfer_align;
         params->boundary = runtime->boundary;
-	spin_lock_irq(&runtime->lock);
 	if (snd_pcm_running(substream)) {
 		if (runtime->sleep_min)
 			snd_pcm_tick_prepare(substream);
@@ -946,7 +947,10 @@
 static int snd_pcm_reset(snd_pcm_substream_t *substream)
 {
 	int res;
+
+	spin_lock_irq(&substream->runtime->lock);
 	_SND_PCM_ACTION(reset, substream, 0, res, 0);
+	spin_unlock_irq(&substream->runtime->lock);
 	return res;
 }
 
@@ -983,6 +987,7 @@
 {
 	int res;
 	snd_card_t *card = substream->pcm->card;
+
 	snd_power_lock(card);
 	while (snd_power_get_state(card) != SNDRV_CTL_POWER_D0) {
 		if (substream->ffile->f_flags & O_NONBLOCK) {
@@ -992,7 +997,9 @@
 		snd_power_wait(card);
 	}
 
+	spin_lock_irq(&substream->runtime->lock);
 	_SND_PCM_ACTION(prepare, substream, 0, res, 0);
+	spin_unlock_irq(&substream->runtime->lock);
 
        _power_unlock:
 	snd_power_unlock(card);
@@ -1616,7 +1623,6 @@
 	snd_assert(err >= 0, return -EINVAL);
 
 	err = snd_pcm_hw_constraint_mask64(runtime, SNDRV_PCM_HW_PARAM_FORMAT, hw->formats);
-	//err = snd_pcm_hw_constraint_mask(runtime, SNDRV_PCM_HW_PARAM_FORMAT, hw->formats);
 	snd_assert(err >= 0, return -EINVAL);
 
 	err = snd_pcm_hw_constraint_mask(runtime, SNDRV_PCM_HW_PARAM_SUBFORMAT, 1 << SNDRV_PCM_SUBFORMAT_STD);
@@ -2051,21 +2057,9 @@
 	case SNDRV_PCM_IOCTL_CHANNEL_INFO:
 		return snd_pcm_channel_info(substream, (snd_pcm_channel_info_t *) arg);
 	case SNDRV_PCM_IOCTL_PREPARE:
-	{
-		int res;
-		spin_lock_irq(&substream->runtime->lock);
-		res = snd_pcm_prepare(substream);
-		spin_unlock_irq(&substream->runtime->lock);
-		return res;
-	}
+		return snd_pcm_prepare(substream);
 	case SNDRV_PCM_IOCTL_RESET:
-	{
-		int res;
-		spin_lock_irq(&substream->runtime->lock);
-		res = snd_pcm_reset(substream);
-		spin_unlock_irq(&substream->runtime->lock);
-		return res;
-	}
+		return snd_pcm_reset(substream);
 	case SNDRV_PCM_IOCTL_START:
 	{
 		int res;
@@ -2525,9 +2519,9 @@
 
 static struct vm_operations_struct snd_pcm_vm_ops_status =
 {
-	nopage:		snd_pcm_mmap_status_nopage,
+	.nopage =	snd_pcm_mmap_status_nopage,
 #ifndef VM_RESERVED
-	swapout:	snd_pcm_mmap_swapout,
+	.swapout =	snd_pcm_mmap_swapout,
 #endif
 };
 
@@ -2579,9 +2573,9 @@
 
 static struct vm_operations_struct snd_pcm_vm_ops_control =
 {
-	nopage:		snd_pcm_mmap_control_nopage,
+	.nopage =	snd_pcm_mmap_control_nopage,
 #ifndef VM_RESERVED
-	swapout:	snd_pcm_mmap_swapout,
+	.swapout =	snd_pcm_mmap_swapout,
 #endif
 };
 
@@ -2664,11 +2658,11 @@
 
 static struct vm_operations_struct snd_pcm_vm_ops_data =
 {
-	open:		snd_pcm_mmap_data_open,
-	close:		snd_pcm_mmap_data_close,
-	nopage:		snd_pcm_mmap_data_nopage,
+	.open =		snd_pcm_mmap_data_open,
+	.close =	snd_pcm_mmap_data_close,
+	.nopage =	snd_pcm_mmap_data_nopage,
 #ifndef VM_RESERVED
-	swapout:	snd_pcm_mmap_swapout,
+	.swapout =	snd_pcm_mmap_swapout,
 #endif
 };
 
@@ -2844,44 +2838,44 @@
 
 static struct file_operations snd_pcm_f_ops_playback = {
 #ifndef LINUX_2_2
-	owner:		THIS_MODULE,
+	.owner =	THIS_MODULE,
 #endif
-	write:		snd_pcm_write,
+	.write =	snd_pcm_write,
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 3, 44)
-	writev:		snd_pcm_writev,
+	.writev =	snd_pcm_writev,
 #endif
-	open:		snd_pcm_open,
-	release:	snd_pcm_release,
-	poll:		snd_pcm_playback_poll,
-	ioctl:		snd_pcm_playback_ioctl,
-	mmap:		snd_pcm_mmap,
-	fasync:		snd_pcm_fasync,
+	.open =		snd_pcm_open,
+	.release =	snd_pcm_release,
+	.poll =		snd_pcm_playback_poll,
+	.ioctl =	snd_pcm_playback_ioctl,
+	.mmap =		snd_pcm_mmap,
+	.fasync =	snd_pcm_fasync,
 };
 
 static struct file_operations snd_pcm_f_ops_capture = {
 #ifndef LINUX_2_2
-	owner:		THIS_MODULE,
+	.owner =	THIS_MODULE,
 #endif
-	read:		snd_pcm_read,
+	.read =		snd_pcm_read,
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 3, 44)
-	readv:		snd_pcm_readv,
+	.readv =	snd_pcm_readv,
 #endif
-	open:		snd_pcm_open,
-	release:	snd_pcm_release,
-	poll:		snd_pcm_capture_poll,
-	ioctl:		snd_pcm_capture_ioctl,
-	mmap:		snd_pcm_mmap,
-	fasync:		snd_pcm_fasync,
+	.open =		snd_pcm_open,
+	.release =	snd_pcm_release,
+	.poll =		snd_pcm_capture_poll,
+	.ioctl =	snd_pcm_capture_ioctl,
+	.mmap =		snd_pcm_mmap,
+	.fasync =	snd_pcm_fasync,
 };
 
 snd_minor_t snd_pcm_reg[2] =
 {
 	{
-		comment:	"digital audio playback",
-		f_ops:		&snd_pcm_f_ops_playback,
+		.comment =	"digital audio playback",
+		.f_ops =	&snd_pcm_f_ops_playback,
 	},
 	{
-		comment:	"digital audio capture",
-		f_ops:		&snd_pcm_f_ops_capture,
+		.comment =	"digital audio capture",
+		.f_ops =	&snd_pcm_f_ops_capture,
 	}
 };
diff -Nru a/sound/core/pcm_sgbuf.c b/sound/core/pcm_sgbuf.c
--- a/sound/core/pcm_sgbuf.c	Mon Oct  7 11:25:39 2002
+++ b/sound/core/pcm_sgbuf.c	Mon Oct  7 11:25:39 2002
@@ -19,7 +19,6 @@
  *
  */
 
-#define __NO_VERSION__
 #include <sound/driver.h>
 #include <linux/slab.h>
 #include <sound/core.h>
@@ -81,6 +80,9 @@
 {
 	struct snd_sg_buf *sgbuf;
 
+	/* return in case, when sgbuf is not initialized */
+	if (substream->dma_private == NULL)
+		return -EINVAL;
 	sgbuf = snd_magic_cast(snd_pcm_sgbuf_t, substream->dma_private, return -EINVAL);
 	sgbuf_shrink(sgbuf, 0);
 	if (sgbuf->table)
@@ -108,7 +110,8 @@
 	if (pages < sgbuf->pages) {
 		/* release unsed pages */
 		sgbuf_shrink(sgbuf, pages);
-		substream->runtime->dma_bytes = size;
+		if (substream->runtime)
+			substream->runtime->dma_bytes = size;
 		return 1; /* changed */
 	} else if (pages > sgbuf->tblsize) {
 		/* bigger than existing one.  reallocate the table. */
@@ -134,7 +137,8 @@
 		changed = 1;
 	}
 	sgbuf->size = size;
-	substream->runtime->dma_bytes = size;
+	if (substream->runtime)
+		substream->runtime->dma_bytes = size;
 	return changed;
 }
 
diff -Nru a/sound/core/pcm_timer.c b/sound/core/pcm_timer.c
--- a/sound/core/pcm_timer.c	Mon Oct  7 11:25:39 2002
+++ b/sound/core/pcm_timer.c	Mon Oct  7 11:25:39 2002
@@ -19,7 +19,6 @@
  *
  */
 
-#define __NO_VERSION__
 #include <sound/driver.h>
 #include <linux/time.h>
 #include <sound/core.h>
@@ -106,12 +105,12 @@
 
 static struct _snd_timer_hardware snd_pcm_timer =
 {
-	flags:		SNDRV_TIMER_HW_AUTO | SNDRV_TIMER_HW_SLAVE,
-	resolution:	0,
-	ticks:		1,
-	c_resolution:	snd_pcm_timer_resolution,
-	start:		snd_pcm_timer_start,
-	stop:		snd_pcm_timer_stop,
+	.flags =	SNDRV_TIMER_HW_AUTO | SNDRV_TIMER_HW_SLAVE,
+	.resolution =	0,
+	.ticks =	1,
+	.c_resolution =	snd_pcm_timer_resolution,
+	.start =	snd_pcm_timer_start,
+	.stop =		snd_pcm_timer_stop,
 };
 
 /*
diff -Nru a/sound/core/rawmidi.c b/sound/core/rawmidi.c
--- a/sound/core/rawmidi.c	Mon Oct  7 11:25:26 2002
+++ b/sound/core/rawmidi.c	Mon Oct  7 11:25:26 2002
@@ -820,11 +820,6 @@
 	return -ENOIOCTLCMD;
 }
 
-void snd_rawmidi_receive_reset(snd_rawmidi_substream_t * substream)
-{
-	/* TODO: reset current state */
-}
-
 int snd_rawmidi_receive(snd_rawmidi_substream_t * substream, const unsigned char *buffer, int count)
 {
 	unsigned long flags;
@@ -969,11 +964,6 @@
 	return result;
 }
 
-void snd_rawmidi_transmit_reset(snd_rawmidi_substream_t * substream)
-{
-	/* TODO: reset current state */
-}
-
 int snd_rawmidi_transmit_empty(snd_rawmidi_substream_t * substream)
 {
 	snd_rawmidi_runtime_t *runtime = substream->runtime;
@@ -1280,20 +1270,20 @@
 static struct file_operations snd_rawmidi_f_ops =
 {
 #ifndef LINUX_2_2
-	owner:		THIS_MODULE,
+	.owner =	THIS_MODULE,
 #endif
-	read:		snd_rawmidi_read,
-	write:		snd_rawmidi_write,
-	open:		snd_rawmidi_open,
-	release:	snd_rawmidi_release,
-	poll:		snd_rawmidi_poll,
-	ioctl:		snd_rawmidi_ioctl,
+	.read =		snd_rawmidi_read,
+	.write =	snd_rawmidi_write,
+	.open =		snd_rawmidi_open,
+	.release =	snd_rawmidi_release,
+	.poll =		snd_rawmidi_poll,
+	.ioctl =	snd_rawmidi_ioctl,
 };
 
 static snd_minor_t snd_rawmidi_reg =
 {
-	comment:	"raw midi",
-	f_ops:		&snd_rawmidi_f_ops,
+	.comment =	"raw midi",
+	.f_ops =	&snd_rawmidi_f_ops,
 };
 
 static int snd_rawmidi_alloc_substreams(snd_rawmidi_t *rmidi,
@@ -1326,9 +1316,9 @@
 	snd_rawmidi_t *rmidi;
 	int err;
 	static snd_device_ops_t ops = {
-		dev_free:	snd_rawmidi_dev_free,
-		dev_register:	snd_rawmidi_dev_register,
-		dev_unregister:	snd_rawmidi_dev_unregister
+		.dev_free = snd_rawmidi_dev_free,
+		.dev_register = snd_rawmidi_dev_register,
+		.dev_unregister = snd_rawmidi_dev_unregister
 	};
 
 	snd_assert(rrawmidi != NULL, return -EINVAL);
@@ -1564,9 +1554,7 @@
 EXPORT_SYMBOL(snd_rawmidi_drop_output);
 EXPORT_SYMBOL(snd_rawmidi_drain_output);
 EXPORT_SYMBOL(snd_rawmidi_drain_input);
-EXPORT_SYMBOL(snd_rawmidi_receive_reset);
 EXPORT_SYMBOL(snd_rawmidi_receive);
-EXPORT_SYMBOL(snd_rawmidi_transmit_reset);
 EXPORT_SYMBOL(snd_rawmidi_transmit_empty);
 EXPORT_SYMBOL(snd_rawmidi_transmit_peek);
 EXPORT_SYMBOL(snd_rawmidi_transmit_ack);
diff -Nru a/sound/core/rtctimer.c b/sound/core/rtctimer.c
--- a/sound/core/rtctimer.c	Mon Oct  7 11:25:28 2002
+++ b/sound/core/rtctimer.c	Mon Oct  7 11:25:28 2002
@@ -56,12 +56,12 @@
  * The hardware dependent description for this timer.
  */
 static struct _snd_timer_hardware rtc_hw = {
-	flags:		SNDRV_TIMER_HW_FIRST|SNDRV_TIMER_HW_AUTO,
-	ticks:		100000000L,		/* FIXME: XXX */
-	open:		rtctimer_open,
-	close:		rtctimer_close,
-	start:		rtctimer_start,
-	stop:		rtctimer_stop,
+	.flags =	SNDRV_TIMER_HW_FIRST|SNDRV_TIMER_HW_AUTO,
+	.ticks =	100000000L,		/* FIXME: XXX */
+	.open =		rtctimer_open,
+	.close =	rtctimer_close,
+	.start =	rtctimer_start,
+	.stop =		rtctimer_stop,
 };
 
 int rtctimer_freq = RTC_FREQ;		/* frequency */
diff -Nru a/sound/core/seq/Makefile b/sound/core/seq/Makefile
--- a/sound/core/seq/Makefile	Mon Oct  7 11:25:38 2002
+++ b/sound/core/seq/Makefile	Mon Oct  7 11:25:38 2002
@@ -67,12 +67,13 @@
 obj-$(CONFIG_SND_ICE1712) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o
 obj-$(CONFIG_SND_INTEL8X0) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o
 obj-$(CONFIG_SND_SONICVIBES) += 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_VIA686) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o
+obj-$(CONFIG_SND_VIA82XX) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o
 obj-$(CONFIG_SND_ALI5451) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o
 obj-$(CONFIG_SND_CS46XX) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o
 obj-$(CONFIG_SND_EMU10K1) += snd-seq-midi.o snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-midi-emul.o snd-seq-virmidi.o
 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-$(CONFIG_SND_USB_AUDIO) += snd-seq.o snd-seq-device.o snd-seq-midi-event.o snd-seq-virmidi.o
 
 obj-m := $(sort $(obj-m))
 
diff -Nru a/sound/core/seq/oss/seq_oss.c b/sound/core/seq/oss/seq_oss.c
--- a/sound/core/seq/oss/seq_oss.c	Mon Oct  7 11:25:36 2002
+++ b/sound/core/seq/oss/seq_oss.c	Mon Oct  7 11:25:36 2002
@@ -195,19 +195,19 @@
 static struct file_operations seq_oss_f_ops =
 {
 #ifndef LINUX_2_2
-	owner:		THIS_MODULE,
+	.owner =	THIS_MODULE,
 #endif
-	read:		odev_read,
-	write:		odev_write,
-	open:		odev_open,
-	release:	odev_release,
-	poll:		odev_poll,
-	ioctl:		odev_ioctl,
+	.read =		odev_read,
+	.write =	odev_write,
+	.open =		odev_open,
+	.release =	odev_release,
+	.poll =		odev_poll,
+	.ioctl =	odev_ioctl,
 };
 
 static snd_minor_t seq_oss_reg = {
-	comment:	"sequencer",
-	f_ops:		&seq_oss_f_ops,
+	.comment =	"sequencer",
+	.f_ops =	&seq_oss_f_ops,
 };
 
 static int __init
diff -Nru a/sound/core/seq/oss/seq_oss_event.c b/sound/core/seq/oss/seq_oss_event.c
--- a/sound/core/seq/oss/seq_oss_event.c	Mon Oct  7 11:25:26 2002
+++ b/sound/core/seq/oss/seq_oss_event.c	Mon Oct  7 11:25:26 2002
@@ -18,7 +18,6 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  */
 
-#define __NO_VERSION__
 #include "seq_oss_device.h"
 #include "seq_oss_synth.h"
 #include "seq_oss_midi.h"
diff -Nru a/sound/core/seq/oss/seq_oss_init.c b/sound/core/seq/oss/seq_oss_init.c
--- a/sound/core/seq/oss/seq_oss_init.c	Mon Oct  7 11:25:25 2002
+++ b/sound/core/seq/oss/seq_oss_init.c	Mon Oct  7 11:25:25 2002
@@ -20,7 +20,6 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  */
 
-#define __NO_VERSION__
 #include "seq_oss_device.h"
 #include "seq_oss_synth.h"
 #include "seq_oss_midi.h"
diff -Nru a/sound/core/seq/oss/seq_oss_ioctl.c b/sound/core/seq/oss/seq_oss_ioctl.c
--- a/sound/core/seq/oss/seq_oss_ioctl.c	Mon Oct  7 11:25:25 2002
+++ b/sound/core/seq/oss/seq_oss_ioctl.c	Mon Oct  7 11:25:25 2002
@@ -20,7 +20,6 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  */
 
-#define __NO_VERSION__
 #include "seq_oss_device.h"
 #include "seq_oss_readq.h"
 #include "seq_oss_writeq.h"
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	Mon Oct  7 11:25:25 2002
+++ b/sound/core/seq/oss/seq_oss_midi.c	Mon Oct  7 11:25:25 2002
@@ -20,7 +20,6 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  */
 
-#define __NO_VERSION__
 #include "seq_oss_midi.h"
 #include "seq_oss_readq.h"
 #include "seq_oss_timer.h"
diff -Nru a/sound/core/seq/oss/seq_oss_readq.c b/sound/core/seq/oss/seq_oss_readq.c
--- a/sound/core/seq/oss/seq_oss_readq.c	Mon Oct  7 11:25:27 2002
+++ b/sound/core/seq/oss/seq_oss_readq.c	Mon Oct  7 11:25:27 2002
@@ -20,7 +20,6 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  */
 
-#define __NO_VERSION__
 #include "seq_oss_readq.h"
 #include "seq_oss_event.h"
 #include <sound/seq_oss_legacy.h>
diff -Nru a/sound/core/seq/oss/seq_oss_rw.c b/sound/core/seq/oss/seq_oss_rw.c
--- a/sound/core/seq/oss/seq_oss_rw.c	Mon Oct  7 11:25:25 2002
+++ b/sound/core/seq/oss/seq_oss_rw.c	Mon Oct  7 11:25:25 2002
@@ -20,7 +20,6 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  */
 
-#define __NO_VERSION__
 #include "seq_oss_device.h"
 #include "seq_oss_readq.h"
 #include "seq_oss_writeq.h"
diff -Nru a/sound/core/seq/oss/seq_oss_synth.c b/sound/core/seq/oss/seq_oss_synth.c
--- a/sound/core/seq/oss/seq_oss_synth.c	Mon Oct  7 11:25:25 2002
+++ b/sound/core/seq/oss/seq_oss_synth.c	Mon Oct  7 11:25:25 2002
@@ -20,7 +20,6 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  */
 
-#define __NO_VERSION__
 #include "seq_oss_synth.h"
 #include "seq_oss_midi.h"
 #include "../seq_lock.h"
@@ -147,6 +146,8 @@
 	debug_printk(("synth %s registered %d\n", rec->name, i));
 	spin_unlock_irqrestore(&register_lock, flags);
 	dev->driver_data = rec;
+	if (i < SNDRV_CARDS)
+		snd_oss_info_register(SNDRV_OSS_INFO_DEV_SYNTH, i, rec->name);
 	return 0;
 }
 
@@ -177,6 +178,8 @@
 		max_synth_devs = index + 1;
 	}
 	spin_unlock_irqrestore(&register_lock, flags);
+	if (rec->seq_device < SNDRV_CARDS)
+		snd_oss_info_unregister(SNDRV_OSS_INFO_DEV_SYNTH, rec->seq_device);
 
 	snd_use_lock_sync(&rec->use_lock);
 	kfree(rec);
diff -Nru a/sound/core/seq/oss/seq_oss_timer.c b/sound/core/seq/oss/seq_oss_timer.c
--- a/sound/core/seq/oss/seq_oss_timer.c	Mon Oct  7 11:25:27 2002
+++ b/sound/core/seq/oss/seq_oss_timer.c	Mon Oct  7 11:25:27 2002
@@ -20,7 +20,6 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  */
 
-#define __NO_VERSION__
 #include "seq_oss_timer.h"
 #include "seq_oss_event.h"
 #include <sound/seq_oss_legacy.h>
diff -Nru a/sound/core/seq/oss/seq_oss_writeq.c b/sound/core/seq/oss/seq_oss_writeq.c
--- a/sound/core/seq/oss/seq_oss_writeq.c	Mon Oct  7 11:25:28 2002
+++ b/sound/core/seq/oss/seq_oss_writeq.c	Mon Oct  7 11:25:28 2002
@@ -20,7 +20,6 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  */
 
-#define __NO_VERSION__
 #include "seq_oss_writeq.h"
 #include "seq_oss_event.h"
 #include "seq_oss_timer.h"
diff -Nru a/sound/core/seq/seq_clientmgr.c b/sound/core/seq/seq_clientmgr.c
--- a/sound/core/seq/seq_clientmgr.c	Mon Oct  7 11:25:26 2002
+++ b/sound/core/seq/seq_clientmgr.c	Mon Oct  7 11:25:26 2002
@@ -21,7 +21,6 @@
  *
  */
 
-#define __NO_VERSION__
 #include <sound/driver.h>
 #include <linux/init.h>
 #include <linux/slab.h>
@@ -2467,20 +2466,20 @@
 static struct file_operations snd_seq_f_ops =
 {
 #ifndef LINUX_2_2
-	owner:		THIS_MODULE,
+	.owner =	THIS_MODULE,
 #endif
-	read:		snd_seq_read,
-	write:		snd_seq_write,
-	open:		snd_seq_open,
-	release:	snd_seq_release,
-	poll:		snd_seq_poll,
-	ioctl:		snd_seq_ioctl,
+	.read =		snd_seq_read,
+	.write =	snd_seq_write,
+	.open =		snd_seq_open,
+	.release =	snd_seq_release,
+	.poll =		snd_seq_poll,
+	.ioctl =	snd_seq_ioctl,
 };
 
 static snd_minor_t snd_seq_reg =
 {
-	comment:	"sequencer",
-	f_ops:		&snd_seq_f_ops,
+	.comment =	"sequencer",
+	.f_ops =	&snd_seq_f_ops,
 };
 
 
diff -Nru a/sound/core/seq/seq_clientmgr.h b/sound/core/seq/seq_clientmgr.h
--- a/sound/core/seq/seq_clientmgr.h	Mon Oct  7 11:25:37 2002
+++ b/sound/core/seq/seq_clientmgr.h	Mon Oct  7 11:25:37 2002
@@ -22,6 +22,7 @@
 #define __SND_SEQ_CLIENTMGR_H
 
 #include <sound/seq_kernel.h>
+#include <linux/bitops.h>
 #include "seq_fifo.h"
 #include "seq_ports.h"
 #include "seq_lock.h"
@@ -53,7 +54,7 @@
 	char name[64];		/* client name */
 	int number;		/* client number */
 	unsigned int filter;	/* filter flags */
-	bitmap_member(event_filter, 256);
+	DECLARE_BITMAP(event_filter, 256);
 	snd_use_lock_t use_lock;
 	int event_lost;
 	/* ports */
diff -Nru a/sound/core/seq/seq_device.c b/sound/core/seq/seq_device.c
--- a/sound/core/seq/seq_device.c	Mon Oct  7 11:25:26 2002
+++ b/sound/core/seq/seq_device.c	Mon Oct  7 11:25:26 2002
@@ -164,9 +164,9 @@
 	ops_list_t *ops;
 	int err;
 	static snd_device_ops_t dops = {
-		dev_free:	snd_seq_device_dev_free,
-		dev_register:	snd_seq_device_dev_register,
-		dev_unregister:	snd_seq_device_dev_unregister
+		.dev_free = snd_seq_device_dev_free,
+		.dev_register = snd_seq_device_dev_register,
+		.dev_unregister = snd_seq_device_dev_unregister
 	};
 
 	if (result)
diff -Nru a/sound/core/seq/seq_fifo.c b/sound/core/seq/seq_fifo.c
--- a/sound/core/seq/seq_fifo.c	Mon Oct  7 11:25:35 2002
+++ b/sound/core/seq/seq_fifo.c	Mon Oct  7 11:25:35 2002
@@ -19,7 +19,6 @@
  *
  */
 
-#define __NO_VERSION__
 #include <sound/driver.h>
 #include <sound/core.h>
 #include <linux/slab.h>
diff -Nru a/sound/core/seq/seq_info.c b/sound/core/seq/seq_info.c
--- a/sound/core/seq/seq_info.c	Mon Oct  7 11:25:27 2002
+++ b/sound/core/seq/seq_info.c	Mon Oct  7 11:25:27 2002
@@ -19,7 +19,6 @@
  *
  */
 
-#define __NO_VERSION__
 #include <sound/driver.h>
 #include <linux/init.h>
 #include <sound/core.h>
diff -Nru a/sound/core/seq/seq_lock.c b/sound/core/seq/seq_lock.c
--- a/sound/core/seq/seq_lock.c	Mon Oct  7 11:25:32 2002
+++ b/sound/core/seq/seq_lock.c	Mon Oct  7 11:25:32 2002
@@ -19,7 +19,6 @@
  *
  */
 
-#define __NO_VERSION__
 #include <sound/driver.h>
 #include <sound/core.h>
 #include "seq_lock.h"
diff -Nru a/sound/core/seq/seq_memory.c b/sound/core/seq/seq_memory.c
--- a/sound/core/seq/seq_memory.c	Mon Oct  7 11:25:25 2002
+++ b/sound/core/seq/seq_memory.c	Mon Oct  7 11:25:25 2002
@@ -20,7 +20,6 @@
  *
  */
 
-#define __NO_VERSION__
 #include <sound/driver.h>
 #include <linux/init.h>
 #include <linux/slab.h>
diff -Nru a/sound/core/seq/seq_ports.c b/sound/core/seq/seq_ports.c
--- a/sound/core/seq/seq_ports.c	Mon Oct  7 11:25:36 2002
+++ b/sound/core/seq/seq_ports.c	Mon Oct  7 11:25:36 2002
@@ -20,7 +20,6 @@
  *
  */
 
-#define __NO_VERSION__
 #include <sound/driver.h>
 #include <sound/core.h>
 #include <linux/slab.h>
diff -Nru a/sound/core/seq/seq_prioq.c b/sound/core/seq/seq_prioq.c
--- a/sound/core/seq/seq_prioq.c	Mon Oct  7 11:25:25 2002
+++ b/sound/core/seq/seq_prioq.c	Mon Oct  7 11:25:25 2002
@@ -19,7 +19,6 @@
  *
  */
 
-#define __NO_VERSION__
 #include <sound/driver.h>
 #include <linux/time.h>
 #include <linux/slab.h>
diff -Nru a/sound/core/seq/seq_queue.c b/sound/core/seq/seq_queue.c
--- a/sound/core/seq/seq_queue.c	Mon Oct  7 11:25:36 2002
+++ b/sound/core/seq/seq_queue.c	Mon Oct  7 11:25:36 2002
@@ -35,7 +35,6 @@
  *     - Addition of experimental sync support.
  */
 
-#define __NO_VERSION__
 #include <sound/driver.h>
 #include <linux/init.h>
 #include <linux/slab.h>
diff -Nru a/sound/core/seq/seq_queue.h b/sound/core/seq/seq_queue.h
--- a/sound/core/seq/seq_queue.h	Mon Oct  7 11:25:28 2002
+++ b/sound/core/seq/seq_queue.h	Mon Oct  7 11:25:28 2002
@@ -26,6 +26,7 @@
 #include "seq_lock.h"
 #include <linux/interrupt.h>
 #include <linux/list.h>
+#include <linux/bitops.h>
 
 #define SEQ_QUEUE_NO_OWNER (-1)
 
@@ -51,7 +52,7 @@
 	spinlock_t check_lock;
 
 	/* clients which uses this queue (bitmap) */
-	bitmap_member(clients_bitmap, SNDRV_SEQ_MAX_CLIENTS);
+	DECLARE_BITMAP(clients_bitmap, SNDRV_SEQ_MAX_CLIENTS);
 	unsigned int clients;	/* users of this queue */
 	struct semaphore timer_mutex;
 
diff -Nru a/sound/core/seq/seq_system.c b/sound/core/seq/seq_system.c
--- a/sound/core/seq/seq_system.c	Mon Oct  7 11:25:36 2002
+++ b/sound/core/seq/seq_system.c	Mon Oct  7 11:25:36 2002
@@ -19,7 +19,6 @@
  *
  */
 
-#define __NO_VERSION__
 #include <sound/driver.h>
 #include <linux/init.h>
 #include <sound/core.h>
diff -Nru a/sound/core/seq/seq_timer.c b/sound/core/seq/seq_timer.c
--- a/sound/core/seq/seq_timer.c	Mon Oct  7 11:25:36 2002
+++ b/sound/core/seq/seq_timer.c	Mon Oct  7 11:25:36 2002
@@ -20,7 +20,6 @@
  *
  */
 
-#define __NO_VERSION__
 #include <sound/driver.h>
 #include <sound/core.h>
 #include <linux/slab.h>
diff -Nru a/sound/core/seq/seq_virmidi.c b/sound/core/seq/seq_virmidi.c
--- a/sound/core/seq/seq_virmidi.c	Mon Oct  7 11:25:36 2002
+++ b/sound/core/seq/seq_virmidi.c	Mon Oct  7 11:25:36 2002
@@ -341,15 +341,15 @@
  */
 
 static snd_rawmidi_ops_t snd_virmidi_input_ops = {
-	open: snd_virmidi_input_open,
-	close: snd_virmidi_input_close,
-	trigger: snd_virmidi_input_trigger,
+	.open = snd_virmidi_input_open,
+	.close = snd_virmidi_input_close,
+	.trigger = snd_virmidi_input_trigger,
 };
 
 static snd_rawmidi_ops_t snd_virmidi_output_ops = {
-	open: snd_virmidi_output_open,
-	close: snd_virmidi_output_close,
-	trigger: snd_virmidi_output_trigger,
+	.open = snd_virmidi_output_open,
+	.close = snd_virmidi_output_close,
+	.trigger = snd_virmidi_output_trigger,
 };
 
 /*
@@ -468,8 +468,8 @@
  *
  */
 static snd_rawmidi_global_ops_t snd_virmidi_global_ops = {
-	dev_register: snd_virmidi_dev_register,
-	dev_unregister: snd_virmidi_dev_unregister,
+	.dev_register = snd_virmidi_dev_register,
+	.dev_unregister = snd_virmidi_dev_unregister,
 };
 
 /*
diff -Nru a/sound/core/sound.c b/sound/core/sound.c
--- a/sound/core/sound.c	Mon Oct  7 11:25:36 2002
+++ b/sound/core/sound.c	Mon Oct  7 11:25:36 2002
@@ -123,7 +123,7 @@
 	struct file_operations *old_fops;
 	int err = 0;
 
-	if (dev != SNDRV_MINOR_SEQUENCER) {
+	if (dev != SNDRV_MINOR_SEQUENCER && dev != SNDRV_MINOR_TIMER) {
 		if (snd_cards[card] == NULL) {
 #ifdef CONFIG_KMOD
 			snd_request_card(card);
@@ -154,9 +154,9 @@
 struct file_operations snd_fops =
 {
 #ifndef LINUX_2_2
-	owner:		THIS_MODULE,
+	.owner =	THIS_MODULE,
 #endif
-	open:		snd_open
+	.open =		snd_open
 };
 
 static int snd_kernel_minor(int type, snd_card_t * card, int dev)
@@ -358,18 +358,12 @@
 static void __exit alsa_sound_exit(void)
 {
 #ifdef CONFIG_DEVFS_FS
-	devfs_handle_t master;
 	char controlname[24];
 	short controlnum;
 
 	for (controlnum = 0; controlnum < snd_cards_limit; controlnum++) {
 		sprintf(controlname, "snd/controlC%d", controlnum);
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)
-		master = devfs_find_handle(NULL, controlname, strlen(controlname), 0, 0, DEVFS_SPECIAL_CHR, 0);
-		devfs_unregister(master);
-#else
 		devfs_find_and_unregister(NULL, controlname, 0, 0, DEVFS_SPECIAL_CHR, 0);
-#endif
 	}
 #endif
 	
diff -Nru a/sound/core/sound_oss.c b/sound/core/sound_oss.c
--- a/sound/core/sound_oss.c	Mon Oct  7 11:25:39 2002
+++ b/sound/core/sound_oss.c	Mon Oct  7 11:25:39 2002
@@ -19,7 +19,6 @@
  *
  */
 
-#define __NO_VERSION__
 #include <sound/driver.h>
 
 #ifdef CONFIG_SND_OSSEMUL
diff -Nru a/sound/core/timer.c b/sound/core/timer.c
--- a/sound/core/timer.c	Mon Oct  7 11:25:36 2002
+++ b/sound/core/timer.c	Mon Oct  7 11:25:36 2002
@@ -637,9 +637,9 @@
 	snd_timer_t *timer;
 	int err;
 	static snd_device_ops_t ops = {
-		dev_free:	snd_timer_dev_free,
-		dev_register:	snd_timer_dev_register,
-		dev_unregister:	snd_timer_dev_unregister
+		.dev_free = snd_timer_dev_free,
+		.dev_register = snd_timer_dev_register,
+		.dev_unregister = snd_timer_dev_unregister
 	};
 
 	snd_assert(tid != NULL, return -EINVAL);
@@ -821,11 +821,11 @@
 
 static struct _snd_timer_hardware snd_timer_system =
 {
-	flags:		SNDRV_TIMER_HW_FIRST,
-	resolution:	1000000000L / HZ,
-	ticks:		10000000L,
-	start:		snd_timer_s_start,
-	stop:		snd_timer_s_stop
+	.flags =	SNDRV_TIMER_HW_FIRST,
+	.resolution =	1000000000L / HZ,
+	.ticks =	10000000L,
+	.start =	snd_timer_s_start,
+	.stop =		snd_timer_s_stop
 };
 
 static void snd_timer_free_system(snd_timer_t *timer)
@@ -1330,19 +1330,19 @@
 static struct file_operations snd_timer_f_ops =
 {
 #ifndef LINUX_2_2
-	owner:		THIS_MODULE,
+	.owner =	THIS_MODULE,
 #endif
-	read:		snd_timer_user_read,
-	open:		snd_timer_user_open,
-	release:	snd_timer_user_release,
-	poll:		snd_timer_user_poll,
-	ioctl:		snd_timer_user_ioctl,
+	.read =		snd_timer_user_read,
+	.open =		snd_timer_user_open,
+	.release =	snd_timer_user_release,
+	.poll =		snd_timer_user_poll,
+	.ioctl =	snd_timer_user_ioctl,
 };
 
 static snd_minor_t snd_timer_reg =
 {
-	comment:	"timer",
-	f_ops:		&snd_timer_f_ops,
+	.comment =	"timer",
+	.f_ops =	&snd_timer_f_ops,
 };
 
 /*
diff -Nru a/sound/core/wrappers.c b/sound/core/wrappers.c
--- a/sound/core/wrappers.c	Mon Oct  7 11:25:26 2002
+++ b/sound/core/wrappers.c	Mon Oct  7 11:25:26 2002
@@ -23,7 +23,6 @@
 #include "config.h"
 #endif
 
-#define __NO_VERSION__
 #include <linux/version.h>
 #include <linux/config.h>
 #ifdef ALSA_BUILD
@@ -59,4 +58,61 @@
 {
 	vfree(obj);
 }
+#endif
+
+
+/* check the condition in <sound/core.h> !! */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 4, 0)
+#if defined(__i386__) || defined(__ppc__) || defined(__x86_64__)
+
+#include <linux/pci.h>
+
+/* to be sure... */
+#ifdef HACK_PCI_ALLOC_CONSISTENT
+#error pci_alloc_consistent hack is already defined!!
+#endif
+
+/*
+ * A dirty hack... when the kernel code is fixed this should be removed.
+ *
+ * since pci_alloc_consistent always tries GFP_DMA when the requested
+ * pci memory region is below 32bit, it happens quite often that even
+ * 2 order of pages cannot be allocated.
+ *
+ * so in the following, we allocate at first without dma_mask, so that
+ * allocation will be done without GFP_DMA.  if the area doesn't match
+ * with the requested region, then realloate with the original dma_mask
+ * again.
+ */
+
+void *snd_pci_hack_alloc_consistent(struct pci_dev *hwdev, size_t size,
+				    dma_addr_t *dma_handle)
+{
+	void *ret;
+	u64 dma_mask;
+	unsigned long rmask;
+
+	if (hwdev == NULL)
+		return pci_alloc_consistent(hwdev, size, dma_handle);
+	dma_mask = hwdev->dma_mask;
+	rmask = ~((unsigned long)dma_mask);
+	hwdev->dma_mask = 0xffffffff; /* do without masking */
+	ret = pci_alloc_consistent(hwdev, size, dma_handle);
+	hwdev->dma_mask = dma_mask; /* restore */
+	if (ret) {
+		/* obtained address is out of range? */
+		if (((unsigned long)*dma_handle + size - 1) & rmask) {
+			/* reallocate with the proper mask */
+			pci_free_consistent(hwdev, size, ret, *dma_handle);
+			ret = pci_alloc_consistent(hwdev, size, dma_handle);
+		}
+	} else {
+		/* wish to success now with the proper mask... */
+		if (dma_mask != 0xffffffff)
+			ret = pci_alloc_consistent(hwdev, size, dma_handle);
+	}
+	return ret;
+}
+
+#endif
 #endif
diff -Nru a/sound/drivers/dummy.c b/sound/drivers/dummy.c
--- a/sound/drivers/dummy.c	Mon Oct  7 11:25:38 2002
+++ b/sound/drivers/dummy.c	Mon Oct  7 11:25:38 2002
@@ -262,38 +262,38 @@
 
 static snd_pcm_hardware_t snd_card_dummy_playback =
 {
-	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
 				 SNDRV_PCM_INFO_MMAP_VALID),
-	formats:		USE_FORMATS,
-	rates:			SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
-	rate_min:		5500,
-	rate_max:		48000,
-	channels_min:		USE_CHANNELS_MIN,
-	channels_max:		USE_CHANNELS_MAX,
-	buffer_bytes_max:	MAX_BUFFER_SIZE,
-	period_bytes_min:	64,
-	period_bytes_max:	MAX_BUFFER_SIZE,
-	periods_min:		USE_PERIODS_MIN,
-	periods_max:		USE_PERIODS_MAX,
-	fifo_size:		0,
+	.formats =		USE_FORMATS,
+	.rates =		SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+	.rate_min =		5500,
+	.rate_max =		48000,
+	.channels_min =		USE_CHANNELS_MIN,
+	.channels_max =		USE_CHANNELS_MAX,
+	.buffer_bytes_max =	MAX_BUFFER_SIZE,
+	.period_bytes_min =	64,
+	.period_bytes_max =	MAX_BUFFER_SIZE,
+	.periods_min =		USE_PERIODS_MIN,
+	.periods_max =		USE_PERIODS_MAX,
+	.fifo_size =		0,
 };
 
 static snd_pcm_hardware_t snd_card_dummy_capture =
 {
-	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
 				 SNDRV_PCM_INFO_MMAP_VALID),
-	formats:		USE_FORMATS,
-	rates:			SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
-	rate_min:		5500,
-	rate_max:		48000,
-	channels_min:		USE_CHANNELS_MIN,
-	channels_max:		USE_CHANNELS_MAX,
-	buffer_bytes_max:	MAX_BUFFER_SIZE,
-	period_bytes_min:	64,
-	period_bytes_max:	MAX_BUFFER_SIZE,
-	periods_min:		USE_PERIODS_MIN,
-	periods_max:		USE_PERIODS_MAX,
-	fifo_size:		0,
+	.formats =		USE_FORMATS,
+	.rates =		SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+	.rate_min =		5500,
+	.rate_max =		48000,
+	.channels_min =		USE_CHANNELS_MIN,
+	.channels_max =		USE_CHANNELS_MAX,
+	.buffer_bytes_max =	MAX_BUFFER_SIZE,
+	.period_bytes_min =	64,
+	.period_bytes_max =	MAX_BUFFER_SIZE,
+	.periods_min =		USE_PERIODS_MIN,
+	.periods_max =		USE_PERIODS_MAX,
+	.fifo_size =		0,
 };
 
 static void snd_card_dummy_runtime_free(snd_pcm_runtime_t *runtime)
@@ -376,21 +376,21 @@
 }
 
 static snd_pcm_ops_t snd_card_dummy_playback_ops = {
-	open:			snd_card_dummy_playback_open,
-	close:			snd_card_dummy_playback_close,
-	ioctl:			snd_card_dummy_playback_ioctl,
-	prepare:		snd_card_dummy_playback_prepare,
-	trigger:		snd_card_dummy_playback_trigger,
-	pointer:		snd_card_dummy_playback_pointer,
+	.open =			snd_card_dummy_playback_open,
+	.close =		snd_card_dummy_playback_close,
+	.ioctl =		snd_card_dummy_playback_ioctl,
+	.prepare =		snd_card_dummy_playback_prepare,
+	.trigger =		snd_card_dummy_playback_trigger,
+	.pointer =		snd_card_dummy_playback_pointer,
 };
 
 static snd_pcm_ops_t snd_card_dummy_capture_ops = {
-	open:			snd_card_dummy_capture_open,
-	close:			snd_card_dummy_capture_close,
-	ioctl:			snd_card_dummy_capture_ioctl,
-	prepare:		snd_card_dummy_capture_prepare,
-	trigger:		snd_card_dummy_capture_trigger,
-	pointer:		snd_card_dummy_capture_pointer,
+	.open =			snd_card_dummy_capture_open,
+	.close =		snd_card_dummy_capture_close,
+	.ioctl =		snd_card_dummy_capture_ioctl,
+	.prepare =		snd_card_dummy_capture_prepare,
+	.trigger =		snd_card_dummy_capture_trigger,
+	.pointer =		snd_card_dummy_capture_pointer,
 };
 
 static int __init snd_card_dummy_pcm(snd_card_dummy_t *dummy, int device, int substreams)
@@ -409,10 +409,10 @@
 }
 
 #define DUMMY_VOLUME(xname, xindex, addr) \
-{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \
-  info: snd_dummy_volume_info, \
-  get: snd_dummy_volume_get, put: snd_dummy_volume_put, \
-  private_value: addr }
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
+  .info = snd_dummy_volume_info, \
+  .get = snd_dummy_volume_get, .put = snd_dummy_volume_put, \
+  .private_value = addr }
 
 static int snd_dummy_volume_info(snd_kcontrol_t * kcontrol, snd_ctl_elem_info_t * uinfo)
 {
@@ -446,7 +446,7 @@
 	left = ucontrol->value.integer.value[0] % 101;
 	right = ucontrol->value.integer.value[1] % 101;
 	spin_lock_irqsave(&dummy->mixer_lock, flags);
-	change = dummy->mixer_volume[addr][0] != left &&
+	change = dummy->mixer_volume[addr][0] != left ||
 	         dummy->mixer_volume[addr][1] != right;
 	dummy->mixer_volume[addr][0] = left;
 	dummy->mixer_volume[addr][1] = right;
@@ -455,10 +455,10 @@
 }                                                                                                                                                                                                                                                                                                            
 
 #define DUMMY_CAPSRC(xname, xindex, addr) \
-{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \
-  info: snd_dummy_capsrc_info, \
-  get: snd_dummy_capsrc_get, put: snd_dummy_capsrc_put, \
-  private_value: addr }
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
+  .info = snd_dummy_capsrc_info, \
+  .get = snd_dummy_capsrc_get, .put = snd_dummy_capsrc_put, \
+  .private_value = addr }
 
 static int snd_dummy_capsrc_info(snd_kcontrol_t * kcontrol, snd_ctl_elem_info_t * uinfo)
 {
diff -Nru a/sound/drivers/mpu401/Makefile b/sound/drivers/mpu401/Makefile
--- a/sound/drivers/mpu401/Makefile	Mon Oct  7 11:25:40 2002
+++ b/sound/drivers/mpu401/Makefile	Mon Oct  7 11:25:40 2002
@@ -35,7 +35,7 @@
 obj-$(CONFIG_SND_ICE1712) += snd-mpu401-uart.o
 obj-$(CONFIG_SND_INTEL8X0) += snd-mpu401-uart.o
 obj-$(CONFIG_SND_SONICVIBES) += snd-mpu401-uart.o
-obj-$(CONFIG_SND_VIA686) += snd-mpu401-uart.o
+obj-$(CONFIG_SND_VIA82XX) += snd-mpu401-uart.o
 obj-$(CONFIG_SND_ALI5451) += snd-mpu401-uart.o
 obj-$(CONFIG_SND_TRIDENT) += snd-mpu401-uart.o
 obj-$(CONFIG_SND_YMFPCI) += snd-mpu401-uart.o
diff -Nru a/sound/drivers/mpu401/mpu401_uart.c b/sound/drivers/mpu401/mpu401_uart.c
--- a/sound/drivers/mpu401/mpu401_uart.c	Mon Oct  7 11:25:32 2002
+++ b/sound/drivers/mpu401/mpu401_uart.c	Mon Oct  7 11:25:32 2002
@@ -350,16 +350,16 @@
 
 static snd_rawmidi_ops_t snd_mpu401_uart_output =
 {
-	open:		snd_mpu401_uart_output_open,
-	close:		snd_mpu401_uart_output_close,
-	trigger:	snd_mpu401_uart_output_trigger,
+	.open =		snd_mpu401_uart_output_open,
+	.close =	snd_mpu401_uart_output_close,
+	.trigger =	snd_mpu401_uart_output_trigger,
 };
 
 static snd_rawmidi_ops_t snd_mpu401_uart_input =
 {
-	open:		snd_mpu401_uart_input_open,
-	close:		snd_mpu401_uart_input_close,
-	trigger:	snd_mpu401_uart_input_trigger,
+	.open =		snd_mpu401_uart_input_open,
+	.close =	snd_mpu401_uart_input_close,
+	.trigger =	snd_mpu401_uart_input_trigger,
 };
 
 static void snd_mpu401_uart_free(snd_rawmidi_t *rmidi)
diff -Nru a/sound/drivers/mtpav.c b/sound/drivers/mtpav.c
--- a/sound/drivers/mtpav.c	Mon Oct  7 11:25:37 2002
+++ b/sound/drivers/mtpav.c	Mon Oct  7 11:25:37 2002
@@ -606,15 +606,15 @@
  */
 
 static snd_rawmidi_ops_t snd_mtpav_output = {
-	open:		snd_mtpav_output_open,
-	close:		snd_mtpav_output_close,
-	trigger:	snd_mtpav_output_trigger,
+	.open =		snd_mtpav_output_open,
+	.close =	snd_mtpav_output_close,
+	.trigger =	snd_mtpav_output_trigger,
 };
 
 static snd_rawmidi_ops_t snd_mtpav_input = {
-	open:		snd_mtpav_input_open,
-	close:		snd_mtpav_input_close,
-	trigger:	snd_mtpav_input_trigger,
+	.open =		snd_mtpav_input_open,
+	.close =	snd_mtpav_input_close,
+	.trigger =	snd_mtpav_input_trigger,
 };
 
 
diff -Nru a/sound/drivers/opl3/opl3_drums.c b/sound/drivers/opl3/opl3_drums.c
--- a/sound/drivers/opl3/opl3_drums.c	Mon Oct  7 11:25:35 2002
+++ b/sound/drivers/opl3/opl3_drums.c	Mon Oct  7 11:25:35 2002
@@ -19,7 +19,6 @@
  *
  */
 
-#define __NO_VERSION__
 #include "opl3_voice.h"
 
 extern char snd_opl3_regmap[MAX_OPL2_VOICES][4];
diff -Nru a/sound/drivers/opl3/opl3_lib.c b/sound/drivers/opl3/opl3_lib.c
--- a/sound/drivers/opl3/opl3_lib.c	Mon Oct  7 11:25:26 2002
+++ b/sound/drivers/opl3/opl3_lib.c	Mon Oct  7 11:25:26 2002
@@ -101,8 +101,10 @@
 	spin_lock_irqsave(&opl3->reg_lock, flags);
 
 	writel((unsigned int)cmd, port << 2);
+	udelay(10);
 
 	writel((unsigned int)val, (port + 1) << 2);
+	udelay(30);
 
 	spin_unlock_irqrestore(&opl3->reg_lock, flags);
 }
@@ -257,20 +259,20 @@
 
 static struct _snd_timer_hardware snd_opl3_timer1 =
 {
-	flags:		SNDRV_TIMER_HW_STOP,
-	resolution:	80000,
-	ticks:		256,
-	start:		snd_opl3_timer1_start,
-	stop:		snd_opl3_timer1_stop,
+	.flags =	SNDRV_TIMER_HW_STOP,
+	.resolution =	80000,
+	.ticks =	256,
+	.start =	snd_opl3_timer1_start,
+	.stop =		snd_opl3_timer1_stop,
 };
 
 static struct _snd_timer_hardware snd_opl3_timer2 =
 {
-	flags:		SNDRV_TIMER_HW_STOP,
-	resolution:	320000,
-	ticks:		256,
-	start:		snd_opl3_timer2_start,
-	stop:		snd_opl3_timer2_stop,
+	.flags =	SNDRV_TIMER_HW_STOP,
+	.resolution =	320000,
+	.ticks =	256,
+	.start =	snd_opl3_timer2_start,
+	.stop =		snd_opl3_timer2_stop,
 };
 
 static int snd_opl3_timer1_init(opl3_t * opl3, int timer_no)
@@ -378,7 +380,7 @@
 	opl3_t *opl3;
 	int err;
 	static snd_device_ops_t ops = {
-		dev_free: snd_opl3_dev_free,
+		.dev_free = snd_opl3_dev_free,
 	};
 
 	*ropl3 = NULL;
diff -Nru a/sound/drivers/opl3/opl3_midi.c b/sound/drivers/opl3/opl3_midi.c
--- a/sound/drivers/opl3/opl3_midi.c	Mon Oct  7 11:25:35 2002
+++ b/sound/drivers/opl3/opl3_midi.c	Mon Oct  7 11:25:35 2002
@@ -22,7 +22,6 @@
 #undef DEBUG_ALLOC
 #undef DEBUG_MIDI
 
-#define __NO_VERSION__
 #include "opl3_voice.h"
 #include <sound/asoundef.h>
 
diff -Nru a/sound/drivers/opl3/opl3_oss.c b/sound/drivers/opl3/opl3_oss.c
--- a/sound/drivers/opl3/opl3_oss.c	Mon Oct  7 11:25:25 2002
+++ b/sound/drivers/opl3/opl3_oss.c	Mon Oct  7 11:25:25 2002
@@ -18,7 +18,6 @@
  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  */
 
-#define __NO_VERSION__
 #include "opl3_voice.h"
 #include <linux/slab.h>
 
@@ -47,12 +46,12 @@
 extern snd_midi_op_t opl3_ops;
 
 static snd_seq_oss_callback_t oss_callback = {
-	owner: THIS_MODULE,
-	open: snd_opl3_open_seq_oss,
-	close: snd_opl3_close_seq_oss,
-	ioctl: snd_opl3_ioctl_seq_oss,
-	load_patch: snd_opl3_load_patch_seq_oss,
-	reset: snd_opl3_reset_seq_oss,
+	.owner = 	THIS_MODULE,
+	.open =		snd_opl3_open_seq_oss,
+	.close =	snd_opl3_close_seq_oss,
+	.ioctl =	snd_opl3_ioctl_seq_oss,
+	.load_patch =	snd_opl3_load_patch_seq_oss,
+	.reset =	snd_opl3_reset_seq_oss,
 };
 
 static int snd_opl3_oss_event_input(snd_seq_event_t *ev, int direct,
diff -Nru a/sound/drivers/opl3/opl3_seq.c b/sound/drivers/opl3/opl3_seq.c
--- a/sound/drivers/opl3/opl3_seq.c	Mon Oct  7 11:25:36 2002
+++ b/sound/drivers/opl3/opl3_seq.c	Mon Oct  7 11:25:36 2002
@@ -146,13 +146,13 @@
  * MIDI emulation operators
  */
 snd_midi_op_t opl3_ops = {
-	snd_opl3_note_on,
-	snd_opl3_note_off,
-	snd_opl3_key_press,
-	snd_opl3_terminate_note,
-	snd_opl3_control,
-	snd_opl3_nrpn,
-	snd_opl3_sysex,
+	.note_on =		snd_opl3_note_on,
+	.note_off =		snd_opl3_note_off,
+	.key_press =		snd_opl3_key_press,
+	.note_terminate =	snd_opl3_terminate_note,
+	.control =		snd_opl3_control,
+	.nrpn =			snd_opl3_nrpn,
+	.sysex =		snd_opl3_sysex,
 };
 
 static int snd_opl3_synth_event_input(snd_seq_event_t * ev, int direct,
diff -Nru a/sound/drivers/opl3/opl3_synth.c b/sound/drivers/opl3/opl3_synth.c
--- a/sound/drivers/opl3/opl3_synth.c	Mon Oct  7 11:25:26 2002
+++ b/sound/drivers/opl3/opl3_synth.c	Mon Oct  7 11:25:26 2002
@@ -19,7 +19,6 @@
  *
  */
 
-#define __NO_VERSION__
 #include <sound/opl3.h>
 #define __SND_OSS_COMPAT__
 #include <sound/asound_fm.h>
diff -Nru a/sound/drivers/serial-u16550.c b/sound/drivers/serial-u16550.c
--- a/sound/drivers/serial-u16550.c	Mon Oct  7 11:25:25 2002
+++ b/sound/drivers/serial-u16550.c	Mon Oct  7 11:25:25 2002
@@ -713,16 +713,16 @@
 
 static snd_rawmidi_ops_t snd_uart16550_output =
 {
-	open:		snd_uart16550_output_open,
-	close:		snd_uart16550_output_close,
-	trigger:	snd_uart16550_output_trigger,
+	.open =		snd_uart16550_output_open,
+	.close =	snd_uart16550_output_close,
+	.trigger =	snd_uart16550_output_trigger,
 };
 
 static snd_rawmidi_ops_t snd_uart16550_input =
 {
-	open:		snd_uart16550_input_open,
-	close:		snd_uart16550_input_close,
-	trigger:	snd_uart16550_input_trigger,
+	.open =		snd_uart16550_input_open,
+	.close =	snd_uart16550_input_close,
+	.trigger =	snd_uart16550_input_trigger,
 };
 
 static int snd_uart16550_free(snd_uart16550_t *uart)
@@ -752,7 +752,7 @@
 				       snd_uart16550_t **ruart)
 {
 	static snd_device_ops_t ops = {
-		dev_free:       snd_uart16550_dev_free,
+		.dev_free =	snd_uart16550_dev_free,
 	};
 	snd_uart16550_t *uart;
 	int err;
diff -Nru a/sound/i2c/cs8427.c b/sound/i2c/cs8427.c
--- a/sound/i2c/cs8427.c	Mon Oct  7 11:25:25 2002
+++ b/sound/i2c/cs8427.c	Mon Oct  7 11:25:25 2002
@@ -82,7 +82,7 @@
 	buf[1] = val;
 	if ((err = snd_i2c_sendbytes(device, buf, 2)) != 2) {
 		snd_printk("unable to send bytes 0x%02x:0x%02x to CS8427 (%i)\n", buf[0], buf[1], err);
-		return err < 0 ? err : -EREMOTE;
+		return err < 0 ? err : -EIO;
 	}
 	return 0;
 }
@@ -94,11 +94,11 @@
 
 	if ((err = snd_i2c_sendbytes(device, &reg, 1)) != 1) {
 		snd_printk("unable to send register 0x%x byte to CS8427\n", reg);
-		return err < 0 ? err : -EREMOTE;
+		return err < 0 ? err : -EIO;
 	}
 	if ((err = snd_i2c_readbytes(device, &buf, 1)) != 1) {
 		snd_printk("unable to read register 0x%x byte from CS8427\n", reg);
-		return err < 0 ? err : -EREMOTE;
+		return err < 0 ? err : -EIO;
 	}
 	return buf;
 }
@@ -148,7 +148,7 @@
 	for (idx = 0; idx < count; idx++)
 		data[idx + 1] = swapbits(ndata[idx]);
 	if (snd_i2c_sendbytes(device, data, count) != count)
-		return -EREMOTE;
+		return -EIO;
 	return 1;
 }
 
@@ -226,7 +226,7 @@
 	/* send initial values */
 	memcpy(chip->regmap + (initvals1[0] & 0x7f), initvals1 + 1, 6);
 	if ((err = snd_i2c_sendbytes(device, initvals1, 7)) != 7) {
-		err = err < 0 ? err : -EREMOTE;
+		err = err < 0 ? err : -EIO;
 		goto __fail;
 	}
 	/* Turn off CS8427 interrupt stuff that is not used in hardware */
@@ -238,7 +238,7 @@
 	/* send transfer initialization sequence */
 	memcpy(chip->regmap + (initvals2[0] & 0x7f), initvals2 + 1, 3);
 	if ((err = snd_i2c_sendbytes(device, initvals2, 4)) != 4) {
-		err = err < 0 ? err : -EREMOTE;
+		err = err < 0 ? err : -EIO;
 		goto __fail;
 	}
 	/* write default channel status bytes */
@@ -274,7 +274,7 @@
       __fail:
       	snd_i2c_unlock(bus);
       	snd_i2c_device_free(device);
-      	return err < 0 ? err : -EREMOTE;
+      	return err < 0 ? err : -EIO;
 }
 
 static int snd_cs8427_in_status_info(snd_kcontrol_t *kcontrol,
@@ -359,35 +359,35 @@
 
 static snd_kcontrol_new_t snd_cs8427_iec958_controls[] = {
 {
-	iface: SNDRV_CTL_ELEM_IFACE_PCM,
-	info: snd_cs8427_in_status_info,
-	name: "IEC958 CS8427 Input Status",
-	access: SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
-	get: snd_cs8427_in_status_get,
+	.iface =	SNDRV_CTL_ELEM_IFACE_PCM,
+	.info =		snd_cs8427_in_status_info,
+	.name =		"IEC958 CS8427 Input Status",
+	.access =	SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+	.get =		snd_cs8427_in_status_get,
 },
 {
-	access:		SNDRV_CTL_ELEM_ACCESS_READ,
-	iface:		SNDRV_CTL_ELEM_IFACE_PCM,
-	name:           SNDRV_CTL_NAME_IEC958("",PLAYBACK,MASK),
-	info:		snd_cs8427_spdif_mask_info,
-	get:		snd_cs8427_spdif_mask_get,
+	.access =	SNDRV_CTL_ELEM_ACCESS_READ,
+	.iface =	SNDRV_CTL_ELEM_IFACE_PCM,
+	.name =		SNDRV_CTL_NAME_IEC958("",PLAYBACK,MASK),
+	.info =		snd_cs8427_spdif_mask_info,
+	.get =		snd_cs8427_spdif_mask_get,
 },
 {
-	iface:		SNDRV_CTL_ELEM_IFACE_PCM,
-	name:           SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
-	info:		snd_cs8427_spdif_info,
-	get:		snd_cs8427_spdif_get,
-	put:		snd_cs8427_spdif_put,
-	private_value:	0
+	.iface =	SNDRV_CTL_ELEM_IFACE_PCM,
+	.name =		SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
+	.info =		snd_cs8427_spdif_info,
+	.get =		snd_cs8427_spdif_get,
+	.put =		snd_cs8427_spdif_put,
+	.private_value = 0
 },
 {
-	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_cs8427_spdif_info,
-	get:		snd_cs8427_spdif_get,
-	put:		snd_cs8427_spdif_put,
-	private_value:	1
+	.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_cs8427_spdif_info,
+	.get =		snd_cs8427_spdif_get,
+	.put =		snd_cs8427_spdif_put,
+	.private_value = 1
 }};
 
 int snd_cs8427_iec958_build(snd_i2c_device_t *cs8427,
diff -Nru a/sound/i2c/i2c.c b/sound/i2c/i2c.c
--- a/sound/i2c/i2c.c	Mon Oct  7 11:25:34 2002
+++ b/sound/i2c/i2c.c	Mon Oct  7 11:25:34 2002
@@ -37,9 +37,9 @@
 static int snd_i2c_bit_probeaddr(snd_i2c_bus_t *bus, unsigned short addr);
 
 static snd_i2c_ops_t snd_i2c_bit_ops = {
-	sendbytes: snd_i2c_bit_sendbytes,
-	readbytes: snd_i2c_bit_readbytes,
-	probeaddr: snd_i2c_bit_probeaddr,
+	.sendbytes = snd_i2c_bit_sendbytes,
+	.readbytes = snd_i2c_bit_readbytes,
+	.probeaddr = snd_i2c_bit_probeaddr,
 };
 
 static int snd_i2c_bus_free(snd_i2c_bus_t *bus)
@@ -77,7 +77,7 @@
 	snd_i2c_bus_t *bus;
 	int err;
 	static snd_device_ops_t ops = {
-		dev_free:       snd_i2c_bus_dev_free,
+		.dev_free =	snd_i2c_bus_dev_free,
 	};
 
 	*ri2c = NULL;
@@ -220,7 +220,7 @@
 	ack = snd_i2c_bit_data(bus, 1);
 	snd_i2c_bit_direction(bus, 1, 1);	/* SCL - wr, SDA - wr */
 	snd_i2c_bit_set(bus, 0, 1);
-	return ack ? -EREMOTEIO : 0;
+	return ack ? -EIO : 0;
 }
 
 static int snd_i2c_bit_sendbyte(snd_i2c_bus_t *bus, unsigned char data)
@@ -260,11 +260,15 @@
 	if (device->flags & SND_I2C_DEVICE_ADDRTEN)
 		return -EIO;		/* not yet implemented */
 	snd_i2c_bit_start(bus);
-	if ((err = snd_i2c_bit_sendbyte(bus, device->addr << 1)) < 0)
+	if ((err = snd_i2c_bit_sendbyte(bus, device->addr << 1)) < 0) {
+		snd_i2c_bit_hw_stop(bus);
 		return err;
+	}
 	while (count-- > 0) {
-		if ((err = snd_i2c_bit_sendbyte(bus, *bytes++)) < 0)
+		if ((err = snd_i2c_bit_sendbyte(bus, *bytes++)) < 0) {
+			snd_i2c_bit_hw_stop(bus);
 			return err;
+		}
 		res++;
 	}
 	snd_i2c_bit_stop(bus);
@@ -279,11 +283,15 @@
 	if (device->flags & SND_I2C_DEVICE_ADDRTEN)
 		return -EIO;		/* not yet implemented */
 	snd_i2c_bit_start(bus);
-	if ((err = snd_i2c_bit_sendbyte(bus, (device->addr << 1) | 1)) < 0)
+	if ((err = snd_i2c_bit_sendbyte(bus, (device->addr << 1) | 1)) < 0) {
+		snd_i2c_bit_hw_stop(bus);
 		return err;
+	}
 	while (count-- > 0) {
-		if ((err = snd_i2c_bit_readbyte(bus, count == 0)) < 0)
+		if ((err = snd_i2c_bit_readbyte(bus, count == 0)) < 0) {
+			snd_i2c_bit_hw_stop(bus);
 			return err;
+		}
 		*bytes++ = (unsigned char)err;
 		res++;
 	}
@@ -300,10 +308,9 @@
 	if (addr & 0x7f80)	/* invalid address */
 		return -EINVAL;
 	snd_i2c_bit_start(bus);
-	if ((err = snd_i2c_bit_sendbyte(bus, addr << 1)) < 0)
-		return err;
+	err = snd_i2c_bit_sendbyte(bus, addr << 1);
 	snd_i2c_bit_stop(bus);
-	return 1;		/* present */
+	return err;
 }
 
 EXPORT_SYMBOL(snd_i2c_bus_create);
diff -Nru a/sound/i2c/l3/uda1341.c b/sound/i2c/l3/uda1341.c
--- a/sound/i2c/l3/uda1341.c	Mon Oct  7 11:25:35 2002
+++ b/sound/i2c/l3/uda1341.c	Mon Oct  7 11:25:35 2002
@@ -16,7 +16,7 @@
  * 2002-04-12	Tomas Kasparek	Proc interface update, code cleanup
  */
 
-/* $Id: uda1341.c,v 1.2 2002/04/18 07:47:59 perex Exp $ */
+/* $Id: uda1341.c,v 1.4 2002/08/15 12:13:06 perex Exp $ */
 
 #include <sound/driver.h>
 #include <linux/module.h>
@@ -459,9 +459,9 @@
 /* {{{ 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) \
+{ .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)
@@ -521,9 +521,9 @@
 /* {{{ 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) \
+{ .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)
@@ -583,9 +583,9 @@
 /* {{{ 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) | \
+{ .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) \
 }
 
@@ -839,17 +839,17 @@
 /* {{{ Module and L3 initialization */
 
 static struct l3_ops uda1341_ops = {
-	open:		uda1341_open,
-	command:	uda1341_command,
-	close:		uda1341_close,
+	.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,
+	.name =		UDA1341_ALSA_NAME,
+	.attach_client = uda1341_attach,
+	.detach_client = uda1341_detach,
+	.ops =		&uda1341_ops,
+	.owner =	THIS_MODULE,
 };
 
 static int __init uda1341_init(void)
diff -Nru a/sound/i2c/tea6330t.c b/sound/i2c/tea6330t.c
--- a/sound/i2c/tea6330t.c	Mon Oct  7 11:25:35 2002
+++ b/sound/i2c/tea6330t.c	Mon Oct  7 11:25:35 2002
@@ -67,9 +67,9 @@
 #endif
 
 #define TEA6330T_MASTER_VOLUME(xname, xindex) \
-{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \
-  info: snd_tea6330t_info_master_volume, \
-  get: snd_tea6330t_get_master_volume, put: snd_tea6330t_put_master_volume }
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
+  .info = snd_tea6330t_info_master_volume, \
+  .get = snd_tea6330t_get_master_volume, .put = snd_tea6330t_put_master_volume }
 
 static int snd_tea6330t_info_master_volume(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
 {
@@ -123,9 +123,9 @@
 }
 
 #define TEA6330T_MASTER_SWITCH(xname, xindex) \
-{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \
-  info: snd_tea6330t_info_master_switch, \
-  get: snd_tea6330t_get_master_switch, put: snd_tea6330t_put_master_switch }
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
+  .info = snd_tea6330t_info_master_switch, \
+  .get = snd_tea6330t_get_master_switch, .put = snd_tea6330t_put_master_switch }
 
 static int snd_tea6330t_info_master_switch(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
 {
@@ -172,9 +172,9 @@
 }
 
 #define TEA6330T_BASS(xname, xindex) \
-{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \
-  info: snd_tea6330t_info_bass, \
-  get: snd_tea6330t_get_bass, put: snd_tea6330t_put_bass }
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
+  .info = snd_tea6330t_info_bass, \
+  .get = snd_tea6330t_get_bass, .put = snd_tea6330t_put_bass }
 
 static int snd_tea6330t_info_bass(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
 {
@@ -216,9 +216,9 @@
 }
 
 #define TEA6330T_TREBLE(xname, xindex) \
-{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \
-  info: snd_tea6330t_info_treble, \
-  get: snd_tea6330t_get_treble, put: snd_tea6330t_put_treble }
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
+  .info = snd_tea6330t_info_treble, \
+  .get = snd_tea6330t_get_treble, .put = snd_tea6330t_put_treble }
 
 static int snd_tea6330t_info_treble(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
 {
diff -Nru a/sound/isa/ad1816a/ad1816a_lib.c b/sound/isa/ad1816a/ad1816a_lib.c
--- a/sound/isa/ad1816a/ad1816a_lib.c	Mon Oct  7 11:25:36 2002
+++ b/sound/isa/ad1816a/ad1816a_lib.c	Mon Oct  7 11:25:36 2002
@@ -334,41 +334,41 @@
 
 
 static snd_pcm_hardware_t snd_ad1816a_playback = {
-	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
 				 SNDRV_PCM_INFO_MMAP_VALID),
-	formats:		(SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW |
+	.formats =		(SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW |
 				 SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE |
 				 SNDRV_PCM_FMTBIT_S16_BE),
-	rates:			SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
-	rate_min:		4000,
-	rate_max:		55200,
-	channels_min:		1,
-	channels_max:		2,
-	buffer_bytes_max:	(128*1024),
-	period_bytes_min:	64,
-	period_bytes_max:	(128*1024),
-	periods_min:		1,
-	periods_max:		1024,
-	fifo_size:		0,
+	.rates =		SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+	.rate_min =		4000,
+	.rate_max =		55200,
+	.channels_min =		1,
+	.channels_max =		2,
+	.buffer_bytes_max =	(128*1024),
+	.period_bytes_min =	64,
+	.period_bytes_max =	(128*1024),
+	.periods_min =		1,
+	.periods_max =		1024,
+	.fifo_size =		0,
 };
 
 static snd_pcm_hardware_t snd_ad1816a_capture = {
-	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
 				 SNDRV_PCM_INFO_MMAP_VALID),
-	formats:		(SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW |
+	.formats =		(SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW |
 				 SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE |
 				 SNDRV_PCM_FMTBIT_S16_BE),
-	rates:			SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
-	rate_min:		4000,
-	rate_max:		55200,
-	channels_min:		1,
-	channels_max:		2,
-	buffer_bytes_max:	(128*1024),
-	period_bytes_min:	64,
-	period_bytes_max:	(128*1024),
-	periods_min:		1,
-	periods_max:		1024,
-	fifo_size:		0,
+	.rates =		SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+	.rate_min =		4000,
+	.rate_max =		55200,
+	.channels_min =		1,
+	.channels_max =		2,
+	.buffer_bytes_max =	(128*1024),
+	.period_bytes_min =	64,
+	.period_bytes_max =	(128*1024),
+	.periods_min =		1,
+	.periods_max =		1024,
+	.fifo_size =		0,
 };
 
 static int snd_ad1816a_timer_close(snd_timer_t *timer)
@@ -425,14 +425,14 @@
 }
 
 static struct _snd_timer_hardware snd_ad1816a_timer_table = {
-	flags:		SNDRV_TIMER_HW_AUTO,
-	resolution:	10000,
-	ticks:		65535,
-	open:		snd_ad1816a_timer_open,
-	close:		snd_ad1816a_timer_close,
-	c_resolution:	snd_ad1816a_timer_resolution,
-	start:		snd_ad1816a_timer_start,
-	stop:		snd_ad1816a_timer_stop,
+	.flags =	SNDRV_TIMER_HW_AUTO,
+	.resolution =	10000,
+	.ticks =	65535,
+	.open =		snd_ad1816a_timer_open,
+	.close =	snd_ad1816a_timer_close,
+	.c_resolution =	snd_ad1816a_timer_resolution,
+	.start =	snd_ad1816a_timer_start,
+	.stop =		snd_ad1816a_timer_stop,
 };
 
 
@@ -575,7 +575,7 @@
 		       ad1816a_t **rchip)
 {
         static snd_device_ops_t ops = {
-		dev_free:       snd_ad1816a_dev_free,
+		.dev_free =	snd_ad1816a_dev_free,
 	};
 	int error;
 	ad1816a_t *chip;
@@ -631,25 +631,25 @@
 }
 
 static snd_pcm_ops_t snd_ad1816a_playback_ops = {
-	open:		snd_ad1816a_playback_open,
-	close:		snd_ad1816a_playback_close,
-	ioctl:		snd_pcm_lib_ioctl,
-	hw_params:	snd_ad1816a_hw_params,
-	hw_free:	snd_ad1816a_hw_free,
-	prepare:	snd_ad1816a_playback_prepare,
-	trigger:	snd_ad1816a_playback_trigger,
-	pointer:	snd_ad1816a_playback_pointer,
+	.open =		snd_ad1816a_playback_open,
+	.close =	snd_ad1816a_playback_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	snd_ad1816a_hw_params,
+	.hw_free =	snd_ad1816a_hw_free,
+	.prepare =	snd_ad1816a_playback_prepare,
+	.trigger =	snd_ad1816a_playback_trigger,
+	.pointer =	snd_ad1816a_playback_pointer,
 };
 
 static snd_pcm_ops_t snd_ad1816a_capture_ops = {
-	open:		snd_ad1816a_capture_open,
-	close:		snd_ad1816a_capture_close,
-	ioctl:		snd_pcm_lib_ioctl,
-	hw_params:	snd_ad1816a_hw_params,
-	hw_free:	snd_ad1816a_hw_free,
-	prepare:	snd_ad1816a_capture_prepare,
-	trigger:	snd_ad1816a_capture_trigger,
-	pointer:	snd_ad1816a_capture_pointer,
+	.open =		snd_ad1816a_capture_open,
+	.close =	snd_ad1816a_capture_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	snd_ad1816a_hw_params,
+	.hw_free =	snd_ad1816a_hw_free,
+	.prepare =	snd_ad1816a_capture_prepare,
+	.trigger =	snd_ad1816a_capture_trigger,
+	.pointer =	snd_ad1816a_capture_pointer,
 };
 
 static void snd_ad1816a_pcm_free(snd_pcm_t *pcm)
@@ -768,9 +768,9 @@
 }
 
 #define AD1816A_SINGLE(xname, reg, shift, mask, invert) \
-{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, info: snd_ad1816a_info_single, \
-  get: snd_ad1816a_get_single, put: snd_ad1816a_put_single, \
-  private_value: reg | (shift << 8) | (mask << 16) | (invert << 24) }
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .info = snd_ad1816a_info_single, \
+  .get = snd_ad1816a_get_single, .put = snd_ad1816a_put_single, \
+  .private_value = reg | (shift << 8) | (mask << 16) | (invert << 24) }
 
 static int snd_ad1816a_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
 {
@@ -825,9 +825,9 @@
 }
 
 #define AD1816A_DOUBLE(xname, reg, shift_left, shift_right, mask, invert) \
-{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, info: snd_ad1816a_info_double, \
-  get: snd_ad1816a_get_double, put: snd_ad1816a_put_double, \
-  private_value: reg | (shift_left << 8) | (shift_right << 12) | (mask << 16) | (invert << 24) }
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .info = snd_ad1816a_info_double, \
+  .get = snd_ad1816a_get_double, .put = snd_ad1816a_put_double, \
+  .private_value = reg | (shift_left << 8) | (shift_right << 12) | (mask << 16) | (invert << 24) }
 
 static int snd_ad1816a_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
 {
@@ -917,11 +917,11 @@
 AD1816A_SINGLE("Phone Playback Switch", AD1816A_PHONE_OUT_ATT, 7, 1, 1),
 AD1816A_SINGLE("Phone Playback Volume", AD1816A_PHONE_OUT_ATT, 0, 31, 1),
 {
-	iface: SNDRV_CTL_ELEM_IFACE_MIXER,
-	name: "Capture Source",
-	info: snd_ad1816a_info_mux,
-	get: snd_ad1816a_get_mux,
-	put: snd_ad1816a_put_mux,
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "Capture Source",
+	.info = snd_ad1816a_info_mux,
+	.get = snd_ad1816a_get_mux,
+	.put = snd_ad1816a_put_mux,
 },
 AD1816A_DOUBLE("Capture Switch", AD1816A_ADC_PGA, 15, 7, 1, 1),
 AD1816A_DOUBLE("Capture Volume", AD1816A_ADC_PGA, 8, 0, 15, 0),
@@ -962,4 +962,3 @@
 
 module_init(alsa_ad1816a_init)
 module_exit(alsa_ad1816a_exit)
-
diff -Nru a/sound/isa/ad1848/ad1848_lib.c b/sound/isa/ad1848/ad1848_lib.c
--- a/sound/isa/ad1848/ad1848_lib.c	Mon Oct  7 11:25:33 2002
+++ b/sound/isa/ad1848/ad1848_lib.c	Mon Oct  7 11:25:33 2002
@@ -27,7 +27,6 @@
 #include <linux/init.h>
 #include <linux/slab.h>
 #include <linux/ioport.h>
-#include <linux/init.h>
 #include <sound/core.h>
 #include <sound/ad1848.h>
 #include <sound/pcm_params.h>
@@ -69,9 +68,9 @@
 };
 
 static snd_pcm_hw_constraint_list_t hw_constraints_rates = {
-	count: 14,
-	list: rates,
-	mask: 0,
+	.count = 14,
+	.list = rates,
+	.mask = 0,
 };
 
 static unsigned char snd_ad1848_original_image[16] =
@@ -702,40 +701,40 @@
 
 static snd_pcm_hardware_t snd_ad1848_playback =
 {
-	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
 				 SNDRV_PCM_INFO_MMAP_VALID),
-	formats:		(SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW |
+	.formats =		(SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW |
 				 SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE),
-	rates:			SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000,
-	rate_min:		5510,
-	rate_max:		48000,
-	channels_min:		1,
-	channels_max:		2,
-	buffer_bytes_max:	(128*1024),
-	period_bytes_min:	64,
-	period_bytes_max:	(128*1024),
-	periods_min:		1,
-	periods_max:		1024,
-	fifo_size:		0,
+	.rates =		SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000,
+	.rate_min =		5510,
+	.rate_max =		48000,
+	.channels_min =		1,
+	.channels_max =		2,
+	.buffer_bytes_max =	(128*1024),
+	.period_bytes_min =	64,
+	.period_bytes_max =	(128*1024),
+	.periods_min =		1,
+	.periods_max =		1024,
+	.fifo_size =		0,
 };
 
 static snd_pcm_hardware_t snd_ad1848_capture =
 {
-	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
 				 SNDRV_PCM_INFO_MMAP_VALID),
-	formats:		(SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW |
+	.formats =		(SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW |
 				 SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE),
-	rates:			SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000,
-	rate_min:		5510,
-	rate_max:		48000,
-	channels_min:		1,
-	channels_max:		2,
-	buffer_bytes_max:	(128*1024),
-	period_bytes_min:	64,
-	period_bytes_max:	(128*1024),
-	periods_min:		1,
-	periods_max:		1024,
-	fifo_size:		0,
+	.rates =		SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000,
+	.rate_min =		5510,
+	.rate_max =		48000,
+	.channels_min =		1,
+	.channels_max =		2,
+	.buffer_bytes_max =	(128*1024),
+	.period_bytes_min =	64,
+	.period_bytes_max =	(128*1024),
+	.periods_min =		1,
+	.periods_max =		1024,
+	.fifo_size =		0,
 };
 
 /*
@@ -834,7 +833,7 @@
 		      ad1848_t ** rchip)
 {
 	static snd_device_ops_t ops = {
-		dev_free:       snd_ad1848_dev_free,
+		.dev_free =	snd_ad1848_dev_free,
 	};
 	ad1848_t *chip;
 	int err;
@@ -883,25 +882,25 @@
 }
 
 static snd_pcm_ops_t snd_ad1848_playback_ops = {
-	open:		snd_ad1848_playback_open,
-	close:		snd_ad1848_playback_close,
-	ioctl:		snd_ad1848_ioctl,
-	hw_params:	snd_ad1848_playback_hw_params,
-	hw_free:	snd_ad1848_playback_hw_free,
-	prepare:	snd_ad1848_playback_prepare,
-	trigger:	snd_ad1848_playback_trigger,
-	pointer:	snd_ad1848_playback_pointer,
+	.open =		snd_ad1848_playback_open,
+	.close =	snd_ad1848_playback_close,
+	.ioctl =	snd_ad1848_ioctl,
+	.hw_params =	snd_ad1848_playback_hw_params,
+	.hw_free =	snd_ad1848_playback_hw_free,
+	.prepare =	snd_ad1848_playback_prepare,
+	.trigger =	snd_ad1848_playback_trigger,
+	.pointer =	snd_ad1848_playback_pointer,
 };
 
 static snd_pcm_ops_t snd_ad1848_capture_ops = {
-	open:		snd_ad1848_capture_open,
-	close:		snd_ad1848_capture_close,
-	ioctl:		snd_ad1848_ioctl,
-	hw_params:	snd_ad1848_capture_hw_params,
-	hw_free:	snd_ad1848_capture_hw_free,
-	prepare:	snd_ad1848_capture_prepare,
-	trigger:	snd_ad1848_capture_trigger,
-	pointer:	snd_ad1848_capture_pointer,
+	.open =		snd_ad1848_capture_open,
+	.close =	snd_ad1848_capture_close,
+	.ioctl =	snd_ad1848_ioctl,
+	.hw_params =	snd_ad1848_capture_hw_params,
+	.hw_free =	snd_ad1848_capture_hw_free,
+	.prepare =	snd_ad1848_capture_prepare,
+	.trigger =	snd_ad1848_capture_trigger,
+	.pointer =	snd_ad1848_capture_pointer,
 };
 
 static void snd_ad1848_pcm_free(snd_pcm_t *pcm)
@@ -1121,11 +1120,11 @@
 AD1848_DOUBLE("Aux Playback Volume", 1, AD1848_AUX2_LEFT_INPUT, AD1848_AUX2_RIGHT_INPUT, 0, 0, 31, 1),
 AD1848_DOUBLE("Capture Volume", 0, AD1848_LEFT_INPUT, AD1848_RIGHT_INPUT, 0, 0, 15, 0),
 {
-	iface: SNDRV_CTL_ELEM_IFACE_MIXER,
-	name: "Capture Source",
-	info: snd_ad1848_info_mux,
-	get: snd_ad1848_get_mux,
-	put: snd_ad1848_put_mux,
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "Capture Source",
+	.info = snd_ad1848_info_mux,
+	.get = snd_ad1848_get_mux,
+	.put = snd_ad1848_put_mux,
 },
 AD1848_SINGLE("Loopback Capture Switch", 0, AD1848_LOOPBACK, 0, 1, 0),
 AD1848_SINGLE("Loopback Capture Volume", 0, AD1848_LOOPBACK, 1, 63, 0)
diff -Nru a/sound/isa/cmi8330.c b/sound/isa/cmi8330.c
--- a/sound/isa/cmi8330.c	Mon Oct  7 11:25:26 2002
+++ b/sound/isa/cmi8330.c	Mon Oct  7 11:25:26 2002
@@ -46,6 +46,11 @@
 #include <sound/driver.h>
 #include <linux/init.h>
 #include <linux/slab.h>
+#ifndef LINUX_ISAPNP_H
+#include <linux/isapnp.h>
+#define isapnp_card pci_bus
+#define isapnp_dev pci_dev
+#endif
 #include <sound/core.h>
 #include <sound/ad1848.h>
 #include <sound/sb.h>
diff -Nru a/sound/isa/cs423x/cs4231_lib.c b/sound/isa/cs423x/cs4231_lib.c
--- a/sound/isa/cs423x/cs4231_lib.c	Mon Oct  7 11:25:26 2002
+++ b/sound/isa/cs423x/cs4231_lib.c	Mon Oct  7 11:25:26 2002
@@ -74,9 +74,9 @@
 };
 
 static snd_pcm_hw_constraint_list_t hw_constraints_rates = {
-	count: 14,
-	list: rates,
-	mask: 0,
+	.count = 14,
+	.list = rates,
+	.mask = 0,
 };
 
 static int snd_cs4231_xrate(snd_pcm_runtime_t *runtime)
@@ -795,14 +795,14 @@
 
 static struct _snd_timer_hardware snd_cs4231_timer_table =
 {
-	flags:		SNDRV_TIMER_HW_AUTO,
-	resolution:	9945,
-	ticks:		65535,
-	open:		snd_cs4231_timer_open,
-	close:		snd_cs4231_timer_close,
-	c_resolution:	snd_cs4231_timer_resolution,
-	start:		snd_cs4231_timer_start,
-	stop:		snd_cs4231_timer_stop,
+	.flags =	SNDRV_TIMER_HW_AUTO,
+	.resolution =	9945,
+	.ticks =	65535,
+	.open =		snd_cs4231_timer_open,
+	.close =	snd_cs4231_timer_close,
+	.c_resolution = snd_cs4231_timer_resolution,
+	.start =	snd_cs4231_timer_start,
+	.stop =		snd_cs4231_timer_stop,
 };
 
 /*
@@ -998,7 +998,12 @@
 		rev = snd_cs4231_in(chip, CS4231_VERSION) & 0xe7;
 		snd_printdd("CS4231: VERSION (I25) = 0x%x\n", rev);
 		if (rev == 0x80) {
-			chip->hardware = CS4231_HW_CS4231;
+			unsigned char tmp = snd_cs4231_in(chip, 23);
+			snd_cs4231_out(chip, 23, ~tmp);
+			if (snd_cs4231_in(chip, 23) != tmp)
+				chip->hardware = CS4231_HW_AD1845;
+			else
+				chip->hardware = CS4231_HW_CS4231;
 		} else if (rev == 0xa0) {
 			chip->hardware = CS4231_HW_CS4231A;
 		} else if (rev == 0xa2) {
@@ -1127,40 +1132,40 @@
 
 static snd_pcm_hardware_t snd_cs4231_playback =
 {
-	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
 				 SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START),
-	formats:		(SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW | SNDRV_PCM_FMTBIT_IMA_ADPCM |
+	.formats =		(SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW | SNDRV_PCM_FMTBIT_IMA_ADPCM |
 				 SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE),
-	rates:			SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000,
-	rate_min:		5510,
-	rate_max:		48000,
-	channels_min:		1,
-	channels_max:		2,
-	buffer_bytes_max:	(128*1024),
-	period_bytes_min:	64,
-	period_bytes_max:	(128*1024),
-	periods_min:		1,
-	periods_max:		1024,
-	fifo_size:		0,
+	.rates =		SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000,
+	.rate_min =		5510,
+	.rate_max =		48000,
+	.channels_min =		1,
+	.channels_max =		2,
+	.buffer_bytes_max =	(128*1024),
+	.period_bytes_min =	64,
+	.period_bytes_max =	(128*1024),
+	.periods_min =		1,
+	.periods_max =		1024,
+	.fifo_size =		0,
 };
 
 static snd_pcm_hardware_t snd_cs4231_capture =
 {
-	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
 				 SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START),
-	formats:		(SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW | SNDRV_PCM_FMTBIT_IMA_ADPCM |
+	.formats =		(SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW | SNDRV_PCM_FMTBIT_IMA_ADPCM |
 				 SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE),
-	rates:			SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000,
-	rate_min:		5510,
-	rate_max:		48000,
-	channels_min:		1,
-	channels_max:		2,
-	buffer_bytes_max:	(128*1024),
-	period_bytes_min:	64,
-	period_bytes_max:	(128*1024),
-	periods_min:		1,
-	periods_max:		1024,
-	fifo_size:		0,
+	.rates =		SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000,
+	.rate_min =		5510,
+	.rate_max =		48000,
+	.channels_min =		1,
+	.channels_max =		2,
+	.buffer_bytes_max =	(128*1024),
+	.period_bytes_min =	64,
+	.period_bytes_max =	(128*1024),
+	.periods_min =		1,
+	.periods_max =		1024,
+	.fifo_size =		0,
 };
 
 /*
@@ -1382,6 +1387,7 @@
 	case CS4231_HW_CS4239:	return "CS4239";
 	case CS4231_HW_INTERWAVE: return "AMD InterWave";
 	case CS4231_HW_OPL3SA2: return chip->card->shortname;
+	case CS4231_HW_AD1845: return "AD1845";
 	default: return "???";
 	}
 }
@@ -1395,8 +1401,8 @@
 		      cs4231_t ** rchip)
 {
 	static snd_device_ops_t ops = {
-		dev_free:       snd_cs4231_dev_free,
-	};                                
+		.dev_free =	snd_cs4231_dev_free,
+	};
 	cs4231_t *chip;
 	int err;
 
@@ -1481,25 +1487,25 @@
 }
 
 static snd_pcm_ops_t snd_cs4231_playback_ops = {
-	open:		snd_cs4231_playback_open,
-	close:		snd_cs4231_playback_close,
-	ioctl:		snd_pcm_lib_ioctl,
-	hw_params:	snd_cs4231_playback_hw_params,
-	hw_free:	snd_cs4231_playback_hw_free,
-	prepare:	snd_cs4231_playback_prepare,
-	trigger:	snd_cs4231_trigger,
-	pointer:	snd_cs4231_playback_pointer,
+	.open =		snd_cs4231_playback_open,
+	.close =	snd_cs4231_playback_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	snd_cs4231_playback_hw_params,
+	.hw_free =	snd_cs4231_playback_hw_free,
+	.prepare =	snd_cs4231_playback_prepare,
+	.trigger =	snd_cs4231_trigger,
+	.pointer =	snd_cs4231_playback_pointer,
 };
 
 static snd_pcm_ops_t snd_cs4231_capture_ops = {
-	open:		snd_cs4231_capture_open,
-	close:		snd_cs4231_capture_close,
-	ioctl:		snd_pcm_lib_ioctl,
-	hw_params:	snd_cs4231_capture_hw_params,
-	hw_free:	snd_cs4231_capture_hw_free,
-	prepare:	snd_cs4231_capture_prepare,
-	trigger:	snd_cs4231_trigger,
-	pointer:	snd_cs4231_capture_pointer,
+	.open =		snd_cs4231_capture_open,
+	.close =	snd_cs4231_capture_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	snd_cs4231_capture_hw_params,
+	.hw_free =	snd_cs4231_capture_hw_free,
+	.prepare =	snd_cs4231_capture_prepare,
+	.trigger =	snd_cs4231_trigger,
+	.pointer =	snd_cs4231_capture_pointer,
 };
 
 static void snd_cs4231_pcm_free(snd_pcm_t *pcm)
@@ -1773,11 +1779,11 @@
 CS4231_SINGLE("Mono Output Playback Bypass", 0, CS4231_MONO_CTRL, 5, 1, 0),
 CS4231_DOUBLE("Capture Volume", 0, CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT, 0, 0, 15, 0),
 {
-	iface: SNDRV_CTL_ELEM_IFACE_MIXER,
-	name: "Capture Source",
-	info: snd_cs4231_info_mux,
-	get: snd_cs4231_get_mux,
-	put: snd_cs4231_put_mux,
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "Capture Source",
+	.info = snd_cs4231_info_mux,
+	.get = snd_cs4231_get_mux,
+	.put = snd_cs4231_put_mux,
 },
 CS4231_DOUBLE("Mic Boost", 0, CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT, 5, 5, 1, 0),
 CS4231_SINGLE("Loopback Capture Switch", 0, CS4231_LOOPBACK, 0, 1, 0),
diff -Nru a/sound/isa/cs423x/cs4236_lib.c b/sound/isa/cs423x/cs4236_lib.c
--- a/sound/isa/cs423x/cs4236_lib.c	Mon Oct  7 11:25:29 2002
+++ b/sound/isa/cs423x/cs4236_lib.c	Mon Oct  7 11:25:29 2002
@@ -143,19 +143,19 @@
 #define CLOCKS 8
 
 static ratnum_t clocks[CLOCKS] = {
-	{ num: 16934400, den_min: 353, den_max: 353, den_step: 1 },
-	{ num: 16934400, den_min: 529, den_max: 529, den_step: 1 },
-	{ num: 16934400, den_min: 617, den_max: 617, den_step: 1 },
-	{ num: 16934400, den_min: 1058, den_max: 1058, den_step: 1 },
-	{ num: 16934400, den_min: 1764, den_max: 1764, den_step: 1 },
-	{ num: 16934400, den_min: 2117, den_max: 2117, den_step: 1 },
-	{ num: 16934400, den_min: 2558, den_max: 2558, den_step: 1 },
-	{ num: 16934400/16, den_min: 21, den_max: 192, den_step: 1 }
+	{ .num = 16934400, .den_min = 353, .den_max = 353, .den_step = 1 },
+	{ .num = 16934400, .den_min = 529, .den_max = 529, .den_step = 1 },
+	{ .num = 16934400, .den_min = 617, .den_max = 617, .den_step = 1 },
+	{ .num = 16934400, .den_min = 1058, .den_max = 1058, .den_step = 1 },
+	{ .num = 16934400, .den_min = 1764, .den_max = 1764, .den_step = 1 },
+	{ .num = 16934400, .den_min = 2117, .den_max = 2117, .den_step = 1 },
+	{ .num = 16934400, .den_min = 2558, .den_max = 2558, .den_step = 1 },
+	{ .num = 16934400/16, .den_min = 21, .den_max = 192, .den_step = 1 }
 };
 
 static snd_pcm_hw_constraint_ratnums_t hw_constraints_clocks = {
-	nrats: CLOCKS,
-	rats: clocks,
+	.nrats = CLOCKS,
+	.rats = clocks,
 };
 
 static int snd_cs4236_xrate(snd_pcm_runtime_t *runtime)
@@ -366,10 +366,10 @@
  */
 
 #define CS4236_SINGLE(xname, xindex, reg, shift, mask, invert) \
-{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \
-  info: snd_cs4236_info_single, \
-  get: snd_cs4236_get_single, put: snd_cs4236_put_single, \
-  private_value: reg | (shift << 8) | (mask << 16) | (invert << 24) }
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
+  .info = snd_cs4236_info_single, \
+  .get = snd_cs4236_get_single, .put = snd_cs4236_put_single, \
+  .private_value = reg | (shift << 8) | (mask << 16) | (invert << 24) }
 
 static int snd_cs4236_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
 {
@@ -423,10 +423,10 @@
 }
 
 #define CS4236_SINGLEC(xname, xindex, reg, shift, mask, invert) \
-{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \
-  info: snd_cs4236_info_single, \
-  get: snd_cs4236_get_singlec, put: snd_cs4236_put_singlec, \
-  private_value: reg | (shift << 8) | (mask << 16) | (invert << 24) }
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
+  .info = snd_cs4236_info_single, \
+  .get = snd_cs4236_get_singlec, .put = snd_cs4236_put_singlec, \
+  .private_value = reg | (shift << 8) | (mask << 16) | (invert << 24) }
 
 static int snd_cs4236_get_singlec(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
 {
@@ -469,10 +469,10 @@
 }
 
 #define CS4236_DOUBLE(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \
-{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \
-  info: snd_cs4236_info_double, \
-  get: snd_cs4236_get_double, put: snd_cs4236_put_double, \
-  private_value: left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) }
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
+  .info = snd_cs4236_info_double, \
+  .get = snd_cs4236_get_double, .put = snd_cs4236_put_double, \
+  .private_value = left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) }
 
 static int snd_cs4236_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
 {
@@ -545,10 +545,10 @@
 }
 
 #define CS4236_DOUBLE1(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \
-{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \
-  info: snd_cs4236_info_double, \
-  get: snd_cs4236_get_double1, put: snd_cs4236_put_double1, \
-  private_value: left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) }
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
+  .info = snd_cs4236_info_double, \
+  .get = snd_cs4236_get_double1, .put = snd_cs4236_put_double1, \
+  .private_value = left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) }
 
 static int snd_cs4236_get_double1(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
 {
@@ -604,10 +604,10 @@
 }
 
 #define CS4236_MASTER_DIGITAL(xname, xindex) \
-{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \
-  info: snd_cs4236_info_double, \
-  get: snd_cs4236_get_master_digital, put: snd_cs4236_put_master_digital, \
-  private_value: 71 << 24 }
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
+  .info = snd_cs4236_info_double, \
+  .get = snd_cs4236_get_master_digital, .put = snd_cs4236_put_master_digital, \
+  .private_value = 71 << 24 }
 
 static inline int snd_cs4236_mixer_master_digital_invert_volume(int vol)
 {
@@ -646,10 +646,10 @@
 }
 
 #define CS4235_OUTPUT_ACCU(xname, xindex) \
-{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \
-  info: snd_cs4236_info_double, \
-  get: snd_cs4235_get_output_accu, put: snd_cs4235_put_output_accu, \
-  private_value: 3 << 24 }
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
+  .info = snd_cs4236_info_double, \
+  .get = snd_cs4235_get_output_accu, .put = snd_cs4235_put_output_accu, \
+  .private_value = 3 << 24 }
 
 static inline int snd_cs4235_mixer_output_accu_get_volume(int vol)
 {
@@ -807,10 +807,10 @@
 };
 
 #define CS4236_IEC958_ENABLE(xname, xindex) \
-{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \
-  info: snd_cs4236_info_single, \
-  get: snd_cs4236_get_iec958_switch, put: snd_cs4236_put_iec958_switch, \
-  private_value: 1 << 16 }
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
+  .info = snd_cs4236_info_single, \
+  .get = snd_cs4236_get_iec958_switch, .put = snd_cs4236_put_iec958_switch, \
+  .private_value = 1 << 16 }
 
 static int snd_cs4236_get_iec958_switch(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
 {
diff -Nru a/sound/isa/dt019x.c b/sound/isa/dt019x.c
--- a/sound/isa/dt019x.c	Mon Oct  7 11:25:32 2002
+++ b/sound/isa/dt019x.c	Mon Oct  7 11:25:32 2002
@@ -104,7 +104,7 @@
 	/* DT197A30 */
 	{
 		ISAPNP_CARD_ID('R','W','B',0x1688),
-		devs: { ISAPNP_DEVICE_ID('@','@','@',0x0001),
+		.devs = { ISAPNP_DEVICE_ID('@','@','@',0x0001),
 			ISAPNP_DEVICE_ID('@','X','@',0x0001),
 			ISAPNP_DEVICE_ID('@','H','@',0x0001) }
 	},
diff -Nru a/sound/isa/es1688/es1688_lib.c b/sound/isa/es1688/es1688_lib.c
--- a/sound/isa/es1688/es1688_lib.c	Mon Oct  7 11:25:36 2002
+++ b/sound/isa/es1688/es1688_lib.c	Mon Oct  7 11:25:36 2002
@@ -286,22 +286,22 @@
 
 static ratnum_t clocks[2] = {
 	{
-		num: 795444,
-		den_min: 1,
-		den_max: 128,
-		den_step: 1,
+		.num = 795444,
+		.den_min = 1,
+		.den_max = 128,
+		.den_step = 1,
 	},
 	{
-		num: 397722,
-		den_min: 1,
-		den_max: 128,
-		den_step: 1,
+		.num = 397722,
+		.den_min = 1,
+		.den_max = 128,
+		.den_step = 1,
 	}
 };
 
 static snd_pcm_hw_constraint_ratnums_t hw_constraints_clocks  = {
-	nrats: 2,
-	rats: clocks,
+	.nrats = 2,
+	.rats = clocks,
 };
 
 static void snd_es1688_set_rate(es1688_t *chip, snd_pcm_substream_t *substream)
@@ -520,38 +520,38 @@
 
 static snd_pcm_hardware_t snd_es1688_playback =
 {
-	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
 				 SNDRV_PCM_INFO_MMAP_VALID),
-	formats:		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
-	rates:			SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
-	rate_min:		4000,
-	rate_max:		48000,
-	channels_min:		1,
-	channels_max:		2,
-	buffer_bytes_max:	65536,
-	period_bytes_min:	64,
-	period_bytes_max:	65536,
-	periods_min:		1,
-	periods_max:		1024,
-	fifo_size:		0,
+	.formats =		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
+	.rates =		SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+	.rate_min =		4000,
+	.rate_max =		48000,
+	.channels_min =		1,
+	.channels_max =		2,
+	.buffer_bytes_max =	65536,
+	.period_bytes_min =	64,
+	.period_bytes_max =	65536,
+	.periods_min =		1,
+	.periods_max =		1024,
+	.fifo_size =		0,
 };
 
 static snd_pcm_hardware_t snd_es1688_capture =
 {
-	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
 				 SNDRV_PCM_INFO_MMAP_VALID),
-	formats:		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
-	rates:			SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
-	rate_min:		4000,
-	rate_max:		48000,
-	channels_min:		1,
-	channels_max:		2,
-	buffer_bytes_max:	65536,
-	period_bytes_min:	64,
-	period_bytes_max:	65536,
-	periods_min:		1,
-	periods_max:		1024,
-	fifo_size:		0,
+	.formats =		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
+	.rates =		SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+	.rate_min =		4000,
+	.rate_max =		48000,
+	.channels_min =		1,
+	.channels_max =		2,
+	.buffer_bytes_max =	65536,
+	.period_bytes_min =	64,
+	.period_bytes_max =	65536,
+	.periods_min =		1,
+	.periods_max =		1024,
+	.fifo_size =		0,
 };
 
 /*
@@ -642,7 +642,7 @@
 		      es1688_t **rchip)
 {
 	static snd_device_ops_t ops = {
-		dev_free:	snd_es1688_dev_free,
+		.dev_free =	snd_es1688_dev_free,
 	};
                                 
 	es1688_t *chip;
@@ -701,25 +701,25 @@
 }
 
 static snd_pcm_ops_t snd_es1688_playback_ops = {
-	open:			snd_es1688_playback_open,
-	close:			snd_es1688_playback_close,
-	ioctl:			snd_es1688_ioctl,
-	hw_params:		snd_es1688_hw_params,
-	hw_free:		snd_es1688_hw_free,
-	prepare:		snd_es1688_playback_prepare,
-	trigger:		snd_es1688_playback_trigger,
-	pointer:		snd_es1688_playback_pointer,
+	.open =			snd_es1688_playback_open,
+	.close =		snd_es1688_playback_close,
+	.ioctl =		snd_es1688_ioctl,
+	.hw_params =		snd_es1688_hw_params,
+	.hw_free =		snd_es1688_hw_free,
+	.prepare =		snd_es1688_playback_prepare,
+	.trigger =		snd_es1688_playback_trigger,
+	.pointer =		snd_es1688_playback_pointer,
 };
 
 static snd_pcm_ops_t snd_es1688_capture_ops = {
-	open:			snd_es1688_capture_open,
-	close:			snd_es1688_capture_close,
-	ioctl:			snd_es1688_ioctl,
-	hw_params:		snd_es1688_hw_params,
-	hw_free:		snd_es1688_hw_free,
-	prepare:		snd_es1688_capture_prepare,
-	trigger:		snd_es1688_capture_trigger,
-	pointer:		snd_es1688_capture_pointer,
+	.open =			snd_es1688_capture_open,
+	.close =		snd_es1688_capture_close,
+	.ioctl =		snd_es1688_ioctl,
+	.hw_params =		snd_es1688_hw_params,
+	.hw_free =		snd_es1688_hw_free,
+	.prepare =		snd_es1688_capture_prepare,
+	.trigger =		snd_es1688_capture_trigger,
+	.pointer =		snd_es1688_capture_pointer,
 };
 
 static void snd_es1688_pcm_free(snd_pcm_t *pcm)
@@ -800,10 +800,10 @@
 }
 
 #define ES1688_SINGLE(xname, xindex, reg, shift, mask, invert) \
-{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \
-  info: snd_es1688_info_single, \
-  get: snd_es1688_get_single, put: snd_es1688_put_single, \
-  private_value: reg | (shift << 8) | (mask << 16) | (invert << 24) }
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
+  .info = snd_es1688_info_single, \
+  .get = snd_es1688_get_single, .put = snd_es1688_put_single, \
+  .private_value = reg | (shift << 8) | (mask << 16) | (invert << 24) }
 
 static int snd_es1688_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
 {
@@ -859,10 +859,10 @@
 }
 
 #define ES1688_DOUBLE(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \
-{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \
-  info: snd_es1688_info_double, \
-  get: snd_es1688_get_double, put: snd_es1688_put_double, \
-  private_value: left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) }
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
+  .info = snd_es1688_info_double, \
+  .get = snd_es1688_get_double, .put = snd_es1688_put_double, \
+  .private_value = left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) }
 
 static int snd_es1688_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
 {
@@ -986,11 +986,11 @@
 ES1688_DOUBLE("Capture Volume", 0, ES1688_RECLEV_DEV, ES1688_RECLEV_DEV, 4, 0, 15, 0),
 ES1688_SINGLE("Capture Switch", 0, ES1688_REC_DEV, 4, 1, 1),
 {
-	iface: SNDRV_CTL_ELEM_IFACE_MIXER,
-	name: "Capture Source",
-	info: snd_es1688_info_mux,
-	get: snd_es1688_get_mux,
-	put: snd_es1688_put_mux,
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "Capture Source",
+	.info = snd_es1688_info_mux,
+	.get = snd_es1688_get_mux,
+	.put = snd_es1688_put_mux,
 },
 };
 
diff -Nru a/sound/isa/es18xx.c b/sound/isa/es18xx.c
--- a/sound/isa/es18xx.c	Mon Oct  7 11:25:33 2002
+++ b/sound/isa/es18xx.c	Mon Oct  7 11:25:33 2002
@@ -363,42 +363,42 @@
 
 static ratnum_t new_clocks[2] = {
 	{
-		num: 793800,
-		den_min: 1,
-		den_max: 128,
-		den_step: 1,
+		.num = 793800,
+		.den_min = 1,
+		.den_max = 128,
+		.den_step = 1,
 	},
 	{
-		num: 768000,
-		den_min: 1,
-		den_max: 128,
-		den_step: 1,
+		.num = 768000,
+		.den_min = 1,
+		.den_max = 128,
+		.den_step = 1,
 	}
 };
 
 static snd_pcm_hw_constraint_ratnums_t new_hw_constraints_clocks = {
-	nrats: 2,
-	rats: new_clocks,
+	.nrats = 2,
+	.rats = new_clocks,
 };
 
 static ratnum_t old_clocks[2] = {
 	{
-		num: 795444,
-		den_min: 1,
-		den_max: 128,
-		den_step: 1,
+		.num = 795444,
+		.den_min = 1,
+		.den_max = 128,
+		.den_step = 1,
 	},
 	{
-		num: 397722,
-		den_min: 1,
-		den_max: 128,
-		den_step: 1,
+		.num = 397722,
+		.den_min = 1,
+		.den_max = 128,
+		.den_step = 1,
 	}
 };
 
 static snd_pcm_hw_constraint_ratnums_t old_hw_constraints_clocks  = {
-	nrats: 2,
-	rats: old_clocks,
+	.nrats = 2,
+	.rats = old_clocks,
 };
 
 
@@ -826,40 +826,40 @@
 
 static snd_pcm_hardware_t snd_es18xx_playback =
 {
-	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
 				 SNDRV_PCM_INFO_MMAP_VALID),
-	formats:		(SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8 | 
+	.formats =		(SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8 | 
 				 SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE),
-	rates:			SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
-	rate_min:		4000,
-	rate_max:		48000,
-	channels_min:		1,
-	channels_max:		2,
-	buffer_bytes_max:	65536,
-	period_bytes_min:	64,
-	period_bytes_max:	65536,
-	periods_min:		1,
-	periods_max:		1024,
-	fifo_size:		0,
+	.rates =		SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+	.rate_min =		4000,
+	.rate_max =		48000,
+	.channels_min =		1,
+	.channels_max =		2,
+	.buffer_bytes_max =	65536,
+	.period_bytes_min =	64,
+	.period_bytes_max =	65536,
+	.periods_min =		1,
+	.periods_max =		1024,
+	.fifo_size =		0,
 };
 
 static snd_pcm_hardware_t snd_es18xx_capture =
 {
-	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
 				 SNDRV_PCM_INFO_MMAP_VALID),
-	formats:		(SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8 | 
+	.formats =		(SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8 | 
 				 SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE),
-	rates:			SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
-	rate_min:		4000,
-	rate_max:		48000,
-	channels_min:		1,
-	channels_max:		2,
-	buffer_bytes_max:	65536,
-	period_bytes_min:	64,
-	period_bytes_max:	65536,
-	periods_min:		1,
-	periods_max:		1024,
-	fifo_size:		0,
+	.rates =		SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+	.rate_min =		4000,
+	.rate_max =		48000,
+	.channels_min =		1,
+	.channels_max =		2,
+	.buffer_bytes_max =	65536,
+	.period_bytes_min =	64,
+	.period_bytes_max =	65536,
+	.periods_min =		1,
+	.periods_max =		1024,
+	.fifo_size =		0,
 };
 
 static int snd_es18xx_playback_open(snd_pcm_substream_t * substream)
@@ -1067,10 +1067,10 @@
 }
 
 #define ES18XX_SINGLE(xname, xindex, reg, shift, mask, invert) \
-{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \
-  info: snd_es18xx_info_single, \
-  get: snd_es18xx_get_single, put: snd_es18xx_put_single, \
-  private_value: reg | (shift << 8) | (mask << 16) | (invert << 24) }
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
+  .info = snd_es18xx_info_single, \
+  .get = snd_es18xx_get_single, .put = snd_es18xx_put_single, \
+  .private_value = reg | (shift << 8) | (mask << 16) | (invert << 24) }
 
 static int snd_es18xx_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
 {
@@ -1117,10 +1117,10 @@
 }
 
 #define ES18XX_DOUBLE(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \
-{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \
-  info: snd_es18xx_info_double, \
-  get: snd_es18xx_get_double, put: snd_es18xx_put_double, \
-  private_value: left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) }
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
+  .info = snd_es18xx_info_double, \
+  .get = snd_es18xx_get_double, .put = snd_es18xx_put_double, \
+  .private_value = left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) }
 
 static int snd_es18xx_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
 {
@@ -1207,11 +1207,11 @@
 ES18XX_DOUBLE("Capture Volume", 0, 0xb4, 0xb4, 4, 0, 15, 0),
 ES18XX_SINGLE("Capture Switch", 0, 0x1c, 4, 1, 1),
 {
-	iface: SNDRV_CTL_ELEM_IFACE_MIXER,
-	name: "Capture Source",
-	info: snd_es18xx_info_mux,
-	get: snd_es18xx_get_mux,
-	put: snd_es18xx_put_mux,
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "Capture Source",
+	.info = snd_es18xx_info_mux,
+	.get = snd_es18xx_get_mux,
+	.put = snd_es18xx_put_mux,
 }
 };
 
@@ -1240,11 +1240,11 @@
 static snd_kcontrol_new_t snd_es18xx_spatializer_controls[] = {
 ES18XX_SINGLE("3D Control - Level", 0, 0x52, 0, 63, 0),
 {
-	iface: SNDRV_CTL_ELEM_IFACE_MIXER,
-	name: "3D Control - Switch",
-	info: snd_es18xx_info_spatializer_enable,
-	get: snd_es18xx_get_spatializer_enable,
-	put: snd_es18xx_put_spatializer_enable,
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "3D Control - Switch",
+	.info = snd_es18xx_info_spatializer_enable,
+	.get = snd_es18xx_get_spatializer_enable,
+	.put = snd_es18xx_put_spatializer_enable,
 }
 };
 
@@ -1256,18 +1256,18 @@
 
 static snd_kcontrol_new_t snd_es18xx_hw_volume_controls[] = {
 {
-	iface: SNDRV_CTL_ELEM_IFACE_MIXER,
-	name: "Hardware Master Playback Volume",
-	access: SNDRV_CTL_ELEM_ACCESS_READ,
-	info: snd_es18xx_info_hw_volume,
-	get: snd_es18xx_get_hw_volume,
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "Hardware Master Playback Volume",
+	.access = SNDRV_CTL_ELEM_ACCESS_READ,
+	.info = snd_es18xx_info_hw_volume,
+	.get = snd_es18xx_get_hw_volume,
 },
 {
-	iface: SNDRV_CTL_ELEM_IFACE_MIXER,
-	name: "Hardware Master Playback Switch",
-	access: SNDRV_CTL_ELEM_ACCESS_READ,
-	info: snd_es18xx_info_hw_switch,
-	get: snd_es18xx_get_hw_switch,
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "Hardware Master Playback Switch",
+	.access = SNDRV_CTL_ELEM_ACCESS_READ,
+	.info = snd_es18xx_info_hw_switch,
+	.get = snd_es18xx_get_hw_switch,
 },
 ES18XX_SINGLE("Hardware Master Volume Split", 0, 0x64, 7, 1, 0),
 };
@@ -1548,23 +1548,23 @@
 }
 
 static snd_pcm_ops_t snd_es18xx_playback_ops = {
-	open:		snd_es18xx_playback_open,
-	close:		snd_es18xx_playback_close,
-	ioctl:		snd_pcm_lib_ioctl,
-	hw_params:	snd_es18xx_playback_hw_params,
-	prepare:	snd_es18xx_playback_prepare,
-	trigger:	snd_es18xx_playback_trigger,
-	pointer:	snd_es18xx_playback_pointer,
+	.open =		snd_es18xx_playback_open,
+	.close =	snd_es18xx_playback_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	snd_es18xx_playback_hw_params,
+	.prepare =	snd_es18xx_playback_prepare,
+	.trigger =	snd_es18xx_playback_trigger,
+	.pointer =	snd_es18xx_playback_pointer,
 };
 
 static snd_pcm_ops_t snd_es18xx_capture_ops = {
-	open:		snd_es18xx_capture_open,
-	close:		snd_es18xx_capture_close,
-	ioctl:		snd_pcm_lib_ioctl,
-	hw_params:	snd_es18xx_capture_hw_params,
-	prepare:	snd_es18xx_capture_prepare,
-	trigger:	snd_es18xx_capture_trigger,
-	pointer:	snd_es18xx_capture_pointer,
+	.open =		snd_es18xx_capture_open,
+	.close =	snd_es18xx_capture_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	snd_es18xx_capture_hw_params,
+	.prepare =	snd_es18xx_capture_prepare,
+	.trigger =	snd_es18xx_capture_trigger,
+	.pointer =	snd_es18xx_capture_pointer,
 };
 
 static void snd_es18xx_pcm_free(snd_pcm_t *pcm)
@@ -1732,7 +1732,7 @@
 {
         es18xx_t *chip;
 	static snd_device_ops_t ops = {
-		dev_free:	snd_es18xx_dev_free,
+		.dev_free =	snd_es18xx_dev_free,
         };
 	int err;
 
diff -Nru a/sound/isa/gus/gus_dma.c b/sound/isa/gus/gus_dma.c
--- a/sound/isa/gus/gus_dma.c	Mon Oct  7 11:25:25 2002
+++ b/sound/isa/gus/gus_dma.c	Mon Oct  7 11:25:25 2002
@@ -19,7 +19,6 @@
  *
  */
 
-#define __NO_VERSION__
 #include <sound/driver.h>
 #include <asm/dma.h>
 #include <linux/slab.h>
diff -Nru a/sound/isa/gus/gus_dram.c b/sound/isa/gus/gus_dram.c
--- a/sound/isa/gus/gus_dram.c	Mon Oct  7 11:25:35 2002
+++ b/sound/isa/gus/gus_dram.c	Mon Oct  7 11:25:35 2002
@@ -19,7 +19,6 @@
  *
  */
 
-#define __NO_VERSION__
 #include <sound/driver.h>
 #include <linux/time.h>
 #include <sound/core.h>
diff -Nru a/sound/isa/gus/gus_instr.c b/sound/isa/gus/gus_instr.c
--- a/sound/isa/gus/gus_instr.c	Mon Oct  7 11:25:40 2002
+++ b/sound/isa/gus/gus_instr.c	Mon Oct  7 11:25:40 2002
@@ -19,7 +19,6 @@
  *
  */
 
-#define __NO_VERSION__
 #include <sound/driver.h>
 #include <linux/time.h>
 #include <sound/core.h>
diff -Nru a/sound/isa/gus/gus_io.c b/sound/isa/gus/gus_io.c
--- a/sound/isa/gus/gus_io.c	Mon Oct  7 11:25:36 2002
+++ b/sound/isa/gus/gus_io.c	Mon Oct  7 11:25:36 2002
@@ -19,7 +19,6 @@
  *
  */
 
-#define __NO_VERSION__
 #include <sound/driver.h>
 #include <linux/delay.h>
 #include <linux/time.h>
diff -Nru a/sound/isa/gus/gus_irq.c b/sound/isa/gus/gus_irq.c
--- a/sound/isa/gus/gus_irq.c	Mon Oct  7 11:25:39 2002
+++ b/sound/isa/gus/gus_irq.c	Mon Oct  7 11:25:39 2002
@@ -19,7 +19,6 @@
  *
  */
 
-#define __NO_VERSION__
 #include <sound/driver.h>
 #include <sound/core.h>
 #include <sound/info.h>
diff -Nru a/sound/isa/gus/gus_lfo.c b/sound/isa/gus/gus_lfo.c
--- a/sound/isa/gus/gus_lfo.c	Mon Oct  7 11:25:33 2002
+++ b/sound/isa/gus/gus_lfo.c	Mon Oct  7 11:25:33 2002
@@ -20,7 +20,6 @@
  *
  */
 
-#define __NO_VERSION__
 #include <sound/driver.h>
 #include <sound/core.h>
 #include <sound/gus.h>
diff -Nru a/sound/isa/gus/gus_main.c b/sound/isa/gus/gus_main.c
--- a/sound/isa/gus/gus_main.c	Mon Oct  7 11:25:25 2002
+++ b/sound/isa/gus/gus_main.c	Mon Oct  7 11:25:25 2002
@@ -93,11 +93,11 @@
 }
 
 static snd_kcontrol_new_t snd_gus_joystick_control = {
-	iface: SNDRV_CTL_ELEM_IFACE_CARD,
-	name: "Joystick Speed",
-	info: snd_gus_joystick_info,
-	get: snd_gus_joystick_get,
-	put: snd_gus_joystick_put
+	.iface = SNDRV_CTL_ELEM_IFACE_CARD,
+	.name = "Joystick Speed",
+	.info = snd_gus_joystick_info,
+	.get = snd_gus_joystick_get,
+	.put = snd_gus_joystick_put
 };
 
 static void snd_gus_init_control(snd_gus_card_t *gus)
@@ -163,7 +163,7 @@
 	snd_gus_card_t *gus;
 	int err;
 	static snd_device_ops_t ops = {
-		dev_free:	snd_gus_dev_free,
+		.dev_free =	snd_gus_dev_free,
 	};
 
 	*rgus = NULL;
diff -Nru a/sound/isa/gus/gus_mem.c b/sound/isa/gus/gus_mem.c
--- a/sound/isa/gus/gus_mem.c	Mon Oct  7 11:25:33 2002
+++ b/sound/isa/gus/gus_mem.c	Mon Oct  7 11:25:33 2002
@@ -19,7 +19,6 @@
  *
  */
 
-#define __NO_VERSION__
 #include <sound/driver.h>
 #include <linux/slab.h>
 #include <sound/core.h>
diff -Nru a/sound/isa/gus/gus_mem_proc.c b/sound/isa/gus/gus_mem_proc.c
--- a/sound/isa/gus/gus_mem_proc.c	Mon Oct  7 11:25:36 2002
+++ b/sound/isa/gus/gus_mem_proc.c	Mon Oct  7 11:25:36 2002
@@ -19,7 +19,6 @@
  *
  */
 
-#define __NO_VERSION__
 #include <sound/driver.h>
 #include <linux/slab.h>
 #include <sound/core.h>
@@ -86,8 +85,8 @@
 }
 
 static struct snd_info_entry_ops snd_gf1_mem_proc_ops = {
-	read: snd_gf1_mem_proc_dump,
-	llseek: snd_gf1_mem_proc_llseek,
+	.read = snd_gf1_mem_proc_dump,
+	.llseek = snd_gf1_mem_proc_llseek,
 };
 
 int snd_gf1_mem_proc_init(snd_gus_card_t * gus)
diff -Nru a/sound/isa/gus/gus_mixer.c b/sound/isa/gus/gus_mixer.c
--- a/sound/isa/gus/gus_mixer.c	Mon Oct  7 11:25:25 2002
+++ b/sound/isa/gus/gus_mixer.c	Mon Oct  7 11:25:25 2002
@@ -19,7 +19,6 @@
  *
  */
 
-#define __NO_VERSION__
 #include <sound/driver.h>
 #include <linux/time.h>
 #include <linux/wait.h>
@@ -34,10 +33,10 @@
  */
 
 #define GF1_SINGLE(xname, xindex, shift, invert) \
-{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \
-  info: snd_gf1_info_single, \
-  get: snd_gf1_get_single, put: snd_gf1_put_single, \
-  private_value: shift | (invert << 8) }
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
+  .info = snd_gf1_info_single, \
+  .get = snd_gf1_get_single, .put = snd_gf1_put_single, \
+  .private_value = shift | (invert << 8) }
 
 static int snd_gf1_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
 {
@@ -84,10 +83,10 @@
 }
 
 #define ICS_DOUBLE(xname, xindex, addr) \
-{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \
-  info: snd_ics_info_double, \
-  get: snd_ics_get_double, put: snd_ics_put_double, \
-  private_value: addr }
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
+  .info = snd_ics_info_double, \
+  .get = snd_ics_get_double, .put = snd_ics_put_double, \
+  .private_value = addr }
 
 static int snd_ics_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
 {
diff -Nru a/sound/isa/gus/gus_pcm.c b/sound/isa/gus/gus_pcm.c
--- a/sound/isa/gus/gus_pcm.c	Mon Oct  7 11:25:33 2002
+++ b/sound/isa/gus/gus_pcm.c	Mon Oct  7 11:25:33 2002
@@ -25,7 +25,6 @@
  *
  */
 
-#define __NO_VERSION__
 #include <sound/driver.h>
 #include <asm/dma.h>
 #include <linux/slab.h>
@@ -531,15 +530,15 @@
 }
 
 static ratnum_t clock = {
-	num: 9878400/16,
-	den_min: 2,
-	den_max: 257,
-	den_step: 1,
+	.num = 9878400/16,
+	.den_min = 2,
+	.den_max = 257,
+	.den_step = 1,
 };
 
 static snd_pcm_hw_constraint_ratnums_t hw_constraints_clocks  = {
-	nrats: 1,
-	rats: &clock,
+	.nrats = 1,
+	.rats = &clock,
 };
 
 static int snd_gf1_pcm_capture_hw_params(snd_pcm_substream_t * substream,
@@ -620,38 +619,38 @@
 
 static snd_pcm_hardware_t snd_gf1_pcm_playback =
 {
-	info:			SNDRV_PCM_INFO_NONINTERLEAVED,
+	.info =			SNDRV_PCM_INFO_NONINTERLEAVED,
 	formats:		(SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 |
 				 SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE),
-	rates:			SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
-	rate_min:		5510,
-	rate_max:		48000,
-	channels_min:		1,
-	channels_max:		2,
-	buffer_bytes_max:	(128*1024),
-	period_bytes_min:	64,
-	period_bytes_max:	(128*1024),
-	periods_min:		1,
-	periods_max:		1024,
-	fifo_size:		0,
+	.rates =		SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+	.rate_min =		5510,
+	.rate_max =		48000,
+	.channels_min =		1,
+	.channels_max =		2,
+	.buffer_bytes_max =	(128*1024),
+	.period_bytes_min =	64,
+	.period_bytes_max =	(128*1024),
+	.periods_min =		1,
+	.periods_max =		1024,
+	.fifo_size =		0,
 };
 
 static snd_pcm_hardware_t snd_gf1_pcm_capture =
 {
-	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
 				 SNDRV_PCM_INFO_MMAP_VALID),
-	formats:		SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8,
-	rates:			SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_44100,
-	rate_min:		5510,
-	rate_max:		44100,
-	channels_min:		1,
-	channels_max:		2,
-	buffer_bytes_max:	(128*1024),
-	period_bytes_min:	64,
-	period_bytes_max:	(128*1024),
-	periods_min:		1,
-	periods_max:		1024,
-	fifo_size:		0,
+	.formats =		SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8,
+	.rates =		SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_44100,
+	.rate_min =		5510,
+	.rate_max =		44100,
+	.channels_min =		1,
+	.channels_max =		2,
+	.buffer_bytes_max =	(128*1024),
+	.period_bytes_min =	64,
+	.period_bytes_max =	(128*1024),
+	.periods_min =		1,
+	.periods_max =		1024,
+	.fifo_size =		0,
 };
 
 static void snd_gf1_pcm_playback_free(snd_pcm_runtime_t *runtime)
@@ -806,35 +805,35 @@
 
 static snd_kcontrol_new_t snd_gf1_pcm_volume_control =
 {
-	iface: SNDRV_CTL_ELEM_IFACE_MIXER,
-	name: "PCM Playback Volume",
-	info: snd_gf1_pcm_volume_info,
-	get: snd_gf1_pcm_volume_get,
-	put: snd_gf1_pcm_volume_put
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "PCM Playback Volume",
+	.info = snd_gf1_pcm_volume_info,
+	.get = snd_gf1_pcm_volume_get,
+	.put = snd_gf1_pcm_volume_put
 };
 
 static snd_pcm_ops_t snd_gf1_pcm_playback_ops = {
-	open:		snd_gf1_pcm_playback_open,
-	close:		snd_gf1_pcm_playback_close,
-	ioctl:		snd_pcm_lib_ioctl,
-	hw_params:	snd_gf1_pcm_playback_hw_params,
-	hw_free:	snd_gf1_pcm_playback_hw_free,
-	prepare:	snd_gf1_pcm_playback_prepare,
-	trigger:	snd_gf1_pcm_playback_trigger,
-	pointer:	snd_gf1_pcm_playback_pointer,
-	copy:		snd_gf1_pcm_playback_copy,
-	silence:	snd_gf1_pcm_playback_silence,
+	.open =		snd_gf1_pcm_playback_open,
+	.close =	snd_gf1_pcm_playback_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	snd_gf1_pcm_playback_hw_params,
+	.hw_free =	snd_gf1_pcm_playback_hw_free,
+	.prepare =	snd_gf1_pcm_playback_prepare,
+	.trigger =	snd_gf1_pcm_playback_trigger,
+	.pointer =	snd_gf1_pcm_playback_pointer,
+	.copy =		snd_gf1_pcm_playback_copy,
+	.silence =	snd_gf1_pcm_playback_silence,
 };
 
 static snd_pcm_ops_t snd_gf1_pcm_capture_ops = {
-	open:		snd_gf1_pcm_capture_open,
-	close:		snd_gf1_pcm_capture_close,
-	ioctl:		snd_pcm_lib_ioctl,
-	hw_params:	snd_gf1_pcm_capture_hw_params,
-	hw_free:	snd_gf1_pcm_capture_hw_free,
-	prepare:	snd_gf1_pcm_capture_prepare,
-	trigger:	snd_gf1_pcm_capture_trigger,
-	pointer:	snd_gf1_pcm_capture_pointer,
+	.open =		snd_gf1_pcm_capture_open,
+	.close =	snd_gf1_pcm_capture_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	snd_gf1_pcm_capture_hw_params,
+	.hw_free =	snd_gf1_pcm_capture_hw_free,
+	.prepare =	snd_gf1_pcm_capture_prepare,
+	.trigger =	snd_gf1_pcm_capture_trigger,
+	.pointer =	snd_gf1_pcm_capture_pointer,
 };
 
 int snd_gf1_pcm_new(snd_gus_card_t * gus, int pcm_dev, int control_index, snd_pcm_t ** rpcm)
diff -Nru a/sound/isa/gus/gus_reset.c b/sound/isa/gus/gus_reset.c
--- a/sound/isa/gus/gus_reset.c	Mon Oct  7 11:25:26 2002
+++ b/sound/isa/gus/gus_reset.c	Mon Oct  7 11:25:26 2002
@@ -18,7 +18,6 @@
  *
  */
 
-#define __NO_VERSION__
 #include <sound/driver.h>
 #include <linux/delay.h>
 #include <linux/interrupt.h>
diff -Nru a/sound/isa/gus/gus_sample.c b/sound/isa/gus/gus_sample.c
--- a/sound/isa/gus/gus_sample.c	Mon Oct  7 11:25:35 2002
+++ b/sound/isa/gus/gus_sample.c	Mon Oct  7 11:25:35 2002
@@ -19,7 +19,6 @@
  *
  */
 
-#define __NO_VERSION__
 #include <sound/driver.h>
 #include <linux/time.h>
 #include <sound/core.h>
diff -Nru a/sound/isa/gus/gus_simple.c b/sound/isa/gus/gus_simple.c
--- a/sound/isa/gus/gus_simple.c	Mon Oct  7 11:25:27 2002
+++ b/sound/isa/gus/gus_simple.c	Mon Oct  7 11:25:27 2002
@@ -19,7 +19,6 @@
  *
  */
 
-#define __NO_VERSION__
 #include <sound/driver.h>
 #include <linux/time.h>
 #include <sound/core.h>
diff -Nru a/sound/isa/gus/gus_timer.c b/sound/isa/gus/gus_timer.c
--- a/sound/isa/gus/gus_timer.c	Mon Oct  7 11:25:32 2002
+++ b/sound/isa/gus/gus_timer.c	Mon Oct  7 11:25:32 2002
@@ -21,7 +21,6 @@
  *
  */
 
-#define __NO_VERSION__
 #include <sound/driver.h>
 #include <linux/time.h>
 #include <sound/core.h>
@@ -129,20 +128,20 @@
 
 static struct _snd_timer_hardware snd_gf1_timer1 =
 {
-	flags:          SNDRV_TIMER_HW_STOP,
-	resolution:     80000,
-	ticks:          256,
-	start:          snd_gf1_timer1_start,
-	stop:           snd_gf1_timer1_stop,
+	.flags =	SNDRV_TIMER_HW_STOP,
+	.resolution =	80000,
+	.ticks =	256,
+	.start =	snd_gf1_timer1_start,
+	.stop =		snd_gf1_timer1_stop,
 };
 
 static struct _snd_timer_hardware snd_gf1_timer2 =
 {
-	flags:          SNDRV_TIMER_HW_STOP,
-	resolution:     320000,
-	ticks:          256,
-	start:          snd_gf1_timer2_start,
-	stop:           snd_gf1_timer2_stop,
+	.flags =	SNDRV_TIMER_HW_STOP,
+	.resolution =	320000,
+	.ticks =	256,
+	.start =	snd_gf1_timer2_start,
+	.stop =		snd_gf1_timer2_stop,
 };
 
 static void snd_gf1_timer1_free(snd_timer_t *timer)
diff -Nru a/sound/isa/gus/gus_uart.c b/sound/isa/gus/gus_uart.c
--- a/sound/isa/gus/gus_uart.c	Mon Oct  7 11:25:28 2002
+++ b/sound/isa/gus/gus_uart.c	Mon Oct  7 11:25:28 2002
@@ -19,7 +19,6 @@
  *
  */
 
-#define __NO_VERSION__
 #include <sound/driver.h>
 #include <linux/delay.h>
 #include <linux/interrupt.h>
@@ -51,7 +50,6 @@
 		if (stat & 0x10) {	/* framing error */
 			gus->gf1.uart_framing++;
 			spin_unlock_irqrestore(&gus->uart_cmd_lock, flags);
-			snd_rawmidi_receive_reset(gus->midi_substream_input);
 			continue;
 		}
 		byte = snd_gf1_uart_get(gus);
@@ -59,7 +57,6 @@
 		snd_rawmidi_receive(gus->midi_substream_input, &byte, 1);
 		if (stat & 0x20) {
 			gus->gf1.uart_overrun++;
-			snd_rawmidi_receive_reset(gus->midi_substream_input);
 		}
 	}
 }
@@ -227,16 +224,16 @@
 
 static snd_rawmidi_ops_t snd_gf1_uart_output =
 {
-	open:		snd_gf1_uart_output_open,
-	close:		snd_gf1_uart_output_close,
-	trigger:	snd_gf1_uart_output_trigger,
+	.open =		snd_gf1_uart_output_open,
+	.close =	snd_gf1_uart_output_close,
+	.trigger =	snd_gf1_uart_output_trigger,
 };
 
 static snd_rawmidi_ops_t snd_gf1_uart_input =
 {
-	open:           snd_gf1_uart_input_open,
-	close:          snd_gf1_uart_input_close,
-	trigger:        snd_gf1_uart_input_trigger,
+	.open =		snd_gf1_uart_input_open,
+	.close =	snd_gf1_uart_input_close,
+	.trigger =	snd_gf1_uart_input_trigger,
 };
 
 int snd_gf1_rawmidi_new(snd_gus_card_t * gus, int device, snd_rawmidi_t ** rrawmidi)
diff -Nru a/sound/isa/gus/gus_volume.c b/sound/isa/gus/gus_volume.c
--- a/sound/isa/gus/gus_volume.c	Mon Oct  7 11:25:28 2002
+++ b/sound/isa/gus/gus_volume.c	Mon Oct  7 11:25:28 2002
@@ -18,7 +18,6 @@
  *
  */
 
-#define __NO_VERSION__
 #include <sound/driver.h>
 #include <linux/time.h>
 #include <sound/core.h>
diff -Nru a/sound/isa/gus/interwave.c b/sound/isa/gus/interwave.c
--- a/sound/isa/gus/interwave.c	Mon Oct  7 11:25:25 2002
+++ b/sound/isa/gus/interwave.c	Mon Oct  7 11:25:25 2002
@@ -221,9 +221,9 @@
 }
 
 static snd_i2c_bit_ops_t snd_interwave_i2c_bit_ops = {
-	setlines: snd_interwave_i2c_setlines,
-	getclock: snd_interwave_i2c_getclockline,
-	getdata:  snd_interwave_i2c_getdataline,
+	.setlines = snd_interwave_i2c_setlines,
+	.getclock = snd_interwave_i2c_getclockline,
+	.getdata  = snd_interwave_i2c_getdataline,
 };
 
 static int __init snd_interwave_detect_stb(struct snd_interwave *iwcard,
diff -Nru a/sound/isa/opl3sa2.c b/sound/isa/opl3sa2.c
--- a/sound/isa/opl3sa2.c	Mon Oct  7 11:25:39 2002
+++ b/sound/isa/opl3sa2.c	Mon Oct  7 11:25:39 2002
@@ -339,10 +339,10 @@
 }
 
 #define OPL3SA2_SINGLE(xname, xindex, reg, shift, mask, invert) \
-{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \
-  info: snd_opl3sa2_info_single, \
-  get: snd_opl3sa2_get_single, put: snd_opl3sa2_put_single, \
-  private_value: reg | (shift << 8) | (mask << 16) | (invert << 24) }
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
+  .info = snd_opl3sa2_info_single, \
+  .get = snd_opl3sa2_get_single, .put = snd_opl3sa2_put_single, \
+  .private_value = reg | (shift << 8) | (mask << 16) | (invert << 24) }
 
 static int snd_opl3sa2_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
 {
@@ -397,10 +397,10 @@
 }
 
 #define OPL3SA2_DOUBLE(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \
-{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \
-  info: snd_opl3sa2_info_double, \
-  get: snd_opl3sa2_get_double, put: snd_opl3sa2_put_double, \
-  private_value: left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) }
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
+  .info = snd_opl3sa2_info_double, \
+  .get = snd_opl3sa2_get_double, .put = snd_opl3sa2_put_double, \
+  .private_value = left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) }
 
 int snd_opl3sa2_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
 {
@@ -725,7 +725,7 @@
 	cs4231_t *cs4231;
 	opl3_t *opl3;
 	static snd_device_ops_t ops = {
-		dev_free:	snd_opl3sa2_dev_free,
+		.dev_free =	snd_opl3sa2_dev_free,
 	};
 	int err;
 
diff -Nru a/sound/isa/opti9xx/opti92x-ad1848.c b/sound/isa/opti9xx/opti92x-ad1848.c
--- a/sound/isa/opti9xx/opti92x-ad1848.c	Mon Oct  7 11:25:26 2002
+++ b/sound/isa/opti9xx/opti92x-ad1848.c	Mon Oct  7 11:25:26 2002
@@ -289,7 +289,7 @@
 #define ISAPNP_OPTI9XX(_va, _vb, _vc, _device, _fa, _fb, _fc, _audio, _mpu401) \
 	{ \
 		ISAPNP_CARD_ID(_va, _vb, _vc, _device), \
-		devs: { ISAPNP_DEVICE_ID(_fa, _fb, _fc, _audio), \
+		.devs = { ISAPNP_DEVICE_ID(_fa, _fb, _fc, _audio), \
 			ISAPNP_DEVICE_ID(_fa, _fb, _fc, _mpu401), } \
 	}
 
@@ -821,9 +821,9 @@
 #define RATES sizeof(rates) / sizeof(rates[0])
 
 static snd_pcm_hw_constraint_list_t hw_constraints_rates = {
-	count: RATES,
-	list: rates,
-	mask: 0,
+	.count = RATES,
+	.list = rates,
+	.mask = 0,
 };
 
 unsigned char bits[] = {  0x01,  0x0f,  0x00,  0x0e,  0x03,  0x02,  0x05,
@@ -1135,39 +1135,39 @@
 
 
 static snd_pcm_hardware_t snd_opti93x_playback = {
-	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
 				 SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START),
-	formats:		(SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW | SNDRV_PCM_FMTBIT_IMA_ADPCM |
+	.formats =		(SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW | SNDRV_PCM_FMTBIT_IMA_ADPCM |
 				 SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE),
-	rates:			SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000,
-	rate_min:		5512,
-	rate_max:		48000,
-	channels_min:		1,
-	channels_max:		2,
-	buffer_bytes_max:	(128*1024),
-	period_bytes_min:	64,
-	period_bytes_max:	(128*1024),
-	periods_min:		1,
-	periods_max:		1024,
-	fifo_size:		0,
+	.rates =		SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_48000,
+	.rate_min =		5512,
+	.rate_max =		48000,
+	.channels_min =		1,
+	.channels_max =		2,
+	.buffer_bytes_max =	(128*1024),
+	.period_bytes_min =	64,
+	.period_bytes_max =	(128*1024),
+	.periods_min =		1,
+	.periods_max =		1024,
+	.fifo_size =		0,
 };
 
 static snd_pcm_hardware_t snd_opti93x_capture = {
-	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
 				 SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START),
-	formats:		(SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW | SNDRV_PCM_FMTBIT_IMA_ADPCM |
+	.formats =		(SNDRV_PCM_FMTBIT_MU_LAW | SNDRV_PCM_FMTBIT_A_LAW | SNDRV_PCM_FMTBIT_IMA_ADPCM |
 				 SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE),
-	rates:			SNDRV_PCM_RATE_8000_48000,
-	rate_min:		5512,
-	rate_max:		48000,
-	channels_min:		1,
-	channels_max:		2,
-	buffer_bytes_max:	(128*1024),
-	period_bytes_min:	64,
-	period_bytes_max:	(128*1024),
-	periods_min:		1,
-	periods_max:		1024,
-	fifo_size:		0,
+	.rates =		SNDRV_PCM_RATE_8000_48000,
+	.rate_min =		5512,
+	.rate_max =		48000,
+	.channels_min =		1,
+	.channels_max =		2,
+	.buffer_bytes_max =	(128*1024),
+	.period_bytes_min =	64,
+	.period_bytes_max =	(128*1024),
+	.periods_min =		1,
+	.periods_max =		1024,
+	.fifo_size =		0,
 };
 
 static int snd_opti93x_playback_open(snd_pcm_substream_t *substream)
@@ -1289,7 +1289,7 @@
 		       opti93x_t **rcodec)
 {
 	static snd_device_ops_t ops = {
-		dev_free:	snd_opti93x_dev_free,
+		.dev_free =	snd_opti93x_dev_free,
 	};
 	int error;
 	opti93x_t *codec;
@@ -1348,25 +1348,25 @@
 }
 
 static snd_pcm_ops_t snd_opti93x_playback_ops = {
-	open:		snd_opti93x_playback_open,
-	close:		snd_opti93x_playback_close,
-	ioctl:		snd_pcm_lib_ioctl,
-	hw_params:	snd_opti93x_hw_params,
-	hw_free:	snd_opti93x_hw_free,
-	prepare:	snd_opti93x_playback_prepare,
-	trigger:	snd_opti93x_playback_trigger,
-	pointer:	snd_opti93x_playback_pointer,
+	.open =		snd_opti93x_playback_open,
+	.close =	snd_opti93x_playback_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	snd_opti93x_hw_params,
+	.hw_free =	snd_opti93x_hw_free,
+	.prepare =	snd_opti93x_playback_prepare,
+	.trigger =	snd_opti93x_playback_trigger,
+	.pointer =	snd_opti93x_playback_pointer,
 };
 
 static snd_pcm_ops_t snd_opti93x_capture_ops = {
-	open:		snd_opti93x_capture_open,
-	close:		snd_opti93x_capture_close,
-	ioctl:		snd_pcm_lib_ioctl,
-	hw_params:	snd_opti93x_hw_params,
-	hw_free:	snd_opti93x_hw_free,
-	prepare:	snd_opti93x_capture_prepare,
-	trigger:	snd_opti93x_capture_trigger,
-	pointer:	snd_opti93x_capture_pointer,
+	.open =		snd_opti93x_capture_open,
+	.close =	snd_opti93x_capture_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	snd_opti93x_hw_params,
+	.hw_free =	snd_opti93x_hw_free,
+	.prepare =	snd_opti93x_capture_prepare,
+	.trigger =	snd_opti93x_capture_trigger,
+	.pointer =	snd_opti93x_capture_pointer,
 };
 
 static void snd_opti93x_pcm_free(snd_pcm_t *pcm)
@@ -1458,10 +1458,10 @@
 #if 0
 
 #define OPTi93X_SINGLE(xname, xindex, reg, shift, mask, invert) \
-{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \
-  info: snd_opti93x_info_single, \
-  get: snd_opti93x_get_single, put: snd_opti93x_put_single, \
-  private_value: reg | (shift << 8) | (mask << 16) | (invert << 24) }
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
+  .info = snd_opti93x_info_single, \
+  .get = snd_opti93x_get_single, .put = snd_opti93x_put_single, \
+  .private_value = reg | (shift << 8) | (mask << 16) | (invert << 24) }
 
 static int snd_opti93x_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
 {
@@ -1517,10 +1517,10 @@
 #endif /* single */
 
 #define OPTi93X_DOUBLE(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \
-{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \
-  info: snd_opti93x_info_double, \
-  get: snd_opti93x_get_double, put: snd_opti93x_put_double, \
-  private_value: left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) }
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
+  .info = snd_opti93x_info_double, \
+  .get = snd_opti93x_get_double, .put = snd_opti93x_put_double, \
+  .private_value = left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) }
 
 #define OPTi93X_DOUBLE_INVERT_INVERT(xctl) \
 	do { xctl.private_value ^= 22; } while (0)
@@ -1612,11 +1612,11 @@
 OPTi93X_DOUBLE("Aux Playback Volume", 0, OPTi931_AUX_LEFT_INPUT, OPTi931_AUX_RIGHT_INPUT, 1, 1, 15, 1), 
 OPTi93X_DOUBLE("Capture Volume", 0, OPTi93X_MIXOUT_LEFT, OPTi93X_MIXOUT_RIGHT, 0, 0, 15, 0),
 {
-	iface: SNDRV_CTL_ELEM_IFACE_MIXER,
-	name: "Capture Source",
-	info: snd_opti93x_info_mux,
-	get: snd_opti93x_get_mux,
-	put: snd_opti93x_put_mux,
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "Capture Source",
+	.info = snd_opti93x_info_mux,
+	.get = snd_opti93x_get_mux,
+	.put = snd_opti93x_put_mux,
 }
 };
                                         
diff -Nru a/sound/isa/sb/emu8000.c b/sound/isa/sb/emu8000.c
--- a/sound/isa/sb/emu8000.c	Mon Oct  7 11:25:30 2002
+++ b/sound/isa/sb/emu8000.c	Mon Oct  7 11:25:30 2002
@@ -20,7 +20,6 @@
  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  */
 
-#define __NO_VERSION__
 #include <sound/driver.h>
 #include <linux/wait.h>
 #include <linux/sched.h>
@@ -867,22 +866,22 @@
 
 static snd_kcontrol_new_t mixer_bass_control =
 {
-	iface: SNDRV_CTL_ELEM_IFACE_MIXER,
-	name: "Synth Tone Control - Bass",
-	info: mixer_bass_treble_info,
-	get: mixer_bass_treble_get,
-	put: mixer_bass_treble_put,
-	private_value: 0,
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "Synth Tone Control - Bass",
+	.info = mixer_bass_treble_info,
+	.get = mixer_bass_treble_get,
+	.put = mixer_bass_treble_put,
+	.private_value = 0,
 };
 
 static snd_kcontrol_new_t mixer_treble_control =
 {
-	iface: SNDRV_CTL_ELEM_IFACE_MIXER,
-	name: "Synth Tone Control - Treble",
-	info: mixer_bass_treble_info,
-	get: mixer_bass_treble_get,
-	put: mixer_bass_treble_put,
-	private_value: 1,
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "Synth Tone Control - Treble",
+	.info = mixer_bass_treble_info,
+	.get = mixer_bass_treble_get,
+	.put = mixer_bass_treble_put,
+	.private_value = 1,
 };
 
 /*
@@ -934,22 +933,22 @@
 
 static snd_kcontrol_new_t mixer_chorus_mode_control =
 {
-	iface: SNDRV_CTL_ELEM_IFACE_MIXER,
-	name: "Chorus Mode",
-	info: mixer_chorus_reverb_info,
-	get: mixer_chorus_reverb_get,
-	put: mixer_chorus_reverb_put,
-	private_value: 1,
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "Chorus Mode",
+	.info = mixer_chorus_reverb_info,
+	.get = mixer_chorus_reverb_get,
+	.put = mixer_chorus_reverb_put,
+	.private_value = 1,
 };
 
 static snd_kcontrol_new_t mixer_reverb_mode_control =
 {
-	iface: SNDRV_CTL_ELEM_IFACE_MIXER,
-	name: "Reverb Mode",
-	info: mixer_chorus_reverb_info,
-	get: mixer_chorus_reverb_get,
-	put: mixer_chorus_reverb_put,
-	private_value: 0,
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "Reverb Mode",
+	.info = mixer_chorus_reverb_info,
+	.get = mixer_chorus_reverb_get,
+	.put = mixer_chorus_reverb_put,
+	.private_value = 0,
 };
 
 /*
@@ -996,22 +995,22 @@
 
 static snd_kcontrol_new_t mixer_fm_chorus_depth_control =
 {
-	iface: SNDRV_CTL_ELEM_IFACE_MIXER,
-	name: "FM Chorus Depth",
-	info: mixer_fm_depth_info,
-	get: mixer_fm_depth_get,
-	put: mixer_fm_depth_put,
-	private_value: 1,
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "FM Chorus Depth",
+	.info = mixer_fm_depth_info,
+	.get = mixer_fm_depth_get,
+	.put = mixer_fm_depth_put,
+	.private_value = 1,
 };
 
 static snd_kcontrol_new_t mixer_fm_reverb_depth_control =
 {
-	iface: SNDRV_CTL_ELEM_IFACE_MIXER,
-	name: "FM Reverb Depth",
-	info: mixer_fm_depth_info,
-	get: mixer_fm_depth_get,
-	put: mixer_fm_depth_put,
-	private_value: 0,
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "FM Reverb Depth",
+	.info = mixer_fm_depth_info,
+	.get = mixer_fm_depth_get,
+	.put = mixer_fm_depth_put,
+	.private_value = 0,
 };
 
 
@@ -1091,7 +1090,7 @@
 	emu8000_t *hw;
 	int err;
 	static snd_device_ops_t ops = {
-		dev_free: snd_emu8000_dev_free,
+		.dev_free = snd_emu8000_dev_free,
 	};
 
 	if (awe_ret)
diff -Nru a/sound/isa/sb/emu8000_callback.c b/sound/isa/sb/emu8000_callback.c
--- a/sound/isa/sb/emu8000_callback.c	Mon Oct  7 11:25:31 2002
+++ b/sound/isa/sb/emu8000_callback.c	Mon Oct  7 11:25:31 2002
@@ -19,7 +19,6 @@
  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  */
 
-#define __NO_VERSION__
 #include "emu8000_local.h"
 #include <sound/asoundef.h>
 
@@ -60,21 +59,21 @@
  * set up operators
  */
 static snd_emux_operators_t emu8000_ops = {
-	owner:		THIS_MODULE,
-	get_voice:	get_voice,
-	prepare:	start_voice,
-	trigger:	trigger_voice,
-	release:	release_voice,
-	update:		update_voice,
-	terminate:	terminate_voice,
-	reset:		reset_voice,
-	sample_new:	snd_emu8000_sample_new,
-	sample_free:	snd_emu8000_sample_free,
-	sample_reset:	snd_emu8000_sample_reset,
-	load_fx:	load_fx,
-	sysex:		sysex,
+	.owner =	THIS_MODULE,
+	.get_voice =	get_voice,
+	.prepare =	start_voice,
+	.trigger =	trigger_voice,
+	.release =	release_voice,
+	.update =	update_voice,
+	.terminate =	terminate_voice,
+	.reset =	reset_voice,
+	.sample_new =	snd_emu8000_sample_new,
+	.sample_free =	snd_emu8000_sample_free,
+	.sample_reset = snd_emu8000_sample_reset,
+	.load_fx =	load_fx,
+	.sysex =	sysex,
 #ifdef CONFIG_SND_SEQUENCER_OSS
-	oss_ioctl:	oss_ioctl,
+	.oss_ioctl =	oss_ioctl,
 #endif
 };
 
diff -Nru a/sound/isa/sb/emu8000_patch.c b/sound/isa/sb/emu8000_patch.c
--- a/sound/isa/sb/emu8000_patch.c	Mon Oct  7 11:25:26 2002
+++ b/sound/isa/sb/emu8000_patch.c	Mon Oct  7 11:25:26 2002
@@ -19,7 +19,6 @@
  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  */
 
-#define __NO_VERSION__
 #include "emu8000_local.h"
 #include <asm/uaccess.h>
 
diff -Nru a/sound/isa/sb/emu8000_pcm.c b/sound/isa/sb/emu8000_pcm.c
--- a/sound/isa/sb/emu8000_pcm.c	Mon Oct  7 11:25:39 2002
+++ b/sound/isa/sb/emu8000_pcm.c	Mon Oct  7 11:25:39 2002
@@ -159,22 +159,22 @@
 
 static snd_pcm_hardware_t emu8k_pcm_hw = {
 #ifdef USE_NONINTERLEAVE
-	info:			SNDRV_PCM_INFO_NONINTERLEAVED,
+	.info =			SNDRV_PCM_INFO_NONINTERLEAVED,
 #else
-	info:			SNDRV_PCM_INFO_INTERLEAVED,
+	.info =			SNDRV_PCM_INFO_INTERLEAVED,
 #endif
-	formats:		SNDRV_PCM_FMTBIT_S16_LE,
-	rates:			SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
-	rate_min:		4000,
-	rate_max:		48000,
-	channels_min:		1,
-	channels_max:		2,
-	buffer_bytes_max:	(128*1024),
-	period_bytes_min:	1024,
-	period_bytes_max:	(128*1024),
-	periods_min:		2,
-	periods_max:		1024,
-	fifo_size:		0,
+	.formats =		SNDRV_PCM_FMTBIT_S16_LE,
+	.rates =		SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+	.rate_min =		4000,
+	.rate_max =		48000,
+	.channels_min =		1,
+	.channels_max =		2,
+	.buffer_bytes_max =	(128*1024),
+	.period_bytes_min =	1024,
+	.period_bytes_max =	(128*1024),
+	.periods_min =		2,
+	.periods_max =		1024,
+	.fifo_size =		0,
 
 };
 
@@ -672,16 +672,16 @@
 
 
 static snd_pcm_ops_t emu8k_pcm_ops = {
-	open:		emu8k_pcm_open,
-	close:		emu8k_pcm_close,
-	ioctl:		snd_pcm_lib_ioctl,
-	hw_params:	emu8k_pcm_hw_params,
-	hw_free:	emu8k_pcm_hw_free,
-	prepare:	emu8k_pcm_prepare,
-	trigger:	emu8k_pcm_trigger,
-	pointer:	emu8k_pcm_pointer,
-	copy:		emu8k_pcm_copy,
-	silence:	emu8k_pcm_silence,
+	.open =		emu8k_pcm_open,
+	.close =	emu8k_pcm_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	emu8k_pcm_hw_params,
+	.hw_free =	emu8k_pcm_hw_free,
+	.prepare =	emu8k_pcm_prepare,
+	.trigger =	emu8k_pcm_trigger,
+	.pointer =	emu8k_pcm_pointer,
+	.copy =		emu8k_pcm_copy,
+	.silence =	emu8k_pcm_silence,
 };
 
 
diff -Nru a/sound/isa/sb/es968.c b/sound/isa/sb/es968.c
--- a/sound/isa/sb/es968.c	Mon Oct  7 11:25:35 2002
+++ b/sound/isa/sb/es968.c	Mon Oct  7 11:25:35 2002
@@ -82,7 +82,7 @@
 static struct isapnp_card_id snd_es968_pnpids[] __devinitdata = {
         {
                 ISAPNP_CARD_ID('E','S','S',0x0968),
-                devs: { ISAPNP_DEVICE_ID('E','S','S',0x0968), }
+                .devs = { ISAPNP_DEVICE_ID('E','S','S',0x0968), }
         },
         { ISAPNP_CARD_END, }
 };
diff -Nru a/sound/isa/sb/sb16_csp.c b/sound/isa/sb/sb16_csp.c
--- a/sound/isa/sb/sb16_csp.c	Mon Oct  7 11:25:39 2002
+++ b/sound/isa/sb/sb16_csp.c	Mon Oct  7 11:25:39 2002
@@ -1010,19 +1010,19 @@
 }
 
 static snd_kcontrol_new_t snd_sb_qsound_switch = {
-	iface: SNDRV_CTL_ELEM_IFACE_MIXER,
-	name: "3D Control - Switch",
-	info: snd_sb_qsound_switch_info,
-	get: snd_sb_qsound_switch_get,
-	put: snd_sb_qsound_switch_put
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "3D Control - Switch",
+	.info = snd_sb_qsound_switch_info,
+	.get = snd_sb_qsound_switch_get,
+	.put = snd_sb_qsound_switch_put
 };
 
 static snd_kcontrol_new_t snd_sb_qsound_space = {
-	iface: SNDRV_CTL_ELEM_IFACE_MIXER,
-	name: "3D Control - Space",
-	info: snd_sb_qsound_space_info,
-	get: snd_sb_qsound_space_get,
-	put: snd_sb_qsound_space_put
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "3D Control - Space",
+	.info = snd_sb_qsound_space_info,
+	.get = snd_sb_qsound_space_get,
+	.put = snd_sb_qsound_space_put
 };
 
 static int snd_sb_qsound_build(snd_sb_csp_t * p)
diff -Nru a/sound/isa/sb/sb16_main.c b/sound/isa/sb/sb16_main.c
--- a/sound/isa/sb/sb16_main.c	Mon Oct  7 11:25:35 2002
+++ b/sound/isa/sb/sb16_main.c	Mon Oct  7 11:25:35 2002
@@ -466,38 +466,38 @@
 
 static snd_pcm_hardware_t snd_sb16_playback =
 {
-	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
 				 SNDRV_PCM_INFO_MMAP_VALID),
-	formats:		0,
-	rates:			SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_44100,
-	rate_min:		4000,
-	rate_max:		44100,
-	channels_min:		1,
-	channels_max:		2,
-	buffer_bytes_max:	(128*1024),
-	period_bytes_min:	64,
-	period_bytes_max:	(128*1024),
-	periods_min:		1,
-	periods_max:		1024,
-	fifo_size:		0,
+	.formats =		0,
+	.rates =		SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_44100,
+	.rate_min =		4000,
+	.rate_max =		44100,
+	.channels_min =		1,
+	.channels_max =		2,
+	.buffer_bytes_max =	(128*1024),
+	.period_bytes_min =	64,
+	.period_bytes_max =	(128*1024),
+	.periods_min =		1,
+	.periods_max =		1024,
+	.fifo_size =		0,
 };
 
 static snd_pcm_hardware_t snd_sb16_capture =
 {
-	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
 				 SNDRV_PCM_INFO_MMAP_VALID),
-	formats:		0,
-	rates:			SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_44100,
-	rate_min:		4000,
-	rate_max:		44100,
-	channels_min:		1,
-	channels_max:		2,
-	buffer_bytes_max:	(128*1024),
-	period_bytes_min:	64,
-	period_bytes_max:	(128*1024),
-	periods_min:		1,
-	periods_max:		1024,
-	fifo_size:		0,
+	.formats =		0,
+	.rates =		SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_44100,
+	.rate_min =		4000,
+	.rate_max =		44100,
+	.channels_min =		1,
+	.channels_max =		2,
+	.buffer_bytes_max =	(128*1024),
+	.period_bytes_min =	64,
+	.period_bytes_max =	(128*1024),
+	.periods_min =		1,
+	.periods_max =		1024,
+	.fifo_size =		0,
 };
 
 /*
@@ -724,11 +724,11 @@
 }
 
 snd_kcontrol_new_t snd_sb16_dma_control = {
-	iface: SNDRV_CTL_ELEM_IFACE_PCM,
-	name: "16-bit DMA Allocation",
-	info: snd_sb16_dma_control_info,
-	get: snd_sb16_dma_control_get,
-	put: snd_sb16_dma_control_put
+	.iface = SNDRV_CTL_ELEM_IFACE_PCM,
+	.name = "16-bit DMA Allocation",
+	.info = snd_sb16_dma_control_info,
+	.get = snd_sb16_dma_control_get,
+	.put = snd_sb16_dma_control_put
 };
 
 /*
@@ -825,25 +825,25 @@
 }
 
 static snd_pcm_ops_t snd_sb16_playback_ops = {
-	open:			snd_sb16_playback_open,
-	close:			snd_sb16_playback_close,
-	ioctl:			snd_pcm_lib_ioctl,
-	hw_params:		snd_sb16_hw_params,
-	hw_free:		snd_sb16_hw_free,
-	prepare:		snd_sb16_playback_prepare,
-	trigger:		snd_sb16_playback_trigger,
-	pointer:		snd_sb16_playback_pointer,
+	.open =		snd_sb16_playback_open,
+	.close =	snd_sb16_playback_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	snd_sb16_hw_params,
+	.hw_free =	snd_sb16_hw_free,
+	.prepare =	snd_sb16_playback_prepare,
+	.trigger =	snd_sb16_playback_trigger,
+	.pointer =	snd_sb16_playback_pointer,
 };
 
 static snd_pcm_ops_t snd_sb16_capture_ops = {
-	open:			snd_sb16_capture_open,
-	close:			snd_sb16_capture_close,
-	ioctl:			snd_pcm_lib_ioctl,
-	hw_params:		snd_sb16_hw_params,
-	hw_free:		snd_sb16_hw_free,
-	prepare:		snd_sb16_capture_prepare,
-	trigger:		snd_sb16_capture_trigger,
-	pointer:		snd_sb16_capture_pointer,
+	.open =		snd_sb16_capture_open,
+	.close =	snd_sb16_capture_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	snd_sb16_hw_params,
+	.hw_free =	snd_sb16_hw_free,
+	.prepare =	snd_sb16_capture_prepare,
+	.trigger =	snd_sb16_capture_trigger,
+	.pointer =	snd_sb16_capture_pointer,
 };
 
 static void snd_sb16dsp_pcm_free(snd_pcm_t *pcm)
diff -Nru a/sound/isa/sb/sb8_main.c b/sound/isa/sb/sb8_main.c
--- a/sound/isa/sb/sb8_main.c	Mon Oct  7 11:25:34 2002
+++ b/sound/isa/sb/sb8_main.c	Mon Oct  7 11:25:34 2002
@@ -49,29 +49,29 @@
 #define SB8_RATE(v)	(SB8_CLOCK / SB8_DEN(v))
 
 static ratnum_t clock = {
-	num: SB8_CLOCK,
-	den_min: 1,
-	den_max: 256,
-	den_step: 1,
+	.num = SB8_CLOCK,
+	.den_min = 1,
+	.den_max = 256,
+	.den_step = 1,
 };
 
 static snd_pcm_hw_constraint_ratnums_t hw_constraints_clock = {
-	nrats: 1,
-	rats: &clock,
+	.nrats = 1,
+	.rats = &clock,
 };
 
 static ratnum_t stereo_clocks[] = {
 	{
-		num: SB8_CLOCK,
-		den_min: SB8_DEN(22050),
-		den_max: SB8_DEN(22050),
-		den_step: 1,
+		.num = SB8_CLOCK,
+		.den_min = SB8_DEN(22050),
+		.den_max = SB8_DEN(22050),
+		.den_step = 1,
 	},
 	{
-		num: SB8_CLOCK,
-		den_min: SB8_DEN(11025),
-		den_max: SB8_DEN(11025),
-		den_step: 1,
+		.num = SB8_CLOCK,
+		.den_min = SB8_DEN(11025),
+		.den_max = SB8_DEN(11025),
+		.den_step = 1,
 	}
 };
 
@@ -386,40 +386,40 @@
 
 static snd_pcm_hardware_t snd_sb8_playback =
 {
-	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
 				 SNDRV_PCM_INFO_MMAP_VALID),
-	formats:		SNDRV_PCM_FMTBIT_U8,
-	rates:			(SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000 |
+	.formats =		 SNDRV_PCM_FMTBIT_U8,
+	.rates =		(SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000 |
 				 SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_22050),
-	rate_min:		4000,
-	rate_max:		23000,
-	channels_min:		1,
-	channels_max:		1,
-	buffer_bytes_max:	65536,
-	period_bytes_min:	64,
-	period_bytes_max:	65536,
-	periods_min:		1,
-	periods_max:		1024,
-	fifo_size:		0,
+	.rate_min =		4000,
+	.rate_max =		23000,
+	.channels_min =		1,
+	.channels_max =		1,
+	.buffer_bytes_max =	65536,
+	.period_bytes_min =	64,
+	.period_bytes_max =	65536,
+	.periods_min =		1,
+	.periods_max =		1024,
+	.fifo_size =		0,
 };
 
 static snd_pcm_hardware_t snd_sb8_capture =
 {
-	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
 				 SNDRV_PCM_INFO_MMAP_VALID),
-	formats:		SNDRV_PCM_FMTBIT_U8,
-	rates:			(SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000 |
+	.formats =		SNDRV_PCM_FMTBIT_U8,
+	.rates =		(SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000 |
 				 SNDRV_PCM_RATE_11025),
-	rate_min:		4000,
-	rate_max:		13000,
-	channels_min:		1,
-	channels_max:		1,
-	buffer_bytes_max:	65536,
-	period_bytes_min:	64,
-	period_bytes_max:	65536,
-	periods_min:		1,
-	periods_max:		1024,
-	fifo_size:		0,
+	.rate_min =		4000,
+	.rate_max =		13000,
+	.channels_min =		1,
+	.channels_max =		1,
+	.buffer_bytes_max =	65536,
+	.period_bytes_min =	64,
+	.period_bytes_max =	65536,
+	.periods_min =		1,
+	.periods_max =		1024,
+	.fifo_size =		0,
 };
 
 /*
@@ -490,25 +490,25 @@
  */
  
 static snd_pcm_ops_t snd_sb8_playback_ops = {
-	open:			snd_sb8_open,
-	close:			snd_sb8_close,
-	ioctl:			snd_pcm_lib_ioctl,
-	hw_params:		snd_sb8_hw_params,
-	hw_free:		snd_sb8_hw_free,
-	prepare:		snd_sb8_playback_prepare,
-	trigger:		snd_sb8_playback_trigger,
-	pointer:		snd_sb8_playback_pointer,
+	.open =			snd_sb8_open,
+	.close =		snd_sb8_close,
+	.ioctl =		snd_pcm_lib_ioctl,
+	.hw_params =		snd_sb8_hw_params,
+	.hw_free =		snd_sb8_hw_free,
+	.prepare =		snd_sb8_playback_prepare,
+	.trigger =		snd_sb8_playback_trigger,
+	.pointer =		snd_sb8_playback_pointer,
 };
 
 static snd_pcm_ops_t snd_sb8_capture_ops = {
-	open:			snd_sb8_open,
-	close:			snd_sb8_close,
-	ioctl:			snd_pcm_lib_ioctl,
-	hw_params:		snd_sb8_hw_params,
-	hw_free:		snd_sb8_hw_free,
-	prepare:		snd_sb8_capture_prepare,
-	trigger:		snd_sb8_capture_trigger,
-	pointer:		snd_sb8_capture_pointer,
+	.open =			snd_sb8_open,
+	.close =		snd_sb8_close,
+	.ioctl =		snd_pcm_lib_ioctl,
+	.hw_params =		snd_sb8_hw_params,
+	.hw_free =		snd_sb8_hw_free,
+	.prepare =		snd_sb8_capture_prepare,
+	.trigger =		snd_sb8_capture_trigger,
+	.pointer =		snd_sb8_capture_pointer,
 };
 
 static void snd_sb8dsp_pcm_free(snd_pcm_t *pcm)
diff -Nru a/sound/isa/sb/sb8_midi.c b/sound/isa/sb/sb8_midi.c
--- a/sound/isa/sb/sb8_midi.c	Mon Oct  7 11:25:25 2002
+++ b/sound/isa/sb/sb8_midi.c	Mon Oct  7 11:25:25 2002
@@ -23,7 +23,6 @@
  *   working.
  */
 
-#define __NO_VERSION__
 #include <sound/driver.h>
 #include <asm/io.h>
 #include <linux/time.h>
@@ -229,16 +228,16 @@
 
 static snd_rawmidi_ops_t snd_sb8dsp_midi_output =
 {
-	open:           snd_sb8dsp_midi_output_open,
-	close:          snd_sb8dsp_midi_output_close,
-	trigger:	snd_sb8dsp_midi_output_trigger,
+	.open =		snd_sb8dsp_midi_output_open,
+	.close =	snd_sb8dsp_midi_output_close,
+	.trigger =	snd_sb8dsp_midi_output_trigger,
 };
 
 static snd_rawmidi_ops_t snd_sb8dsp_midi_input =
 {
-	open:           snd_sb8dsp_midi_input_open,
-	close:          snd_sb8dsp_midi_input_close,
-	trigger:        snd_sb8dsp_midi_input_trigger,
+	.open =		snd_sb8dsp_midi_input_open,
+	.close =	snd_sb8dsp_midi_input_close,
+	.trigger =	snd_sb8dsp_midi_input_trigger,
 };
 
 int snd_sb8dsp_midi(sb_t *chip, int device, snd_rawmidi_t ** rrawmidi)
diff -Nru a/sound/isa/sb/sb_common.c b/sound/isa/sb/sb_common.c
--- a/sound/isa/sb/sb_common.c	Mon Oct  7 11:25:39 2002
+++ b/sound/isa/sb/sb_common.c	Mon Oct  7 11:25:39 2002
@@ -221,7 +221,7 @@
 	sb_t *chip;
 	int err;
 	static snd_device_ops_t ops = {
-		dev_free:       snd_sbdsp_dev_free,
+		.dev_free =	snd_sbdsp_dev_free,
 	};
 
 	snd_assert(r_chip != NULL, return -EINVAL);
diff -Nru a/sound/isa/sb/sb_mixer.c b/sound/isa/sb/sb_mixer.c
--- a/sound/isa/sb/sb_mixer.c	Mon Oct  7 11:25:33 2002
+++ b/sound/isa/sb/sb_mixer.c	Mon Oct  7 11:25:33 2002
@@ -19,7 +19,6 @@
  *
  */
 
-#define __NO_VERSION__
 #include <sound/driver.h>
 #include <asm/io.h>
 #include <linux/delay.h>
@@ -62,11 +61,11 @@
  */
 
 #define SB_SINGLE(xname, reg, shift, mask) \
-{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, \
-  name: xname, \
-  info: snd_sbmixer_info_single, \
-  get: snd_sbmixer_get_single, put: snd_sbmixer_put_single, \
-  private_value: reg | (shift << 16) | (mask << 24) }
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+  .name = xname, \
+  .info = snd_sbmixer_info_single, \
+  .get = snd_sbmixer_get_single, put: snd_sbmixer_put_single, \
+  .private_value = reg | (shift << 16) | (mask << 24) }
 
 static int snd_sbmixer_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
 {
@@ -121,11 +120,11 @@
  */
 
 #define SB_DOUBLE(xname, left_reg, right_reg, left_shift, right_shift, mask) \
-{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, \
-  name: xname, \
-  info: snd_sbmixer_info_double, \
-  get: snd_sbmixer_get_double, put: snd_sbmixer_put_double, \
-  private_value: left_reg | (right_reg << 8) | (left_shift << 16) | (right_shift << 19) | (mask << 24) }
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+  .name = xname, \
+  .info = snd_sbmixer_info_double, \
+  .get = snd_sbmixer_get_double, put: snd_sbmixer_put_double, \
+  .private_value = left_reg | (right_reg << 8) | (left_shift << 16) | (right_shift << 19) | (mask << 24) }
 
 static int snd_sbmixer_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
 {
@@ -364,11 +363,11 @@
  */
 
 #define SB16_INPUT_SW(xname, reg1, reg2, left_shift, right_shift) \
-{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, \
-  name: xname, \
-  info: snd_sb16mixer_info_input_sw, \
-  get: snd_sb16mixer_get_input_sw, put: snd_sb16mixer_put_input_sw, \
-  private_value: reg1 | (reg2 << 8) | (left_shift << 16) | (right_shift << 24) }
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+  .name = xname, \
+  .info = snd_sb16mixer_info_input_sw, \
+  .get = snd_sb16mixer_get_input_sw, put: snd_sb16mixer_put_input_sw, \
+  .private_value = reg1 | (reg2 << 8) | (left_shift << 16) | (right_shift << 24) }
 
 static int snd_sb16mixer_info_input_sw(snd_kcontrol_t * kcontrol, snd_ctl_elem_info_t * uinfo)
 {
@@ -456,11 +455,11 @@
 SB_DOUBLE("Line Playback Volume", SB_DSP_LINE_DEV, SB_DSP_LINE_DEV, 5, 1, 7),
 SB_SINGLE("Mic Playback Volume", SB_DSP_MIC_DEV, 1, 3),
 {
-	iface: SNDRV_CTL_ELEM_IFACE_MIXER,
-	name: "Capture Source",
-	info: snd_sb8mixer_info_mux,
-	get: snd_sb8mixer_get_mux,
-	put: snd_sb8mixer_put_mux,
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "Capture Source",
+	.info = snd_sb8mixer_info_mux,
+	.get = snd_sb8mixer_get_mux,
+	.put = snd_sb8mixer_put_mux,
 },
 SB_SINGLE("Capture Filter", SB_DSP_CAPTURE_FILT, 5, 1),
 SB_SINGLE("Capture Low-Pass Filter", SB_DSP_CAPTURE_FILT, 3, 1)
@@ -530,11 +529,11 @@
 SB_DOUBLE("PCM Playback Switch", SB_DT019X_OUTPUT_SW2, SB_DT019X_OUTPUT_SW2, 2,1, 1),
 SB_DOUBLE("Synth Playback Switch", SB_DT019X_OUTPUT_SW2, SB_DT019X_OUTPUT_SW2, 4,3, 1),
 {
-	iface: SNDRV_CTL_ELEM_IFACE_MIXER,
-	name: "Capture Source",
-	info: snd_dt019x_input_sw_info,
-	get: snd_dt019x_input_sw_get,
-	put: snd_dt019x_input_sw_put,
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "Capture Source",
+	.info = snd_dt019x_input_sw_info,
+	.get = snd_dt019x_input_sw_get,
+	.put = snd_dt019x_input_sw_put,
 },
 };
 
diff -Nru a/sound/isa/sgalaxy.c b/sound/isa/sgalaxy.c
--- a/sound/isa/sgalaxy.c	Mon Oct  7 11:25:40 2002
+++ b/sound/isa/sgalaxy.c	Mon Oct  7 11:25:40 2002
@@ -26,6 +26,7 @@
 #include <linux/init.h>
 #include <linux/delay.h>
 #include <linux/time.h>
+#include <linux/irq.h>
 #include <sound/core.h>
 #include <sound/sb.h>
 #include <sound/ad1848.h>
@@ -110,6 +111,10 @@
 	return 0;
 }
 
+static void snd_sgalaxy_dummy_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+}
+
 static int __init snd_sgalaxy_setup_wss(unsigned long port, int irq, int dma)
 {
 	static int interrupt_bits[] = {-1, -1, -1, -1, -1, -1, -1, 0x08, -1, 
@@ -117,8 +122,6 @@
 	static int dma_bits[] = {1, 2, 0, 3};
 	int tmp, tmp1;
 
-	unsigned long flags;
-
 	if ((tmp = inb(port + 3)) == 0xff)
 	{
 		snd_printdd("I/O address dead (0x%lx)\n", port);
@@ -140,20 +143,20 @@
 	snd_printdd("sgalaxy - setting up IRQ/DMA for WSS\n");
 #endif
 
-	save_flags(flags);
-	cli();
-
         /* initialize IRQ for WSS codec */
         tmp = interrupt_bits[irq % 16];
-        if (tmp < 0) {
-		restore_flags(flags);
+        if (tmp < 0)
                 return -EINVAL;
-	}
+
+	if (request_irq(irq, snd_sgalaxy_dummy_interrupt, SA_INTERRUPT, "sgalaxy", NULL))
+		return -EIO;
+
         outb(tmp | 0x40, port);
         tmp1 = dma_bits[dma % 4];
         outb(tmp | tmp1, port);
 
-	restore_flags(flags);
+	free_irq(irq, NULL);
+
 	return 0;
 }
 
diff -Nru a/sound/isa/wavefront/wavefront.c b/sound/isa/wavefront/wavefront.c
--- a/sound/isa/wavefront/wavefront.c	Mon Oct  7 11:25:26 2002
+++ b/sound/isa/wavefront/wavefront.c	Mon Oct  7 11:25:26 2002
@@ -111,14 +111,14 @@
 static struct isapnp_card_id snd_wavefront_pnpids[] __devinitdata = {
 	{
 		ISAPNP_CARD_ID('C','S','C',0x7532),	/* Tropez */
-		devs: { ISAPNP_DEVICE_ID('C','S','C',0x0000),	/* WSS */
+		.devs = { ISAPNP_DEVICE_ID('C','S','C',0x0000),	/* WSS */
 			ISAPNP_DEVICE_ID('C','S','C',0x0010),	/* CTRL */
 			ISAPNP_DEVICE_ID('P','n','P',0xb006),	/* MPU */
 			ISAPNP_DEVICE_ID('C','S','C',000004), }, /* SYNTH */
 	},
 	{
 		ISAPNP_CARD_ID('C','S','C',0x7632),	/* Tropez+ */
-		devs: { ISAPNP_DEVICE_ID('C','S','C',0x0000),	/* WSS */
+		.devs = { ISAPNP_DEVICE_ID('C','S','C',0x0000),	/* WSS */
 			ISAPNP_DEVICE_ID('C','S','C',0x0010),	/* CTRL */
 			ISAPNP_DEVICE_ID('P','n','P',0xb006),	/* MPU */
 			ISAPNP_DEVICE_ID('C','S','C',000004), }, /* SYNTH */
@@ -495,6 +495,7 @@
 	}
 	acard = (snd_wavefront_card_t *)card->private_data;
 	acard->wavefront.irq = -1;
+	spin_lock_init(&acard->wavefront.irq_lock);
 	init_waitqueue_head(&acard->wavefront.interrupt_sleeper);
 	spin_lock_init(&acard->wavefront.midi.open);
 	spin_lock_init(&acard->wavefront.midi.virtual);
diff -Nru a/sound/isa/wavefront/wavefront_fx.c b/sound/isa/wavefront/wavefront_fx.c
--- a/sound/isa/wavefront/wavefront_fx.c	Mon Oct  7 11:25:36 2002
+++ b/sound/isa/wavefront/wavefront_fx.c	Mon Oct  7 11:25:36 2002
@@ -16,7 +16,6 @@
  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  */
 
-#define __NO_VERSION__
 #include <sound/driver.h>
 #include <asm/io.h>
 #include <linux/init.h>
diff -Nru a/sound/isa/wavefront/wavefront_midi.c b/sound/isa/wavefront/wavefront_midi.c
--- a/sound/isa/wavefront/wavefront_midi.c	Mon Oct  7 11:25:39 2002
+++ b/sound/isa/wavefront/wavefront_midi.c	Mon Oct  7 11:25:39 2002
@@ -47,7 +47,6 @@
  *  
  */
 
-#define __NO_VERSION__
 #include <sound/driver.h>
 #include <asm/io.h>
 #include <linux/init.h>
@@ -560,15 +559,15 @@
 
 snd_rawmidi_ops_t snd_wavefront_midi_output =
 {
-	open:		snd_wavefront_midi_output_open,
-	close:		snd_wavefront_midi_output_close,
-	trigger:	snd_wavefront_midi_output_trigger,
+	.open =		snd_wavefront_midi_output_open,
+	.close =	snd_wavefront_midi_output_close,
+	.trigger =	snd_wavefront_midi_output_trigger,
 };
 
 snd_rawmidi_ops_t snd_wavefront_midi_input =
 {
-	open:		snd_wavefront_midi_input_open,
-	close:		snd_wavefront_midi_input_close,
-	trigger:	snd_wavefront_midi_input_trigger,
+	.open =		snd_wavefront_midi_input_open,
+	.close =	snd_wavefront_midi_input_close,
+	.trigger =	snd_wavefront_midi_input_trigger,
 };
 
diff -Nru a/sound/isa/wavefront/wavefront_synth.c b/sound/isa/wavefront/wavefront_synth.c
--- a/sound/isa/wavefront/wavefront_synth.c	Mon Oct  7 11:25:32 2002
+++ b/sound/isa/wavefront/wavefront_synth.c	Mon Oct  7 11:25:32 2002
@@ -20,7 +20,6 @@
  *
  */
 
-#define __NO_VERSION__
 #include <sound/driver.h>
 #include <asm/io.h>
 #include <linux/interrupt.h>
@@ -1706,9 +1705,11 @@
 		return;
 	}
 
+	spin_lock(&dev->irq_lock);
 	dev->irq_ok = 1;
 	dev->irq_cnt++;
-	wake_up_interruptible (&dev->interrupt_sleeper);
+	spin_unlock(&dev->irq_lock);
+	wake_up(&dev->interrupt_sleeper);
 }
 
 /* STATUS REGISTER 
@@ -1756,14 +1757,20 @@
 				  int val, int port, int timeout)
 
 {
-	unsigned long flags;
+	wait_queue_t wait;
 
-	save_flags (flags);
-	cli();
+	init_waitqueue_entry(&wait, current);
+	spin_lock_irq(&dev->irq_lock);
+	add_wait_queue(&dev->interrupt_sleeper, &wait);
 	dev->irq_ok = 0;
 	outb (val,port);
-	interruptible_sleep_on_timeout (&dev->interrupt_sleeper, timeout);
-	restore_flags (flags);
+	spin_unlock_irq(&dev->irq_lock);
+	while (1) {
+		if ((timeout = schedule_timeout(timeout)) == 0)
+			return;
+		if (dev->irq_ok)
+			return;
+	}
 }
 
 static int __init
diff -Nru a/sound/oss/maestro3.c b/sound/oss/maestro3.c
--- a/sound/oss/maestro3.c	Mon Oct  7 11:25:38 2002
+++ b/sound/oss/maestro3.c	Mon Oct  7 11:25:38 2002
@@ -28,6 +28,9 @@
  * Shouts go out to Mike "DJ XPCom" Ang.
  *
  * History
+ *  v1.23 - Jun 5 2002 - Michael Olson <olson@cs.odu.edu>
+ *   added a module option to allow selection of GPIO pin number 
+ *   for external amp 
  *  v1.22 - Feb 28 2001 - Zach Brown <zab@zabbo.net>
  *   allocate mem at insmod/setup, rather than open
  *   limit pci dma addresses to 28bit, thanks guys.
@@ -153,7 +156,7 @@
 
 #define M_DEBUG 1
 
-#define DRIVER_VERSION      "1.22"
+#define DRIVER_VERSION      "1.23"
 #define M3_MODULE_NAME      "maestro3"
 #define PFX                 M3_MODULE_NAME ": "
 
@@ -190,6 +193,7 @@
 };
 
 int external_amp = 1;
+int gpio_pin = -1;
 
 struct m3_state {
     unsigned int magic;
@@ -2472,14 +2476,20 @@
     if(!external_amp)
         return;
 
-    switch (card->card_type) {
-        case ESS_ALLEGRO:
-            polarity_port = 0x1800;
-            break;
-        default:
-            /* presumably this is for all 'maestro3's.. */
-            polarity_port = 0x1100;
-            break;
+    if (gpio_pin >= 0  && gpio_pin <= 15) {
+        polarity_port = 0x1000 + (0x100 * gpio_pin);
+    } else {
+        switch (card->card_type) {
+            case ESS_ALLEGRO:
+                polarity_port = 0x1800;
+                break;
+            default:
+                polarity_port = 0x1100;
+                /* Panasonic toughbook CF72 has to be different... */
+                if(card->pcidev->subsystem_vendor == 0x10F7 && card->pcidev->subsystem_device == 0x833D)
+                	polarity_port = 0x1D00;
+                break;
+        }
     }
 
     gpo = (polarity_port >> 8) & 0x0F;
@@ -2912,6 +2922,7 @@
 MODULE_PARM(debug,"i");
 #endif
 MODULE_PARM(external_amp,"i");
+MODULE_PARM(gpio_pin, "i");
 
 static struct pci_driver m3_pci_driver = {
     name:       "ess_m3_audio",
diff -Nru a/sound/pci/Config.help b/sound/pci/Config.help
--- a/sound/pci/Config.help	Mon Oct  7 11:25:27 2002
+++ b/sound/pci/Config.help	Mon Oct  7 11:25:27 2002
@@ -82,8 +82,5 @@
 CONFIG_SND_SONICVIBES
   Say 'Y' or 'M' to include support for S3 SonicVibes based soundcards.
 
-CONFIG_SND_VIA686
-  Say 'Y' or 'M' to include support for VIA VT82C686A/B South Bridge.
-
-CONFIG_SND_VIA8233
-  Say 'Y' or 'M' to include support for VIA VT8233 South Bridge.
+CONFIG_SND_VIA82XX
+  Say 'Y' or 'M' to include support for VIA VT82C686A/B, VT8233 South Bridge.
diff -Nru a/sound/pci/Config.in b/sound/pci/Config.in
--- a/sound/pci/Config.in	Mon Oct  7 11:25:32 2002
+++ b/sound/pci/Config.in	Mon Oct  7 11:25:32 2002
@@ -6,8 +6,8 @@
 dep_tristate 'ALi PCI Audio M5451' CONFIG_SND_ALI5451 $CONFIG_SND
 dep_tristate 'Cirrus Logic (Sound Fusion) CS4280/CS461x/CS462x/CS463x' CONFIG_SND_CS46XX $CONFIG_SND
 dep_mbool '  Cirrus Logic (Sound Fusion) New DSP support (EXPERIMENTAL)' CONFIG_SND_CS46XX_NEW_DSP $CONFIG_SND_CS46XX $CONFIG_EXPERIMENTAL
-dep_tristate 'Cirrus Logic CS4281' CONFIG_SND_CS4281 $CONFIG_SND
-dep_tristate 'EMU10K1 (SB Live!, E-mu APS)' CONFIG_SND_EMU10K1 $CONFIG_SND
+dep_tristate 'Cirrus Logic (Sound Fusion) CS4281' CONFIG_SND_CS4281 $CONFIG_SND
+dep_tristate 'EMU10K1 (SB Live! & Audigy, 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
@@ -25,10 +25,9 @@
 dep_tristate 'ESS Allegro/Maestro3' CONFIG_SND_MAESTRO3 $CONFIG_SND
 dep_tristate 'ForteMedia FM801' CONFIG_SND_FM801 $CONFIG_SND
 dep_tristate 'ICEnsemble ICE1712 (Envy24)' CONFIG_SND_ICE1712 $CONFIG_SND
-dep_tristate 'Intel i810/i820/i830/i840/MX440 integrated audio' CONFIG_SND_INTEL8X0 $CONFIG_SND
+dep_tristate 'Intel i8x0/MX440, SiS 7012; Ali 5455; NForce Audio; AMD768/8111' CONFIG_SND_INTEL8X0 $CONFIG_SND
 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
+dep_tristate 'VIA 82C686A/B, 8233 South Bridge' CONFIG_SND_VIA82XX $CONFIG_SND
 
 # define gameport if necessary
 if [ "$CONFIG_INPUT_GAMEPORT" != "n" ]; then
diff -Nru a/sound/pci/Makefile b/sound/pci/Makefile
--- a/sound/pci/Makefile	Mon Oct  7 11:25:25 2002
+++ b/sound/pci/Makefile	Mon Oct  7 11:25:25 2002
@@ -11,14 +11,12 @@
 snd-es1938-objs := es1938.o
 snd-es1968-objs := es1968.o
 snd-fm801-objs := fm801.o
-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
-snd-via8233-objs := via8233.o
+snd-via82xx-objs := via82xx.o
 
 # Toplevel Module Dependency
 obj-$(CONFIG_SND_ALS4000) += snd-als4000.o
@@ -29,15 +27,13 @@
 obj-$(CONFIG_SND_ES1938) += snd-es1938.o
 obj-$(CONFIG_SND_ES1968) += snd-es1968.o
 obj-$(CONFIG_SND_FM801) += snd-fm801.o
-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
-obj-$(CONFIG_SND_VIA8233) += snd-via8233.o
+obj-$(CONFIG_SND_VIA82XX) += snd-via82xx.o
 
-obj-$(CONFIG_SND) += ac97/ ali5451/ cs46xx/ emu10k1/ korg1212/ nm256/ rme9652/ trident/ ymfpci/
+obj-$(CONFIG_SND) += ac97/ ali5451/ cs46xx/ emu10k1/ korg1212/ nm256/ rme9652/ trident/ ymfpci/ ice1712/
 
 include $(TOPDIR)/Rules.make
diff -Nru a/sound/pci/ac97/Makefile b/sound/pci/ac97/Makefile
--- a/sound/pci/ac97/Makefile	Mon Oct  7 11:25:25 2002
+++ b/sound/pci/ac97/Makefile	Mon Oct  7 11:25:25 2002
@@ -17,8 +17,7 @@
 obj-$(CONFIG_SND_ICE1712) += snd-ac97-codec.o
 obj-$(CONFIG_SND_INTEL8X0) += snd-ac97-codec.o
 obj-$(CONFIG_SND_MAESTRO3) += snd-ac97-codec.o
-obj-$(CONFIG_SND_VIA686) += snd-ac97-codec.o
-obj-$(CONFIG_SND_VIA8233) += snd-ac97-codec.o
+obj-$(CONFIG_SND_VIA82XX) += snd-ac97-codec.o
 obj-$(CONFIG_SND_ALI5451) += snd-ac97-codec.o
 obj-$(CONFIG_SND_CS46XX) += snd-ac97-codec.o
 obj-$(CONFIG_SND_EMU10K1) += snd-ac97-codec.o
diff -Nru a/sound/pci/ac97/ac97_codec.c b/sound/pci/ac97/ac97_codec.c
--- a/sound/pci/ac97/ac97_codec.c	Mon Oct  7 11:25:31 2002
+++ b/sound/pci/ac97/ac97_codec.c	Mon Oct  7 11:25:31 2002
@@ -65,12 +65,16 @@
 { 0x41445300, 0xffffff00, "Analog Devices",	NULL },
 { 0x414c4300, 0xffffff00, "Realtek",		NULL },
 { 0x414c4700, 0xffffff00, "Avance Logic",	NULL },
+{ 0x434d4900, 0xffffff00, "C-Media Electronics", NULL },
 { 0x43525900, 0xffffff00, "Cirrus Logic",	NULL },
 { 0x43585400, 0xffffff00, "Conexant",           NULL },
+{ 0x454d4300, 0xffffff00, "eMicro",		NULL },
 { 0x45838300, 0xffffff00, "ESS Technology",	NULL },
 { 0x48525300, 0xffffff00, "Intersil",		NULL },
 { 0x49434500, 0xffffff00, "ICEnsemble",		NULL },
+{ 0x49544500, 0xffffff00, "ITE Tech.Inc",	NULL },
 { 0x4e534300, 0xffffff00, "National Semiconductor", NULL },
+{ 0x50534300, 0xffffff00, "Philips",		NULL },
 { 0x53494c00, 0xffffff00, "Silicon Laboratory",	NULL },
 { 0x54524100, 0xffffff00, "TriTech",		NULL },
 { 0x54584e00, 0xffffff00, "Texas Instruments",	NULL },
@@ -94,6 +98,8 @@
 { 0x41445360, 0xffffffff, "AD1885",		patch_ad1885 },
 { 0x41445361, 0xffffffff, "AD1886",		patch_ad1886 },
 { 0x41445362, 0xffffffff, "AD1887",		patch_ad1881 },
+{ 0x41445363, 0xffffffff, "AD1886A",		patch_ad1881 },
+{ 0x41445370, 0xffffffff, "AD1980",		patch_ad1980 },
 { 0x41445372, 0xffffffff, "AD1981A",		patch_ad1881 },
 { 0x414c4300, 0xfffffff0, "RL5306",	 	NULL },
 { 0x414c4310, 0xfffffff0, "RL5382", 		NULL },
@@ -103,6 +109,8 @@
 { 0x414c4730, 0xffffffff, "ALC101",		NULL },
 { 0x414c4740, 0xfffffff0, "ALC202",		NULL },
 { 0x414c4750, 0xfffffff0, "ALC250",		NULL },
+{ 0x434d4941, 0xffffffff, "CMI9738",		NULL },
+{ 0x434d4961, 0xffffffff, "CMI9739",		NULL },
 { 0x43525900, 0xfffffff8, "CS4297",		NULL },
 { 0x43525910, 0xfffffff8, "CS4297A",		patch_cirrus_spdif },
 { 0x43525920, 0xfffffff8, "CS4294/4298",	NULL },
@@ -111,15 +119,21 @@
 { 0x43525948, 0xfffffff8, "CS4201",		NULL },
 { 0x43525958, 0xfffffff8, "CS4205",		patch_cirrus_spdif },
 { 0x43525960, 0xfffffff8, "CS4291",		NULL },
-{ 0x43585429, 0xffffffff, "Cx20468",		patch_conexant },
+{ 0x43585421, 0xffffffff, "HSD11246",		NULL },	// SmartMC II
+{ 0x43585428, 0xfffffff8, "Cx20468",		patch_conexant }, // SmartAMC fixme: the mask might be different
+{ 0x454d4328, 0xffffffff, "28028",		NULL },  // same as TR28028?
 { 0x45838308, 0xffffffff, "ESS1988",		NULL },
 { 0x48525300, 0xffffff00, "HMP9701",		NULL },
 { 0x49434501, 0xffffffff, "ICE1230",		NULL },
 { 0x49434511, 0xffffffff, "ICE1232",		NULL }, // alias VIA VT1611A?
+{ 0x49544520, 0xffffffff, "IT2226E",		NULL },
 { 0x4e534300, 0xffffffff, "LM4540/43/45/46/48",	NULL }, // only guess --jk
 { 0x4e534331, 0xffffffff, "LM4549",		NULL },
+{ 0x50534304, 0xffffffff, "UCB1400",		NULL },
 { 0x53494c22, 0xffffffff, "Si3036",		NULL },
 { 0x53494c23, 0xffffffff, "Si3038",		NULL },
+{ 0x54524102, 0xffffffff, "TR28022",		NULL },
+{ 0x54524106, 0xffffffff, "TR28026",		NULL },
 { 0x54524108, 0xffffffff, "TR28028",		patch_tritech_tr28028 }, // added by xin jin [07/09/99]
 { 0x54524123, 0xffffffff, "TR28602",		NULL }, // only guess --jk [TR28023 = eMicro EM28023 (new CT1297)]
 { 0x54584e20, 0xffffffff, "TLC320AD9xC",	NULL },
@@ -128,14 +142,19 @@
 { 0x574d4c00, 0xffffffff, "WM9701A",		patch_wolfson00 },
 { 0x574d4c03, 0xffffffff, "WM9703/9707",	patch_wolfson03 },
 { 0x574d4c04, 0xffffffff, "WM9704 (quad)",	patch_wolfson04 },
+{ 0x574d4c05, 0xffffffff, "WM9705",		NULL },	// patch?
 { 0x594d4800, 0xffffffff, "YMF743",		NULL },
+{ 0x594d4802, 0xffffffff, "YMF752",		NULL },
+{ 0x594d4803, 0xffffffff, "YMF753",		NULL },
 { 0x83847600, 0xffffffff, "STAC9700/83/84",	NULL },
 { 0x83847604, 0xffffffff, "STAC9701/3/4/5",	NULL },
 { 0x83847605, 0xffffffff, "STAC9704",		NULL },
 { 0x83847608, 0xffffffff, "STAC9708/11",	patch_sigmatel_stac9708 },
 { 0x83847609, 0xffffffff, "STAC9721/23",	patch_sigmatel_stac9721 },
 { 0x83847644, 0xffffffff, "STAC9744",		patch_sigmatel_stac9744 },
+{ 0x83847650, 0xffffffff, "STAC9750/51",	NULL },	// patch?
 { 0x83847656, 0xffffffff, "STAC9756/57",	patch_sigmatel_stac9756 },
+{ 0x83847666, 0xffffffff, "STAC9766/67",	NULL }, // patch?
 { 0, 	      0,	  NULL,			NULL }
 };
 
@@ -181,6 +200,9 @@
 
 static int snd_ac97_valid_reg(ac97_t *ac97, unsigned short reg)
 {
+	if (ac97->limited_regs && ! test_bit(reg, ac97->reg_accessed))
+  		return 0;
+
 	/* filter some registers for buggy codecs */
 	switch (ac97->id) {
 	case AC97_ID_AK4540:
@@ -196,6 +218,7 @@
 		return 1;
 	case AC97_ID_AD1885:	/* AD1885 */
 	case AC97_ID_AD1886:	/* AD1886 */
+	case AC97_ID_AD1886A:	/* AD1886A - !!verify!! --jk */
 	case AC97_ID_AD1887:	/* AD1887 - !!verify!! --jk */
 		if (reg == 0x5a)
 			return 1;
@@ -242,8 +265,6 @@
 
 static void snd_ac97_write_cache_test(ac97_t *ac97, unsigned short reg, unsigned short value)
 {
-	if (ac97->limited_regs && ! test_bit(reg, ac97->reg_accessed))
-  		return;
 #if 0
 	if (!snd_ac97_valid_reg(ac97, reg))
 		return;
@@ -273,14 +294,13 @@
 	return change;
 }
 
-int snd_ac97_update_bits(ac97_t *ac97, unsigned short reg, unsigned short mask, unsigned short value)
+int snd_ac97_update_bits_nolock(ac97_t *ac97, unsigned short reg, unsigned short mask, unsigned short value)
 {
 	int change;
 	unsigned short old, new;
 
 	if (!snd_ac97_valid_reg(ac97, reg))
 		return -EINVAL;
-	spin_lock(&ac97->reg_lock);
 	old = ac97->regs[reg];
 	new = (old & ~mask) | value;
 	change = old != new;
@@ -288,6 +308,14 @@
 		ac97->write(ac97, reg, new);
 		ac97->regs[reg] = new;
 	}
+	return change;
+}
+
+int snd_ac97_update_bits(ac97_t *ac97, unsigned short reg, unsigned short mask, unsigned short value)
+{
+	int change;
+	spin_lock(&ac97->reg_lock);
+	change = snd_ac97_update_bits_nolock(ac97, reg, mask, value);
 	spin_unlock(&ac97->reg_lock);
 	return change;
 }
@@ -361,9 +389,9 @@
 }
 
 #define AC97_ENUM_DOUBLE(xname, reg, shift, invert) \
-{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, info: snd_ac97_info_enum_double, \
-  get: snd_ac97_get_enum_double, put: snd_ac97_put_enum_double, \
-  private_value: reg | (shift << 8) | (invert << 24) }
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .info = snd_ac97_info_enum_double, \
+  .get = snd_ac97_get_enum_double, .put = snd_ac97_put_enum_double, \
+  .private_value = reg | (shift << 8) | (invert << 24) }
 
 static int snd_ac97_info_enum_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
 {
@@ -423,9 +451,9 @@
 }
 
 #define AC97_SINGLE(xname, reg, shift, mask, invert) \
-{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, info: snd_ac97_info_single, \
-  get: snd_ac97_get_single, put: snd_ac97_put_single, \
-  private_value: reg | (shift << 8) | (mask << 16) | (invert << 24) }
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .info = snd_ac97_info_single, \
+  .get = snd_ac97_get_single, .put = snd_ac97_put_single, \
+  .private_value = reg | (shift << 8) | (mask << 16) | (invert << 24) }
 
 static int snd_ac97_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
 {
@@ -468,9 +496,9 @@
 }
 
 #define AC97_DOUBLE(xname, reg, shift_left, shift_right, mask, invert) \
-{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: (xname), info: snd_ac97_info_double, \
-  get: snd_ac97_get_double, put: snd_ac97_put_double, \
-  private_value: reg | (shift_left << 8) | (shift_right << 12) | (mask << 16) | (invert << 24) }
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), .info = snd_ac97_info_double, \
+  .get = snd_ac97_get_double, .put = snd_ac97_put_double, \
+  .private_value = reg | (shift_left << 8) | (shift_right << 12) | (mask << 16) | (invert << 24) }
 
 static int snd_ac97_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
 {
@@ -587,11 +615,11 @@
 
 static const snd_kcontrol_new_t snd_ac97_controls_capture[3] = {
 {
-	iface: SNDRV_CTL_ELEM_IFACE_MIXER,
-	name: "Capture Source",
-	info: snd_ac97_info_mux,
-	get: snd_ac97_get_mux,
-	put: snd_ac97_put_mux,
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "Capture Source",
+	.info = snd_ac97_info_mux,
+	.get = snd_ac97_get_mux,
+	.put = snd_ac97_put_mux,
 },
 AC97_SINGLE("Capture Switch", AC97_REC_GAIN, 15, 1, 1),
 AC97_DOUBLE("Capture Volume", AC97_REC_GAIN, 8, 0, 15, 0)
@@ -737,16 +765,16 @@
 		case 2: x = 0; break;  // 48.0
 		default: x = 0; break; // illegal.
 		}
-		change = snd_ac97_update_bits(ac97, AC97_CSR_SPDIF, 0x3fff, ((val & 0xcfff) | (x << 12)));
+		change = snd_ac97_update_bits_nolock(ac97, AC97_CSR_SPDIF, 0x3fff, ((val & 0xcfff) | (x << 12)));
 	} else if (ac97->flags & AC97_CX_SPDIF) {
 		int v;
 		v = ucontrol->value.iec958.status[0] & (IEC958_AES0_CON_EMPHASIS_5015|IEC958_AES0_CON_NOT_COPYRIGHT) ? 0 : AC97_CXR_COPYRGT;
 		v |= ucontrol->value.iec958.status[0] & IEC958_AES0_NONAUDIO ? AC97_CXR_SPDIF_AC3 : AC97_CXR_SPDIF_PCM;
-		change = snd_ac97_update_bits(ac97, AC97_CXR_AUDIO_MISC, 
-					      AC97_CXR_SPDIF_MASK | AC97_CXR_COPYRGT,
-					      v);
+		change = snd_ac97_update_bits_nolock(ac97, AC97_CXR_AUDIO_MISC, 
+						     AC97_CXR_SPDIF_MASK | AC97_CXR_COPYRGT,
+						     v);
 	} else {
-		change = snd_ac97_update_bits(ac97, AC97_SPDIF, 0x3fff, val);
+		change = snd_ac97_update_bits_nolock(ac97, AC97_SPDIF, 0x3fff, val);
 	}
 
 	change |= ac97->spdif_status != new;
@@ -757,25 +785,25 @@
 
 static const snd_kcontrol_new_t snd_ac97_controls_spdif[5] = {
 	{
-		access: SNDRV_CTL_ELEM_ACCESS_READ,
-		iface: SNDRV_CTL_ELEM_IFACE_MIXER,
-		name: SNDRV_CTL_NAME_IEC958("",PLAYBACK,CON_MASK),
-		info: snd_ac97_spdif_mask_info,
-		get: snd_ac97_spdif_cmask_get,
+		.access = SNDRV_CTL_ELEM_ACCESS_READ,
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,CON_MASK),
+		.info = snd_ac97_spdif_mask_info,
+		.get = snd_ac97_spdif_cmask_get,
 	},
 	{
-		access: SNDRV_CTL_ELEM_ACCESS_READ,
-		iface: SNDRV_CTL_ELEM_IFACE_MIXER,
-		name: SNDRV_CTL_NAME_IEC958("",PLAYBACK,PRO_MASK),
-		info: snd_ac97_spdif_mask_info,
-		get: snd_ac97_spdif_pmask_get,
+		.access = SNDRV_CTL_ELEM_ACCESS_READ,
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,PRO_MASK),
+		.info = snd_ac97_spdif_mask_info,
+		.get = snd_ac97_spdif_pmask_get,
 	},
 	{
-		iface: SNDRV_CTL_ELEM_IFACE_MIXER,
-		name: SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
-		info: snd_ac97_spdif_mask_info,
-		get: snd_ac97_spdif_default_get,
-		put: snd_ac97_spdif_default_put,
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
+		.info = snd_ac97_spdif_mask_info,
+		.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),
@@ -792,9 +820,9 @@
 };
 
 #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, \
-  private_value: codec | (shift << 8) | (mask << 16) }
+{ .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, \
+  .private_value = codec | (shift << 8) | (mask << 16) }
 
 static int snd_ac97_ad18xx_pcm_info_bits(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
 {
@@ -831,9 +859,9 @@
 }
 
 #define AD18XX_PCM_VOLUME(xname, codec) \
-{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, info: snd_ac97_ad18xx_pcm_info_volume, \
-  get: snd_ac97_ad18xx_pcm_get_volume, put: snd_ac97_ad18xx_pcm_put_volume, \
-  private_value: codec }
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .info = snd_ac97_ad18xx_pcm_info_volume, \
+  .get = snd_ac97_ad18xx_pcm_get_volume, .put = snd_ac97_ad18xx_pcm_put_volume, \
+  .private_value = codec }
 
 static int snd_ac97_ad18xx_pcm_info_volume(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
 {
@@ -911,7 +939,7 @@
 {
 	unsigned short val, mask = 0x8000;
 
-	if (ac97->limited_regs && ! test_bit(reg, ac97->reg_accessed))
+	if (! snd_ac97_valid_reg(ac97, reg))
 		return 0;
 
 	switch (reg) {
@@ -1452,7 +1480,7 @@
 	char name[64];
 	signed long end_time;
 	static snd_device_ops_t ops = {
-		dev_free:	snd_ac97_dev_free,
+		.dev_free =	snd_ac97_dev_free,
 	};
 
 	snd_assert(rac97 != NULL, return -EINVAL);
@@ -1481,76 +1509,109 @@
 		/* the REC_GAIN register is used for tests */
 		end_time = jiffies + HZ;
 		do {
+			unsigned short ext_mid;
+		
 			/* use preliminary reads to settle the communication */
 			snd_ac97_read(ac97, AC97_RESET);
 			snd_ac97_read(ac97, AC97_VENDOR_ID1);
 			snd_ac97_read(ac97, AC97_VENDOR_ID2);
-			/* test if we can write to the PCM volume register */
+			/* modem? */
+			ext_mid = snd_ac97_read(ac97, AC97_EXTENDED_MID);
+			if (ext_mid != 0xffff && (ext_mid & 1) != 0)
+				goto __access_ok;
+			/* test if we can write to the record gain volume register */
 			snd_ac97_write_cache(ac97, AC97_REC_GAIN, 0x8a05);
 			if ((err = snd_ac97_read(ac97, AC97_REC_GAIN)) == 0x8a05)
 				goto __access_ok;
 			set_current_state(TASK_UNINTERRUPTIBLE);
 			schedule_timeout(HZ/100);
-		} while (end_time - (signed long)jiffies >= 0);
-		snd_printd("AC'97 %d:%d does not respond - RESET [REC_GAIN = 0x%x]\n", ac97->num, ac97->addr, err);
+		} while (time_after_eq(end_time, jiffies));
+		snd_printk("AC'97 %d:%d does not respond - RESET [REC_GAIN = 0x%x]\n", ac97->num, ac97->addr, err);
 		snd_ac97_free(ac97);
 		return -ENXIO;
 	}
       __access_ok:
-	ac97->caps = snd_ac97_read(ac97, AC97_RESET);
 	ac97->id = snd_ac97_read(ac97, AC97_VENDOR_ID1) << 16;
 	ac97->id |= snd_ac97_read(ac97, AC97_VENDOR_ID2);
-	ac97->ext_id = snd_ac97_read(ac97, AC97_EXTENDED_ID);
-	if (ac97->ext_id == 0xffff)	/* invalid combination */
-		ac97->ext_id = 0;
 	if (ac97->id == 0x00000000 || ac97->id == 0xffffffff) {
 		snd_printk("AC'97 %d:%d access is not valid [0x%x], removing mixer.\n", ac97->num, ac97->addr, ac97->id);
 		snd_ac97_free(ac97);
 		return -EIO;
 	}
+	
+	/* test for AC'97 */
+	/* test if we can write to the record gain volume register */
+	snd_ac97_write_cache(ac97, AC97_REC_GAIN, 0x8a06);
+	if ((err = snd_ac97_read(ac97, AC97_REC_GAIN)) == 0x8a06) {
+		ac97->scaps |= AC97_SCAP_AUDIO;
+		ac97->caps = snd_ac97_read(ac97, AC97_RESET);
+		ac97->ext_id = snd_ac97_read(ac97, AC97_EXTENDED_ID);
+		if (ac97->ext_id == 0xffff)	/* invalid combination */
+			ac97->ext_id = 0;
+	}
+
+	/* test for MC'97 */
+	ac97->ext_mid = snd_ac97_read(ac97, AC97_EXTENDED_MID);
+	if (ac97->ext_mid == 0xffff)	/* invalid combination */
+		ac97->ext_mid = 0;
+	if (ac97->ext_mid & 1)
+		ac97->scaps |= AC97_SCAP_MODEM;
 
 	if (ac97->reset) // FIXME: always skipping?
 		goto __ready_ok;
 
 	/* FIXME: add powerdown control */
 	/* nothing should be in powerdown mode */
-	snd_ac97_write_cache_test(ac97, AC97_POWERDOWN, 0);
-	snd_ac97_write_cache_test(ac97, AC97_RESET, 0);		/* reset to defaults */
-	udelay(100);
-	/* nothing should be in powerdown mode */
-	snd_ac97_write_cache_test(ac97, AC97_POWERDOWN, 0);
-	snd_ac97_write_cache_test(ac97, AC97_GENERAL_PURPOSE, 0);
-	end_time = jiffies + (HZ / 10);
-	do {
-		if ((snd_ac97_read(ac97, AC97_POWERDOWN) & 0x0f) == 0x0f)
-			goto __ready_ok;
-		set_current_state(TASK_UNINTERRUPTIBLE);
-		schedule_timeout(HZ/10);
-	} while (end_time - (signed long)jiffies >= 0);
-	snd_printk("AC'97 %d:%d analog subsections not ready\n", ac97->num, ac97->addr);
+	if (ac97->scaps & AC97_SCAP_AUDIO) {
+		snd_ac97_write_cache_test(ac97, AC97_POWERDOWN, 0);
+		snd_ac97_write_cache_test(ac97, AC97_RESET, 0);		/* reset to defaults */
+		udelay(100);
+		/* nothing should be in powerdown mode */
+		snd_ac97_write_cache_test(ac97, AC97_POWERDOWN, 0);
+		snd_ac97_write_cache_test(ac97, AC97_GENERAL_PURPOSE, 0);
+		end_time = jiffies + (HZ / 10);
+		do {
+			if ((snd_ac97_read(ac97, AC97_POWERDOWN) & 0x0f) == 0x0f)
+				goto __ready_ok;
+			set_current_state(TASK_UNINTERRUPTIBLE);
+			schedule_timeout(HZ/10);
+		} while (time_after_eq(end_time, jiffies));
+		snd_printk("AC'97 %d:%d analog subsections not ready\n", ac97->num, ac97->addr);
+	}
+
       __ready_ok:
 	if (ac97->clock == 0)
 		ac97->clock = 48000;	/* standard value */
+	if (ac97->scaps & AC97_SCAP_AUDIO)
+		ac97->addr = (ac97->ext_id & AC97_EI_ADDR_MASK) >> AC97_EI_ADDR_SHIFT;
+	else
+		ac97->addr = (ac97->ext_mid & AC97_MEI_ADDR_MASK) >> AC97_MEI_ADDR_SHIFT;
 	if (ac97->ext_id & 0x0189)	/* L/R, MIC, SDAC, LDAC VRA support */
 		snd_ac97_write_cache(ac97, AC97_EXTENDED_STATUS, ac97->ext_id & 0x0189);
-	if (ac97->ext_id & 0x0001) {	/* VRA support */
-		snd_ac97_determine_rates(ac97, AC97_PCM_FRONT_DAC_RATE, &ac97->rates_front_dac);
-		snd_ac97_determine_rates(ac97, AC97_PCM_LR_ADC_RATE, &ac97->rates_adc);
+	if (ac97->ext_id & AC97_EI_VRA) {	/* VRA support */
+		snd_ac97_determine_rates(ac97, AC97_PCM_FRONT_DAC_RATE, &ac97->rates[AC97_RATES_FRONT_DAC]);
+		snd_ac97_determine_rates(ac97, AC97_PCM_LR_ADC_RATE, &ac97->rates[AC97_RATES_ADC]);
 	} else {
-		ac97->rates_front_dac = SNDRV_PCM_RATE_48000;
-		ac97->rates_adc = SNDRV_PCM_RATE_48000;
+		ac97->rates[AC97_RATES_FRONT_DAC] = SNDRV_PCM_RATE_48000;
+		ac97->rates[AC97_RATES_ADC] = SNDRV_PCM_RATE_48000;
 	}
-	if (ac97->ext_id & 0x0008) {	/* MIC VRA support */
-		snd_ac97_determine_rates(ac97, AC97_PCM_MIC_ADC_RATE, &ac97->rates_mic_adc);
+	if (ac97->ext_id & AC97_EI_SPDIF) {
+		/* codec specific code (patch) should override these values */
+		ac97->rates[AC97_RATES_SPDIF] = SNDRV_PCM_RATE_48000 |
+						SNDRV_PCM_RATE_44100 |
+						SNDRV_PCM_RATE_32000;
+	}
+	if (ac97->ext_id & AC97_EI_VRM) {	/* MIC VRA support */
+		snd_ac97_determine_rates(ac97, AC97_PCM_MIC_ADC_RATE, &ac97->rates[AC97_RATES_MIC_ADC]);
 	} else {
-		ac97->rates_mic_adc = SNDRV_PCM_RATE_48000;
+		ac97->rates[AC97_RATES_MIC_ADC] = SNDRV_PCM_RATE_48000;
 	}
-	if (ac97->ext_id & 0x0080) {	/* SDAC support */
-		snd_ac97_determine_rates(ac97, AC97_PCM_SURR_DAC_RATE, &ac97->rates_surr_dac);
+	if (ac97->ext_id & AC97_EI_SDAC) {	/* SDAC support */
+		snd_ac97_determine_rates(ac97, AC97_PCM_SURR_DAC_RATE, &ac97->rates[AC97_RATES_SURR_DAC]);
 		ac97->scaps |= AC97_SCAP_SURROUND_DAC;
 	}
-	if (ac97->ext_id & 0x0100) {	/* LDAC support */
-		snd_ac97_determine_rates(ac97, AC97_PCM_LFE_DAC_RATE, &ac97->rates_lfe_dac);
+	if (ac97->ext_id & AC97_EI_LDAC) {	/* LDAC support */
+		snd_ac97_determine_rates(ac97, AC97_PCM_LFE_DAC_RATE, &ac97->rates[AC97_RATES_LFE_DAC]);
 		ac97->scaps |= AC97_SCAP_CENTER_LFE_DAC;
 	}
 	/* additional initializations */
@@ -1566,9 +1627,17 @@
 			strcat(card->mixername, name);
 		}
 	}
-	if ((err = snd_component_add(card, "AC97")) < 0) {
-		snd_ac97_free(ac97);
-		return err;
+	if (ac97->scaps & AC97_SCAP_AUDIO) {
+		if ((err = snd_component_add(card, "AC97a")) < 0) {
+			snd_ac97_free(ac97);
+			return err;
+		}
+	}
+	if (ac97->scaps & AC97_SCAP_MODEM) {
+		if ((err = snd_component_add(card, "AC97m")) < 0) {
+			snd_ac97_free(ac97);
+			return err;
+		}
 	}
 	if (snd_ac97_mixer_build(card, ac97) < 0) {
 		snd_ac97_free(ac97);
@@ -1591,7 +1660,7 @@
 {
 	char name[64];
 	unsigned int id;
-	unsigned short val, tmp, ext;
+	unsigned short val, tmp, ext, mext;
 	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" };
@@ -1600,26 +1669,29 @@
 	id |= snd_ac97_read(ac97, AC97_VENDOR_ID2);
 	snd_ac97_get_name(NULL, id, name);
 	snd_iprintf(buffer, "%d-%d/%d: %s\n\n", ac97->addr, ac97->num, subidx, name);
+	if ((ac97->scaps & AC97_SCAP_AUDIO) == 0)
+		goto __modem;
+
 	val = snd_ac97_read(ac97, AC97_RESET);
 	snd_iprintf(buffer, "Capabilities     :%s%s%s%s%s%s\n",
-	    	    val & 0x0001 ? " -dedicated MIC PCM IN channel-" : "",
-		    val & 0x0002 ? " -reserved1-" : "",
-		    val & 0x0004 ? " -bass & treble-" : "",
-		    val & 0x0008 ? " -simulated stereo-" : "",
-		    val & 0x0010 ? " -headphone out-" : "",
-		    val & 0x0020 ? " -loudness-" : "");
-	tmp = ac97->caps & 0x00c0;
+	    	    val & AC97_BC_DEDICATED_MIC ? " -dedicated MIC PCM IN channel-" : "",
+		    val & AC97_BC_RESERVED1 ? " -reserved1-" : "",
+		    val & AC97_BC_BASS_TREBLE ? " -bass & treble-" : "",
+		    val & AC97_BC_SIM_STEREO ? " -simulated stereo-" : "",
+		    val & AC97_BC_HEADPHONE ? " -headphone out-" : "",
+		    val & AC97_BC_LOUDNESS ? " -loudness-" : "");
+	tmp = ac97->caps & AC97_BC_DAC_MASK;
 	snd_iprintf(buffer, "DAC resolution   : %s%s%s%s\n",
-		    tmp == 0x0000 ? "16-bit" : "",
-		    tmp == 0x0040 ? "18-bit" : "",
-		    tmp == 0x0080 ? "20-bit" : "",
-		    tmp == 0x00c0 ? "???" : "");
-	tmp = ac97->caps & 0x0300;
+		    tmp == AC97_BC_16BIT_DAC ? "16-bit" : "",
+		    tmp == AC97_BC_18BIT_DAC ? "18-bit" : "",
+		    tmp == AC97_BC_20BIT_DAC ? "20-bit" : "",
+		    tmp == AC97_BC_DAC_MASK ? "???" : "");
+	tmp = ac97->caps & AC97_BC_ADC_MASK;
 	snd_iprintf(buffer, "ADC resolution   : %s%s%s%s\n",
-		    tmp == 0x0000 ? "16-bit" : "",
-		    tmp == 0x0100 ? "18-bit" : "",
-		    tmp == 0x0200 ? "20-bit" : "",
-		    tmp == 0x0300 ? "???" : "");
+		    tmp == AC97_BC_16BIT_ADC ? "16-bit" : "",
+		    tmp == AC97_BC_18BIT_ADC ? "18-bit" : "",
+		    tmp == AC97_BC_20BIT_ADC ? "20-bit" : "",
+		    tmp == AC97_BC_ADC_MASK ? "???" : "");
 	snd_iprintf(buffer, "3D enhancement   : %s\n",
 		snd_ac97_stereo_enhancements[(val >> 10) & 0x1f]);
 	snd_iprintf(buffer, "\nCurrent setup\n");
@@ -1640,79 +1712,93 @@
 		    val & 0x0200 ? "Mic" : "MIX",
 		    val & 0x0100 ? "Mic2" : "Mic1",
 		    val & 0x0080 ? "on" : "off");
-	ext = snd_ac97_read(ac97, AC97_EXTENDED_ID);
 
+	ext = snd_ac97_read(ac97, AC97_EXTENDED_ID);
 	if (ext == 0)
-		return;
+		goto __modem;
+		
 	snd_iprintf(buffer, "Extended ID      : codec=%i rev=%i%s%s%s%s DSA=%i%s%s%s%s\n",
-			(ext >> 14) & 3,
-			(ext >> 10) & 3,
-			ext & 0x0200 ? " AMAP" : "",
-			ext & 0x0100 ? " LDAC" : "",
-			ext & 0x0080 ? " SDAC" : "",
-			ext & 0x0040 ? " CDAC" : "",
-			(ext >> 4) & 3,
-			ext & 0x0008 ? " VRM" : "",
-			ext & 0x0004 ? " SPDIF" : "",
-			ext & 0x0002 ? " DRA" : "",
-			ext & 0x0001 ? " VRA" : "");
+			(ext & AC97_EI_ADDR_MASK) >> AC97_EI_ADDR_SHIFT,
+			(ext & AC97_EI_REV_MASK) >> AC97_EI_REV_SHIFT,
+			ext & AC97_EI_AMAP ? " AMAP" : "",
+			ext & AC97_EI_LDAC ? " LDAC" : "",
+			ext & AC97_EI_SDAC ? " SDAC" : "",
+			ext & AC97_EI_CDAC ? " CDAC" : "",
+			(ext & AC97_EI_DACS_SLOT_MASK) >> AC97_EI_DACS_SLOT_SHIFT,
+			ext & AC97_EI_VRM ? " VRM" : "",
+			ext & AC97_EI_SPDIF ? " SPDIF" : "",
+			ext & AC97_EI_DRA ? " DRA" : "",
+			ext & AC97_EI_VRA ? " VRA" : "");
 	val = snd_ac97_read(ac97, AC97_EXTENDED_STATUS);
 	snd_iprintf(buffer, "Extended status  :%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n",
-			val & 0x4000 ? " PRL" : "",
-			val & 0x2000 ? " PRK" : "",
-			val & 0x1000 ? " PRJ" : "",
-			val & 0x0800 ? " PRI" : "",
-			val & 0x0400 ? " SPCV" : "",
-			val & 0x0200 ? " MADC" : "",
-			val & 0x0100 ? " LDAC" : "",
-			val & 0x0080 ? " SDAC" : "",
-			val & 0x0040 ? " CDAC" : "",
-			ext & 0x0004 ? spdif_slots[(val & 0x0030) >> 4] : "",
-			val & 0x0008 ? " VRM" : "",
-			val & 0x0004 ? " SPDIF" : "",
-			val & 0x0002 ? " DRA" : "",
-			val & 0x0001 ? " VRA" : "");
-	if (ext & 1) {	/* VRA */
+			val & AC97_EA_PRL ? " PRL" : "",
+			val & AC97_EA_PRK ? " PRK" : "",
+			val & AC97_EA_PRJ ? " PRJ" : "",
+			val & AC97_EA_PRI ? " PRI" : "",
+			val & AC97_EA_SPCV ? " SPCV" : "",
+			val & AC97_EA_MDAC ? " MADC" : "",
+			val & AC97_EA_LDAC ? " LDAC" : "",
+			val & AC97_EA_SDAC ? " SDAC" : "",
+			val & AC97_EA_CDAC ? " CDAC" : "",
+			ext & AC97_EI_SPDIF ? spdif_slots[(val & AC97_EA_SPSA_SLOT_MASK) >> AC97_EA_SPSA_SLOT_SHIFT] : "",
+			val & AC97_EA_VRM ? " VRM" : "",
+			val & AC97_EA_SPDIF ? " SPDIF" : "",
+			val & AC97_EA_DRA ? " DRA" : "",
+			val & AC97_EA_VRA ? " VRA" : "");
+	if (ext & AC97_EI_VRA) {	/* VRA */
 		val = snd_ac97_read(ac97, AC97_PCM_FRONT_DAC_RATE);
 		snd_iprintf(buffer, "PCM front DAC    : %iHz\n", val);
-		if (ext & 0x0080) {
+		if (ext & AC97_EI_SDAC) {
 			val = snd_ac97_read(ac97, AC97_PCM_SURR_DAC_RATE);
 			snd_iprintf(buffer, "PCM Surr DAC     : %iHz\n", val);
 		}
-		if (ext & 0x0100) {
+		if (ext & AC97_EI_LDAC) {
 			val = snd_ac97_read(ac97, AC97_PCM_LFE_DAC_RATE);
 			snd_iprintf(buffer, "PCM LFE DAC      : %iHz\n", val);
 		}
 		val = snd_ac97_read(ac97, AC97_PCM_LR_ADC_RATE);
 		snd_iprintf(buffer, "PCM ADC          : %iHz\n", val);
 	}
-	if (ext & 0x0008) {
+	if (ext & AC97_EI_VRM) {
 		val = snd_ac97_read(ac97, AC97_PCM_MIC_ADC_RATE);
 		snd_iprintf(buffer, "PCM MIC ADC      : %iHz\n", val);
 	}
-	if ((ext & 0x0004) || (ac97->flags & AC97_CS_SPDIF)) {
+	if ((ext & AC97_EI_SPDIF) || (ac97->flags & AC97_CS_SPDIF)) {
 	        if (ac97->flags & AC97_CS_SPDIF)
 			val = snd_ac97_read(ac97, AC97_CSR_SPDIF);
 		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",
-			val & 0x0004 ? " Copyright" : "",
-			val & 0x0008 ? " Preemph50/15" : "",
-			(val & 0x07f0) >> 4,
-			(val & 0x0800) >> 11,
+			val & AC97_SC_PRO ? " PRO" : " Consumer",
+			val & AC97_SC_NAUDIO ? " Non-audio" : " PCM",
+			val & AC97_SC_COPY ? " Copyright" : "",
+			val & AC97_SC_PRE ? " Preemph50/15" : "",
+			(val & AC97_SC_CC_MASK) >> AC97_SC_CC_SHIFT,
+			(val & AC97_SC_L) >> 11,
 			(ac97->flags & AC97_CS_SPDIF) ?
-			    spdif_rates_cs4205[(val & 0x3000) >> 12] :
-			    spdif_rates[(val & 0x3000) >> 12],
+			    spdif_rates_cs4205[(val & AC97_SC_SPSR_MASK) >> AC97_SC_SPSR_SHIFT] :
+			    spdif_rates[(val & AC97_SC_SPSR_MASK) >> AC97_SC_SPSR_SHIFT],
 			(ac97->flags & AC97_CS_SPDIF) ?
-			    (val & 0x4000 ? " Validity" : "") :
-			    (val & 0x4000 ? " DRS" : ""),
+			    (val & AC97_SC_DRS ? " Validity" : "") :
+			    (val & AC97_SC_DRS ? " DRS" : ""),
 			(ac97->flags & AC97_CS_SPDIF) ?
-			    (val & 0x8000 ? " Enabled" : "") :
-			    (val & 0x8000 ? " Validity" : ""));
+			    (val & AC97_SC_V ? " Enabled" : "") :
+			    (val & AC97_SC_V ? " Validity" : ""));
 	}
+
+      __modem:
+	mext = snd_ac97_read(ac97, AC97_EXTENDED_MID);
+	if (mext == 0)
+		return;
+	
+	snd_iprintf(buffer, "Extended modem ID: codec=%i%s%s%s%s%s\n",
+			(mext & AC97_MEI_ADDR_MASK) >> AC97_MEI_ADDR_SHIFT,
+			mext & AC97_MEI_CID2 ? " CID2" : "",
+			mext & AC97_MEI_CID1 ? " CID1" : "",
+			mext & AC97_MEI_HEADSET ? " HSET" : "",
+			mext & AC97_MEI_LINE2 ? " LIN2" : "",
+			mext & AC97_MEI_LINE1 ? " LIN1" : "");
 }
 
 static void snd_ac97_proc_read(snd_info_entry_t *entry, snd_info_buffer_t * buffer)
@@ -1752,10 +1838,7 @@
 	int reg, val;
 
 	for (reg = 0; reg < 0x80; reg += 2) {
-		if (ac97->limited_regs && ! test_bit(reg, ac97->reg_accessed))
-			val = 0xffff;
-		else
-			val = snd_ac97_read(ac97, reg);
+		val = snd_ac97_read(ac97, reg);
 		snd_iprintf(buffer, "%i:%02x = %04x\n", subidx, reg, val);
 	}
 }
@@ -1842,54 +1925,35 @@
 {
 	unsigned short mask;
 	unsigned int tmp;
-	signed long end_time;
 	
 	switch (reg) {
 	case AC97_PCM_MIC_ADC_RATE:
 		mask = 0x0000;
-		if ((ac97->regs[AC97_EXTENDED_STATUS] & 0x0008) == 0)	/* MIC VRA */
+		if ((ac97->regs[AC97_EXTENDED_STATUS] & AC97_EA_VRM) == 0)	/* MIC VRA */
 			if (rate != 48000)
 				return -EINVAL;
 		break;
 	case AC97_PCM_FRONT_DAC_RATE:
 		mask = 0x0200;
-		if ((ac97->regs[AC97_EXTENDED_STATUS] & 0x0001) == 0)	/* VRA */
+		if ((ac97->regs[AC97_EXTENDED_STATUS] & AC97_EA_VRA) == 0)	/* VRA */
 			if (rate != 48000)
 				return -EINVAL;
 		break;
 	case AC97_PCM_LR_ADC_RATE:
 		mask = 0x0100;
-		if ((ac97->regs[AC97_EXTENDED_STATUS] & 0x0001) == 0)	/* VRA */
+		if ((ac97->regs[AC97_EXTENDED_STATUS] & AC97_EA_VRA) == 0)	/* VRA */
 			if (rate != 48000)
 				return -EINVAL;
 		break;
+	case AC97_SPDIF:
+		return 0;
 	default: return -EINVAL;
 	}
 	tmp = ((unsigned int)rate * ac97->clock) / 48000;
 	if (tmp > 65535)
 		return -EINVAL;
-	snd_ac97_update_bits(ac97, AC97_POWERDOWN, mask, mask);
-	end_time = jiffies + (HZ / 50);
-	do {
-		set_current_state(TASK_UNINTERRUPTIBLE);
-		schedule_timeout(1);
-	} 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 {
-		set_current_state(TASK_UNINTERRUPTIBLE);
-		schedule_timeout(1);
-	} while (end_time - (signed long)jiffies >= 0);
-	end_time = jiffies + (HZ / 10);
-	do {
-		if ((snd_ac97_read(ac97, AC97_POWERDOWN) & 0x0003) == 0x0003)
-			break;
-		set_current_state(TASK_UNINTERRUPTIBLE);
-		schedule_timeout(1);
-	} while (end_time - (signed long)jiffies >= 0);
+	snd_ac97_read(ac97, reg);
 	return 0;
 }
 
diff -Nru a/sound/pci/ac97/ac97_id.h b/sound/pci/ac97/ac97_id.h
--- a/sound/pci/ac97/ac97_id.h	Mon Oct  7 11:25:39 2002
+++ b/sound/pci/ac97/ac97_id.h	Mon Oct  7 11:25:39 2002
@@ -30,6 +30,7 @@
 #define AC97_ID_AD1885		0x41445360
 #define AC97_ID_AD1886		0x41445361
 #define AC97_ID_AD1887		0x41445362
+#define AC97_ID_AD1886A		0x41445363
 #define AC97_ID_TR28028		0x54524108
 #define AC97_ID_STAC9700	0x83847600
 #define AC97_ID_STAC9704	0x83847604
diff -Nru a/sound/pci/ac97/ac97_patch.c b/sound/pci/ac97/ac97_patch.c
--- a/sound/pci/ac97/ac97_patch.c	Mon Oct  7 11:25:39 2002
+++ b/sound/pci/ac97/ac97_patch.c	Mon Oct  7 11:25:39 2002
@@ -31,6 +31,7 @@
 #include <sound/ac97_codec.h>
 #include <sound/asoundef.h>
 #include <sound/initval.h>
+#include "ac97_patch.h"
 
 /*
  *  Chip specific initialization
@@ -166,6 +167,7 @@
 	*/
 
 	ac97->flags |= AC97_CS_SPDIF; 
+	ac97->rates[AC97_RATES_SPDIF] &= ~SNDRV_PCM_RATE_32000;
         ac97->ext_id |= AC97_EA_SPDIF;	/* force the detection of spdif */
 	snd_ac97_write_cache(ac97, AC97_CSR_ACMODE, 0x0080);
 	return 0;
@@ -320,5 +322,17 @@
 	/* Presario700 workaround */
 	/* for Jack Sense/SPDIF Register misetting causing */
 	snd_ac97_write_cache(ac97, AC97_AD_JACK_SPDIF, 0x0010);
+	return 0;
+}
+
+int patch_ad1980(ac97_t * ac97)
+{
+	unsigned short misc;
+	
+	patch_ad1881(ac97);
+	/* Switch FRONT/SURROUND LINE-OUT/HP-OUT default connection */
+	/* it seems that most vendors connect line-out connector to headphone out of AC'97 */
+	misc = snd_ac97_read(ac97, AC97_AD_MISC);
+	snd_ac97_write_cache(ac97, AC97_AD_MISC, misc | 0x0420);
 	return 0;
 }
diff -Nru a/sound/pci/ac97/ac97_patch.h b/sound/pci/ac97/ac97_patch.h
--- a/sound/pci/ac97/ac97_patch.h	Mon Oct  7 11:25:25 2002
+++ b/sound/pci/ac97/ac97_patch.h	Mon Oct  7 11:25:25 2002
@@ -37,3 +37,4 @@
 int patch_ad1881(ac97_t * ac97);
 int patch_ad1885(ac97_t * ac97);
 int patch_ad1886(ac97_t * ac97);
+int patch_ad1980(ac97_t * ac97);
diff -Nru a/sound/pci/ac97/ak4531_codec.c b/sound/pci/ac97/ak4531_codec.c
--- a/sound/pci/ac97/ak4531_codec.c	Mon Oct  7 11:25:26 2002
+++ b/sound/pci/ac97/ak4531_codec.c	Mon Oct  7 11:25:26 2002
@@ -56,10 +56,10 @@
  */
 
 #define AK4531_SINGLE(xname, xindex, reg, shift, mask, invert) \
-{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \
-  info: snd_ak4531_info_single, \
-  get: snd_ak4531_get_single, put: snd_ak4531_put_single, \
-  private_value: reg | (shift << 16) | (mask << 24) | (invert << 22) }
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
+  .info = snd_ak4531_info_single, \
+  .get = snd_ak4531_get_single, .put = snd_ak4531_put_single, \
+  .private_value = reg | (shift << 16) | (mask << 24) | (invert << 22) }
 
 static int snd_ak4531_info_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_info_t * uinfo)
 {
@@ -117,10 +117,10 @@
 }                                                                                                                                                                                                                                                                                                            
 
 #define AK4531_DOUBLE(xname, xindex, left_reg, right_reg, left_shift, right_shift, mask, invert) \
-{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \
-  info: snd_ak4531_info_double, \
-  get: snd_ak4531_get_double, put: snd_ak4531_put_double, \
-  private_value: left_reg | (right_reg << 8) | (left_shift << 16) | (right_shift << 19) | (mask << 24) | (invert << 22) }
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
+  .info = snd_ak4531_info_double, \
+  .get = snd_ak4531_get_double, .put = snd_ak4531_put_double, \
+  .private_value = left_reg | (right_reg << 8) | (left_shift << 16) | (right_shift << 19) | (mask << 24) | (invert << 22) }
 
 static int snd_ak4531_info_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_info_t * uinfo)
 {
@@ -196,10 +196,10 @@
 }                                                                                                                                                                                                                                                                                                            
 
 #define AK4531_INPUT_SW(xname, xindex, reg1, reg2, left_shift, right_shift) \
-{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \
-  info: snd_ak4531_info_input_sw, \
-  get: snd_ak4531_get_input_sw, put: snd_ak4531_put_input_sw, \
-  private_value: reg1 | (reg2 << 8) | (left_shift << 16) | (right_shift << 24) }
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
+  .info = snd_ak4531_info_input_sw, \
+  .get = snd_ak4531_get_input_sw, .put = snd_ak4531_put_input_sw, \
+  .private_value = reg1 | (reg2 << 8) | (left_shift << 16) | (right_shift << 24) }
 
 static int snd_ak4531_info_input_sw(snd_kcontrol_t * kcontrol, snd_ctl_elem_info_t * uinfo)
 {
@@ -362,7 +362,7 @@
 	int idx, err;
 	ak4531_t * ak4531;
 	static snd_device_ops_t ops = {
-		dev_free:	snd_ak4531_dev_free,
+		.dev_free =	snd_ak4531_dev_free,
 	};
 
 	snd_assert(rak4531 != NULL, return -EINVAL);
diff -Nru a/sound/pci/ali5451/ali5451.c b/sound/pci/ali5451/ali5451.c
--- a/sound/pci/ali5451/ali5451.c	Mon Oct  7 11:25:39 2002
+++ b/sound/pci/ali5451/ali5451.c	Mon Oct  7 11:25:39 2002
@@ -1540,22 +1540,22 @@
 
 static snd_pcm_hardware_t snd_ali_playback =
 {
-	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
 				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
 				 SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START),
-	formats:		(SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE |
+	.formats =		(SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE |
 				 SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U16_LE),
-	rates:			SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
-	rate_min:		4000,
-	rate_max:		48000,
-	channels_min:		1,
-	channels_max:		2,
-	buffer_bytes_max:	(256*1024),
-	period_bytes_min:	64,
-	period_bytes_max:	(256*1024),
-	periods_min:		1,
-	periods_max:		1024,
-	fifo_size:		0,
+	.rates =		SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+	.rate_min =		4000,
+	.rate_max =		48000,
+	.channels_min =		1,
+	.channels_max =		2,
+	.buffer_bytes_max =	(256*1024),
+	.period_bytes_min =	64,
+	.period_bytes_max =	(256*1024),
+	.periods_min =		1,
+	.periods_max =		1024,
+	.fifo_size =		0,
 };
 
 /*
@@ -1564,22 +1564,22 @@
 
 static snd_pcm_hardware_t snd_ali_capture =
 {
-	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
 				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
 				 SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START),
-	formats:		(SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE |
+	.formats =		(SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE |
 				 SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U16_LE),
-	rates:			SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
-	rate_min:		4000,
-	rate_max:		48000,
-	channels_min:		1,
-	channels_max:		2,
-	buffer_bytes_max:	(128*1024),
-	period_bytes_min:	64,
-	period_bytes_max:	(128*1024),
-	periods_min:		1,
-	periods_max:		1024,
-	fifo_size:		0,
+	.rates =		SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+	.rate_min =		4000,
+	.rate_max =		48000,
+	.channels_min =		1,
+	.channels_max =		2,
+	.buffer_bytes_max =	(128*1024),
+	.period_bytes_min =	64,
+	.period_bytes_max =	(128*1024),
+	.periods_min =		1,
+	.periods_max =		1024,
+	.fifo_size =		0,
 };
 
 static void snd_ali_pcm_free_substream(snd_pcm_runtime_t *runtime)
@@ -1666,25 +1666,25 @@
 }
 
 static snd_pcm_ops_t snd_ali_playback_ops = {
-	open:		snd_ali_playback_open,
-	close:		snd_ali_playback_close,
-	ioctl:		snd_ali_ioctl,
-	hw_params:	snd_ali_playback_hw_params,
-	hw_free:	snd_ali_playback_hw_free,
-	prepare:	snd_ali_playback_prepare,
-	trigger:	snd_ali_trigger,
-	pointer:	snd_ali_playback_pointer,
+	.open =		snd_ali_playback_open,
+	.close =	snd_ali_playback_close,
+	.ioctl =	snd_ali_ioctl,
+	.hw_params =	snd_ali_playback_hw_params,
+	.hw_free =	snd_ali_playback_hw_free,
+	.prepare =	snd_ali_playback_prepare,
+	.trigger =	snd_ali_trigger,
+	.pointer =	snd_ali_playback_pointer,
 };
 
 static snd_pcm_ops_t snd_ali_capture_ops = {
-	open:		snd_ali_capture_open,
-	close:		snd_ali_capture_close,
-	ioctl:		snd_ali_ioctl,
-	hw_params:	snd_ali_capture_hw_params,
-	hw_free:	snd_ali_capture_hw_free,
-	prepare:	snd_ali_capture_prepare,
-	trigger:	snd_ali_trigger,
-	pointer:	snd_ali_capture_pointer,
+	.open =		snd_ali_capture_open,
+	.close =	snd_ali_capture_close,
+	.ioctl =	snd_ali_ioctl,
+	.hw_params =	snd_ali_capture_hw_params,
+	.hw_free =	snd_ali_capture_hw_free,
+	.prepare =	snd_ali_capture_prepare,
+	.trigger =	snd_ali_trigger,
+	.pointer =	snd_ali_capture_pointer,
 };
 
 
@@ -1722,9 +1722,9 @@
 }
 
 #define ALI5451_SPDIF(xname, xindex, value) \
-{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex,\
-info: snd_ali5451_spdif_info, get: snd_ali5451_spdif_get, \
-put: snd_ali5451_spdif_put, private_value: value}
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex,\
+.info = snd_ali5451_spdif_info, .get = snd_ali5451_spdif_get, \
+.put = snd_ali5451_spdif_put, .private_value = value}
 
 static int snd_ali5451_spdif_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
 {
@@ -1871,7 +1871,6 @@
 	ali_t *chip = snd_magic_cast(ali_t, pci_get_drvdata(dev), return);
 #endif
 	ali_image_t *im;
-	unsigned long flags;
 	int i, j;
 
 	im = chip->image;
@@ -1882,8 +1881,7 @@
 		return;
 #endif
 
-	save_flags(flags); 
-	cli();
+	spin_lock_irq(&chip->reg_lock);
 	
 	im->regs[ALI_MISCINT >> 2] = inl(ALI_REG(chip, ALI_MISCINT));
 	// im->regs[ALI_START >> 2] = inl(ALI_REG(chip, ALI_START));
@@ -1907,7 +1905,7 @@
 	// stop all HW channel
 	outl(0xffffffff, ALI_REG(chip, ALI_STOP));
 
-	restore_flags(flags);
+	spin_unlock_irq(&chip->reg_lock);
 #ifndef PCI_OLD_SUSPEND
 	return 0;
 #endif
@@ -1925,7 +1923,6 @@
 	ali_t *chip = snd_magic_cast(ali_t, pci_get_drvdata(dev), return);
 #endif
 	ali_image_t *im;
-	unsigned long flags;
 	int i, j;
 
 	im = chip->image;
@@ -1938,8 +1935,7 @@
 
 	pci_enable_device(chip->pci);
 
-	save_flags(flags); 
-	cli();
+	spin_lock_irq(&chip->reg_lock);
 	
 	for (i = 0; i < ALI_CHANNELS; i++) {
 		outb(i, ALI_REG(chip, ALI_GC_CIR));
@@ -1960,7 +1956,7 @@
 	// restore IRQ enable bits
 	outl(im->regs[ALI_MISCINT >> 2], ALI_REG(chip, ALI_MISCINT));
 	
-	restore_flags(flags);
+	spin_unlock_irq(&chip->reg_lock);
 #ifndef PCI_OLD_SUSPEND
 	return 0;
 #endif
@@ -2240,13 +2236,13 @@
 }
 
 static struct pci_driver driver = {
-	name: "ALI 5451",
-	id_table: snd_ali_ids,
-	probe: snd_ali_probe,
-	remove: __devexit_p(snd_ali_remove),
+	.name = "ALI 5451",
+	.id_table = snd_ali_ids,
+	.probe = snd_ali_probe,
+	.remove = __devexit_p(snd_ali_remove),
 #ifdef CONFIG_PM
-	suspend: snd_ali_suspend,
-	resume: snd_ali_resume,
+	.suspend = snd_ali_suspend,
+	.resume = snd_ali_resume,
 #endif
 };                                
 
diff -Nru a/sound/pci/als4000.c b/sound/pci/als4000.c
--- a/sound/pci/als4000.c	Mon Oct  7 11:25:39 2002
+++ b/sound/pci/als4000.c	Mon Oct  7 11:25:39 2002
@@ -372,40 +372,40 @@
 
 static snd_pcm_hardware_t snd_als4000_playback =
 {
-	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
 				 SNDRV_PCM_INFO_MMAP_VALID),
-	formats:		SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 |
+	.formats =		SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 |
 				SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE,	/* formats */
-	rates:			SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
-	rate_min:		4000,
-	rate_max:		48000,
-	channels_min:		1,
-	channels_max:		2,
-	buffer_bytes_max:	65536,
-	period_bytes_min:	64,
-	period_bytes_max:	65536,
-	periods_min:		1,
-	periods_max:		1024,
-	fifo_size:		0
+	.rates =		SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+	.rate_min =		4000,
+	.rate_max =		48000,
+	.channels_min =		1,
+	.channels_max =		2,
+	.buffer_bytes_max =	65536,
+	.period_bytes_min =	64,
+	.period_bytes_max =	65536,
+	.periods_min =		1,
+	.periods_max =		1024,
+	.fifo_size =		0
 };
 
 static snd_pcm_hardware_t snd_als4000_capture =
 {
-	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
 				 SNDRV_PCM_INFO_MMAP_VALID),
-	formats:		SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 |
+	.formats =		SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 |
 				SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE,	/* formats */
-	rates:			SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
-	rate_min:		4000,
-	rate_max:		48000,
-	channels_min:		1,
-	channels_max:		2,
-	buffer_bytes_max:	65536,
-	period_bytes_min:	64,
-	period_bytes_max:	65536,
-	periods_min:		1,
-	periods_max:		1024,
-	fifo_size:		0
+	.rates =		SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+	.rate_min =		4000,
+	.rate_max =		48000,
+	.channels_min =		1,
+	.channels_max =		2,
+	.buffer_bytes_max =	65536,
+	.period_bytes_min =	64,
+	.period_bytes_max =	65536,
+	.periods_min =		1,
+	.periods_max =		1024,
+	.fifo_size =		0
 };
 
 /*****************************************************************/
@@ -451,25 +451,25 @@
 /******************************************************************/
 
 static snd_pcm_ops_t snd_als4000_playback_ops = {
-	open:		snd_als4000_playback_open,
-	close:		snd_als4000_playback_close,
-	ioctl:		snd_pcm_lib_ioctl,
-	hw_params:	snd_als4000_hw_params,
-	hw_free:	snd_als4000_hw_free,
-	prepare:	snd_als4000_playback_prepare,
-	trigger:	snd_als4000_playback_trigger,
-	pointer:	snd_als4000_playback_pointer
+	.open =		snd_als4000_playback_open,
+	.close =	snd_als4000_playback_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	snd_als4000_hw_params,
+	.hw_free =	snd_als4000_hw_free,
+	.prepare =	snd_als4000_playback_prepare,
+	.trigger =	snd_als4000_playback_trigger,
+	.pointer =	snd_als4000_playback_pointer
 };
 
 static snd_pcm_ops_t snd_als4000_capture_ops = {
-	open:		snd_als4000_capture_open,
-	close:		snd_als4000_capture_close,
-	ioctl:		snd_pcm_lib_ioctl,
-	hw_params:	snd_als4000_hw_params,
-	hw_free:	snd_als4000_hw_free,
-	prepare:	snd_als4000_capture_prepare,
-	trigger:	snd_als4000_capture_trigger,
-	pointer:	snd_als4000_capture_pointer
+	.open =		snd_als4000_capture_open,
+	.close =	snd_als4000_capture_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	snd_als4000_hw_params,
+	.hw_free =	snd_als4000_hw_free,
+	.prepare =	snd_als4000_capture_prepare,
+	.trigger =	snd_als4000_capture_trigger,
+	.pointer =	snd_als4000_capture_pointer
 };
 
 static void snd_als4000_pcm_free(snd_pcm_t *pcm)
@@ -679,10 +679,10 @@
 }
 
 static struct pci_driver driver = {
-	name: "ALS4000",
-	id_table: snd_als4000_ids,
-	probe: snd_card_als4k_probe,
-	remove: __devexit_p(snd_card_als4k_remove),
+	.name = "ALS4000",
+	.id_table = snd_als4000_ids,
+	.probe = snd_card_als4k_probe,
+	.remove = __devexit_p(snd_card_als4k_remove),
 };
 
 static int __init alsa_card_als4k_init(void)
diff -Nru a/sound/pci/cmipci.c b/sound/pci/cmipci.c
--- a/sound/pci/cmipci.c	Mon Oct  7 11:25:26 2002
+++ b/sound/pci/cmipci.c	Mon Oct  7 11:25:26 2002
@@ -205,9 +205,9 @@
 #define CM_SFILENB		0x00001000
 #define CM_MMODE_MASK		0x00000E00
 #define CM_SPDIF_SELECT2	0x00000100	/* for model > 039 ? */
-#define CM_ENCENTER		0x00000080	/* shared with FLINKON? */
-#define CM_FLINKON		0x00000080
-#define CM_FLINKOFF		0x00000040
+#define CM_ENCENTER		0x00000080
+#define CM_FLINKON		0x00000040
+#define CM_FLINKOFF		0x00000020
 #define CM_MIDSMP		0x00000010
 #define CM_UPDDMA_MASK		0x0000000C
 #define CM_TWAIT_MASK		0x00000003
@@ -257,6 +257,7 @@
 #define CM_REG_MISC		0x27
 #define CM_XGPO1		0x20
 // #define CM_XGPBIO		0x04
+#define CM_MIC_CENTER_LFE	0x04	/* mic as center/lfe out? (model 039 or later?) */
 #define CM_SPDIF_INVERSE	0x04	/* spdif input phase inverse (model 037) */
 #define CM_SPDVALID		0x02	/* spdif input valid check */
 #define CM_DMAUTO		0x01
@@ -531,14 +532,14 @@
 
 static unsigned int hw_channels[] = {1, 2, 4, 5, 6};
 static snd_pcm_hw_constraint_list_t hw_constraints_channels_4 = {
-	count: 3,
-	list: hw_channels,
-	mask: 0,
+	.count = 3,
+	.list = hw_channels,
+	.mask = 0,
 };
 static snd_pcm_hw_constraint_list_t hw_constraints_channels_6 = {
-	count: 5,
-	list: hw_channels,
-	mask: 0,
+	.count = 5,
+	.list = hw_channels,
+	.mask = 0,
 };
 
 static int set_dac_channels(cmipci_t *cm, cmipci_pcm_t *rec, int channels)
@@ -624,7 +625,7 @@
 	/* program sample counts */
 	reg = rec->ch ? CM_REG_CH1_FRAME2 : CM_REG_CH0_FRAME2;
 	snd_cmipci_write_w(cm, reg, rec->dma_size - 1);
-	snd_cmipci_write_w(cm, reg + 2, rec->period_size - 1);
+	snd_cmipci_write_w(cm, reg + 2, rec->period_size); /* FIXME: or period_size-1 ?? */
 
 	/* set adc/dac flag */
 	val = rec->ch ? CM_CHADC1 : CM_CHADC0;
@@ -943,11 +944,11 @@
 
 static snd_kcontrol_new_t snd_cmipci_spdif_default __devinitdata =
 {
-	iface:		SNDRV_CTL_ELEM_IFACE_PCM,
-	name:           SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
-	info:		snd_cmipci_spdif_default_info,
-	get:		snd_cmipci_spdif_default_get,
-	put:		snd_cmipci_spdif_default_put
+	.iface =	SNDRV_CTL_ELEM_IFACE_PCM,
+	.name =		SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
+	.info =		snd_cmipci_spdif_default_info,
+	.get =		snd_cmipci_spdif_default_get,
+	.put =		snd_cmipci_spdif_default_put
 };
 
 static int snd_cmipci_spdif_mask_info(snd_kcontrol_t *kcontrol,
@@ -970,11 +971,11 @@
 
 static snd_kcontrol_new_t snd_cmipci_spdif_mask __devinitdata =
 {
-	access:		SNDRV_CTL_ELEM_ACCESS_READ,
-	iface:		SNDRV_CTL_ELEM_IFACE_MIXER,
-	name:           SNDRV_CTL_NAME_IEC958("",PLAYBACK,CON_MASK),
-	info:		snd_cmipci_spdif_mask_info,
-	get:		snd_cmipci_spdif_mask_get,
+	.access =	SNDRV_CTL_ELEM_ACCESS_READ,
+	.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name =		SNDRV_CTL_NAME_IEC958("",PLAYBACK,CON_MASK),
+	.info =		snd_cmipci_spdif_mask_info,
+	.get =		snd_cmipci_spdif_mask_get,
 };
 
 static int snd_cmipci_spdif_stream_info(snd_kcontrol_t *kcontrol,
@@ -1019,12 +1020,12 @@
 
 static snd_kcontrol_new_t snd_cmipci_spdif_stream __devinitdata =
 {
-	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_cmipci_spdif_stream_info,
-	get:		snd_cmipci_spdif_stream_get,
-	put:		snd_cmipci_spdif_stream_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_cmipci_spdif_stream_info,
+	.get =		snd_cmipci_spdif_stream_get,
+	.put =		snd_cmipci_spdif_stream_put
 };
 
 /*
@@ -1087,7 +1088,7 @@
 }
 
 /* spinlock held! */
-static void setup_ac3(cmipci_t *cm, int do_ac3, int rate)
+static void setup_ac3(cmipci_t *cm, snd_pcm_substream_t *subs, int do_ac3, int rate)
 {
 	cm->channel[CM_CH_PLAY].ac3_shift = 0;
 	cm->spdif_counter = 0;
@@ -1111,7 +1112,7 @@
 			/* set 176K sample rate to fix 033 HW bug */
 			if (cm->chip_version == 33) {
 				if (rate >= 48000) {
-					snd_cmipci_set_bit(cm, CM_REG_CHFORMAT, CM_PLAYBACK_176K);
+					snd_cmipci_set_bit(cm, CM_REG_CHFORMAT, CM_PLAYBACK_SRATE_176K);
 				} else {
 					snd_cmipci_clear_bit(cm, CM_REG_CHFORMAT, CM_PLAYBACK_SRATE_176K);
 				}
@@ -1152,7 +1153,7 @@
 		/* snd_cmipci_set_bit(cm, CM_REG_FUNCTRL1, CM_SPDO2DAC); */
 		if (cm->spdif_playback_enabled)
 			snd_cmipci_set_bit(cm, CM_REG_FUNCTRL1, CM_PLAYBACK_SPDF);
-		setup_ac3(cm, do_ac3, rate);
+		setup_ac3(cm, subs, do_ac3, rate);
 
 		if (rate == 48000)
 			snd_cmipci_set_bit(cm, CM_REG_MISC_CTRL, CM_SPDIF48K | CM_SPDF_AC97);
@@ -1164,7 +1165,7 @@
 		/* snd_cmipci_clear_bit(cm, CM_REG_LEGACY_CTRL, CM_ENSPDOUT); */
 		/* snd_cmipci_clear_bit(cm, CM_REG_FUNCTRL1, CM_SPDO2DAC); */
 		snd_cmipci_clear_bit(cm, CM_REG_FUNCTRL1, CM_PLAYBACK_SPDF);
-		setup_ac3(cm, 0, 0);
+		setup_ac3(cm, subs, 0, 0);
 	}
 	spin_unlock_irqrestore(&cm->reg_lock, flags);
 }
@@ -1284,100 +1285,100 @@
 /* playback on channel A */
 static snd_pcm_hardware_t snd_cmipci_playback =
 {
-	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
-				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
+	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_PAUSE |
 				 SNDRV_PCM_INFO_MMAP_VALID),
-	formats:		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
-	rates:			SNDRV_PCM_RATE_5512 | SNDRV_PCM_RATE_8000_48000,
-	rate_min:		5512,
-	rate_max:		48000,
-	channels_min:		1,
-	channels_max:		2,
-	buffer_bytes_max:	(128*1024),
-	period_bytes_min:	64,
-	period_bytes_max:	(128*1024),
-	periods_min:		2,
-	periods_max:		1024,
-	fifo_size:		0,
+	.formats =		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
+	.rates =		SNDRV_PCM_RATE_5512 | SNDRV_PCM_RATE_8000_48000,
+	.rate_min =		5512,
+	.rate_max =		48000,
+	.channels_min =		1,
+	.channels_max =		2,
+	.buffer_bytes_max =	(128*1024),
+	.period_bytes_min =	64,
+	.period_bytes_max =	(128*1024),
+	.periods_min =		2,
+	.periods_max =		1024,
+	.fifo_size =		0,
 };
 
 /* capture on channel B */
 static snd_pcm_hardware_t snd_cmipci_capture =
 {
-	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
-				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
+	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_PAUSE |
 				 SNDRV_PCM_INFO_MMAP_VALID),
-	formats:		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
-	rates:			SNDRV_PCM_RATE_5512 | SNDRV_PCM_RATE_8000_48000,
-	rate_min:		5512,
-	rate_max:		48000,
-	channels_min:		1,
-	channels_max:		2,
-	buffer_bytes_max:	(128*1024),
-	period_bytes_min:	64,
-	period_bytes_max:	(128*1024),
-	periods_min:		2,
-	periods_max:		1024,
-	fifo_size:		0,
+	.formats =		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
+	.rates =		SNDRV_PCM_RATE_5512 | SNDRV_PCM_RATE_8000_48000,
+	.rate_min =		5512,
+	.rate_max =		48000,
+	.channels_min =		1,
+	.channels_max =		2,
+	.buffer_bytes_max =	(128*1024),
+	.period_bytes_min =	64,
+	.period_bytes_max =	(128*1024),
+	.periods_min =		2,
+	.periods_max =		1024,
+	.fifo_size =		0,
 };
 
 /* playback on channel B - stereo 16bit only? */
 static snd_pcm_hardware_t snd_cmipci_playback2 =
 {
-	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
-				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
+	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_PAUSE |
 				 SNDRV_PCM_INFO_MMAP_VALID),
-	formats:		SNDRV_PCM_FMTBIT_S16_LE,
-	rates:			SNDRV_PCM_RATE_5512 | SNDRV_PCM_RATE_8000_48000,
-	rate_min:		5512,
-	rate_max:		48000,
-	channels_min:		2,
-	channels_max:		2,
-	buffer_bytes_max:	(128*1024),
-	period_bytes_min:	64,
-	period_bytes_max:	(128*1024),
-	periods_min:		2,
-	periods_max:		1024,
-	fifo_size:		0,
+	.formats =		SNDRV_PCM_FMTBIT_S16_LE,
+	.rates =		SNDRV_PCM_RATE_5512 | SNDRV_PCM_RATE_8000_48000,
+	.rate_min =		5512,
+	.rate_max =		48000,
+	.channels_min =		2,
+	.channels_max =		2,
+	.buffer_bytes_max =	(128*1024),
+	.period_bytes_min =	64,
+	.period_bytes_max =	(128*1024),
+	.periods_min =		2,
+	.periods_max =		1024,
+	.fifo_size =		0,
 };
 
 /* spdif playback on channel A */
 static snd_pcm_hardware_t snd_cmipci_playback_spdif =
 {
-	info:			(SNDRV_PCM_INFO_INTERLEAVED |
-				 SNDRV_PCM_INFO_BLOCK_TRANSFER),
-	formats:		SNDRV_PCM_FMTBIT_S16_LE /*| SNDRV_PCM_FMTBIT_S32_LE*/,
-	rates:			SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000,
-	rate_min:		44100,
-	rate_max:		48000,
-	channels_min:		2,
-	channels_max:		2,
-	buffer_bytes_max:	(128*1024),
-	period_bytes_min:	64,
-	period_bytes_max:	(128*1024),
-	periods_min:		2,
-	periods_max:		1024,
-	fifo_size:		0,
+	.info =			(SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_PAUSE),
+	.formats =		SNDRV_PCM_FMTBIT_S16_LE /*| SNDRV_PCM_FMTBIT_S32_LE*/,
+	.rates =		SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000,
+	.rate_min =		44100,
+	.rate_max =		48000,
+	.channels_min =		2,
+	.channels_max =		2,
+	.buffer_bytes_max =	(128*1024),
+	.period_bytes_min =	64,
+	.period_bytes_max =	(128*1024),
+	.periods_min =		2,
+	.periods_max =		1024,
+	.fifo_size =		0,
 };
 
 /* spdif capture on channel B */
 static snd_pcm_hardware_t snd_cmipci_capture_spdif =
 {
-	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
-				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
+	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_PAUSE |
 				 SNDRV_PCM_INFO_MMAP_VALID),
-	formats:	        SNDRV_PCM_FMTBIT_S16_LE,
-	rates:			SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000,
-	rate_min:		44100,
-	rate_max:		48000,
-	channels_min:		2,
-	channels_max:		2,
-	buffer_bytes_max:	(128*1024),
-	period_bytes_min:	64,
-	period_bytes_max:	(128*1024),
-	periods_min:		2,
-	periods_max:		1024,
-	fifo_size:		0,
+	.formats =	        SNDRV_PCM_FMTBIT_S16_LE,
+	.rates =		SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000,
+	.rate_min =		44100,
+	.rate_max =		48000,
+	.channels_min =		2,
+	.channels_max =		2,
+	.buffer_bytes_max =	(128*1024),
+	.period_bytes_min =	64,
+	.period_bytes_max =	(128*1024),
+	.periods_min =		2,
+	.periods_max =		1024,
+	.fifo_size =		0,
 };
 
 /*
@@ -1556,62 +1557,62 @@
  */
 
 static snd_pcm_ops_t snd_cmipci_playback_ops = {
-	open:		snd_cmipci_playback_open,
-	close:		snd_cmipci_playback_close,
-	ioctl:		snd_pcm_lib_ioctl,
-	hw_params:	snd_cmipci_hw_params,
-	hw_free:	snd_cmipci_playback_hw_free,
-	prepare:	snd_cmipci_playback_prepare,
-	trigger:	snd_cmipci_playback_trigger,
-	pointer:	snd_cmipci_playback_pointer,
+	.open =		snd_cmipci_playback_open,
+	.close =	snd_cmipci_playback_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	snd_cmipci_hw_params,
+	.hw_free =	snd_cmipci_playback_hw_free,
+	.prepare =	snd_cmipci_playback_prepare,
+	.trigger =	snd_cmipci_playback_trigger,
+	.pointer =	snd_cmipci_playback_pointer,
 };
 
 static snd_pcm_ops_t snd_cmipci_capture_ops = {
-	open:		snd_cmipci_capture_open,
-	close:		snd_cmipci_capture_close,
-	ioctl:		snd_pcm_lib_ioctl,
-	hw_params:	snd_cmipci_hw_params,
-	hw_free:	snd_cmipci_hw_free,
-	prepare:	snd_cmipci_capture_prepare,
-	trigger:	snd_cmipci_capture_trigger,
-	pointer:	snd_cmipci_capture_pointer,
+	.open =		snd_cmipci_capture_open,
+	.close =	snd_cmipci_capture_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	snd_cmipci_hw_params,
+	.hw_free =	snd_cmipci_hw_free,
+	.prepare =	snd_cmipci_capture_prepare,
+	.trigger =	snd_cmipci_capture_trigger,
+	.pointer =	snd_cmipci_capture_pointer,
 };
 
 static snd_pcm_ops_t snd_cmipci_playback2_ops = {
-	open:		snd_cmipci_playback2_open,
-	close:		snd_cmipci_playback2_close,
-	ioctl:		snd_pcm_lib_ioctl,
-	hw_params:	snd_cmipci_hw_params,
-	hw_free:	snd_cmipci_hw_free,
-	prepare:	snd_cmipci_capture_prepare,	/* channel B */
-	trigger:	snd_cmipci_capture_trigger,	/* channel B */
-	pointer:	snd_cmipci_capture_pointer,	/* channel B */
+	.open =		snd_cmipci_playback2_open,
+	.close =	snd_cmipci_playback2_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	snd_cmipci_hw_params,
+	.hw_free =	snd_cmipci_hw_free,
+	.prepare =	snd_cmipci_capture_prepare,	/* channel B */
+	.trigger =	snd_cmipci_capture_trigger,	/* channel B */
+	.pointer =	snd_cmipci_capture_pointer,	/* channel B */
 };
 
 static snd_pcm_ops_t snd_cmipci_playback_spdif_ops = {
-	open:		snd_cmipci_playback_spdif_open,
-	close:		snd_cmipci_playback_spdif_close,
-	ioctl:		snd_pcm_lib_ioctl,
-	hw_params:	snd_cmipci_hw_params,
-	hw_free:	snd_cmipci_playback_hw_free,
-	prepare:	snd_cmipci_playback_spdif_prepare,	/* set up rate */
-	trigger:	snd_cmipci_playback_trigger,
-	pointer:	snd_cmipci_playback_pointer,
+	.open =		snd_cmipci_playback_spdif_open,
+	.close =	snd_cmipci_playback_spdif_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	snd_cmipci_hw_params,
+	.hw_free =	snd_cmipci_playback_hw_free,
+	.prepare =	snd_cmipci_playback_spdif_prepare,	/* set up rate */
+	.trigger =	snd_cmipci_playback_trigger,
+	.pointer =	snd_cmipci_playback_pointer,
 #ifdef DO_SOFT_AC3
-	copy:		snd_cmipci_ac3_copy,
-	silence:	snd_cmipci_ac3_silence,
+	.copy =		snd_cmipci_ac3_copy,
+	.silence =	snd_cmipci_ac3_silence,
 #endif
 };
 
 static snd_pcm_ops_t snd_cmipci_capture_spdif_ops = {
-	open:		snd_cmipci_capture_spdif_open,
-	close:		snd_cmipci_capture_spdif_close,
-	ioctl:		snd_pcm_lib_ioctl,
-	hw_params:	snd_cmipci_hw_params,
-	hw_free:	snd_cmipci_capture_spdif_hw_free,
-	prepare:	snd_cmipci_capture_spdif_prepare,
-	trigger:	snd_cmipci_capture_trigger,
-	pointer:	snd_cmipci_capture_pointer,
+	.open =		snd_cmipci_capture_spdif_open,
+	.close =	snd_cmipci_capture_spdif_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	snd_cmipci_hw_params,
+	.hw_free =	snd_cmipci_capture_spdif_hw_free,
+	.prepare =	snd_cmipci_capture_spdif_prepare,
+	.trigger =	snd_cmipci_capture_trigger,
+	.pointer =	snd_cmipci_capture_pointer,
 };
 
 
@@ -1730,10 +1731,10 @@
  ((lreg) | ((rreg) << 8) | (lshift << 16) | (rshift << 19) | (mask << 24) | (invert << 22) | (stereo << 23))
 
 #define CMIPCI_DOUBLE(xname, left_reg, right_reg, left_shift, right_shift, mask, invert, stereo) \
-{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, \
-  info: snd_cmipci_info_volume, \
-  get: snd_cmipci_get_volume, put: snd_cmipci_put_volume, \
-  private_value: COMPOSE_SB_REG(left_reg, right_reg, left_shift, right_shift, mask, invert, stereo), \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+  .info = snd_cmipci_info_volume, \
+  .get = snd_cmipci_get_volume, .put = snd_cmipci_put_volume, \
+  .private_value = COMPOSE_SB_REG(left_reg, right_reg, left_shift, right_shift, mask, invert, stereo), \
 }
 
 #define CMIPCI_SB_VOL_STEREO(xname,reg,shift,mask) CMIPCI_DOUBLE(xname, reg, reg+1, shift, shift, mask, 0, 1)
@@ -1830,10 +1831,10 @@
  * input route (left,right) -> (left,right)
  */
 #define CMIPCI_SB_INPUT_SW(xname, left_shift, right_shift) \
-{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, \
-  info: snd_cmipci_info_input_sw, \
-  get: snd_cmipci_get_input_sw, put: snd_cmipci_put_input_sw, \
-  private_value: COMPOSE_SB_REG(SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT, left_shift, right_shift, 1, 0, 1), \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+  .info = snd_cmipci_info_input_sw, \
+  .get = snd_cmipci_get_input_sw, .put = snd_cmipci_put_input_sw, \
+  .private_value = COMPOSE_SB_REG(SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT, left_shift, right_shift, 1, 0, 1), \
 }
 
 static int snd_cmipci_info_input_sw(snd_kcontrol_t * kcontrol, snd_ctl_elem_info_t * uinfo)
@@ -1894,31 +1895,31 @@
  */
 
 #define CMIPCI_MIXER_SW_STEREO(xname, reg, lshift, rshift, invert) \
-{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, \
-  info: snd_cmipci_info_native_mixer, \
-  get: snd_cmipci_get_native_mixer, put: snd_cmipci_put_native_mixer, \
-  private_value: COMPOSE_SB_REG(reg, reg, lshift, rshift, 1, invert, 1), \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+  .info = snd_cmipci_info_native_mixer, \
+  .get = snd_cmipci_get_native_mixer, .put = snd_cmipci_put_native_mixer, \
+  .private_value = COMPOSE_SB_REG(reg, reg, lshift, rshift, 1, invert, 1), \
 }
 
 #define CMIPCI_MIXER_SW_MONO(xname, reg, shift, invert) \
-{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, \
-  info: snd_cmipci_info_native_mixer, \
-  get: snd_cmipci_get_native_mixer, put: snd_cmipci_put_native_mixer, \
-  private_value: COMPOSE_SB_REG(reg, reg, shift, shift, 1, invert, 0), \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+  .info = snd_cmipci_info_native_mixer, \
+  .get = snd_cmipci_get_native_mixer, .put = snd_cmipci_put_native_mixer, \
+  .private_value = COMPOSE_SB_REG(reg, reg, shift, shift, 1, invert, 0), \
 }
 
 #define CMIPCI_MIXER_VOL_STEREO(xname, reg, lshift, rshift, mask) \
-{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, \
-  info: snd_cmipci_info_native_mixer, \
-  get: snd_cmipci_get_native_mixer, put: snd_cmipci_put_native_mixer, \
-  private_value: COMPOSE_SB_REG(reg, reg, lshift, rshift, mask, 0, 1), \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+  .info = snd_cmipci_info_native_mixer, \
+  .get = snd_cmipci_get_native_mixer, .put = snd_cmipci_put_native_mixer, \
+  .private_value = COMPOSE_SB_REG(reg, reg, lshift, rshift, mask, 0, 1), \
 }
 
 #define CMIPCI_MIXER_VOL_MONO(xname, reg, shift, mask) \
-{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, \
-  info: snd_cmipci_info_native_mixer, \
-  get: snd_cmipci_get_native_mixer, put: snd_cmipci_put_native_mixer, \
-  private_value: COMPOSE_SB_REG(reg, reg, shift, shift, mask, 0, 0), \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+  .info = snd_cmipci_info_native_mixer, \
+  .get = snd_cmipci_get_native_mixer, .put = snd_cmipci_put_native_mixer, \
+  .private_value = COMPOSE_SB_REG(reg, reg, shift, shift, mask, 0, 0), \
 }
 
 static int snd_cmipci_info_native_mixer(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
@@ -2013,12 +2014,12 @@
 	CMIPCI_SB_VOL_STEREO("PCM Playback Volume", SB_DSP4_PCM_DEV, 3, 31),
 	//CMIPCI_MIXER_SW_MONO("PCM Playback Switch", CM_REG_MIXER1, CM_WSMUTE_SHIFT, 1),
 	{ /* switch with sensitivity */
-		iface: SNDRV_CTL_ELEM_IFACE_MIXER,
-		name: "PCM Playback Switch",
-		info: snd_cmipci_info_native_mixer,
-		get: snd_cmipci_get_native_mixer_sensitive,
-		put: snd_cmipci_put_native_mixer_sensitive,
-		private_value: COMPOSE_SB_REG(CM_REG_MIXER1, CM_REG_MIXER1, CM_WSMUTE_SHIFT, CM_WSMUTE_SHIFT, 1, 1, 0),
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "PCM Playback Switch",
+		.info = snd_cmipci_info_native_mixer,
+		.get = snd_cmipci_get_native_mixer_sensitive,
+		.put = snd_cmipci_put_native_mixer_sensitive,
+		.private_value = COMPOSE_SB_REG(CM_REG_MIXER1, CM_REG_MIXER1, CM_WSMUTE_SHIFT, CM_WSMUTE_SHIFT, 1, 1, 0),
 	},
 	CMIPCI_MIXER_SW_STEREO("PCM Capture Switch", CM_REG_MIXER1, CM_WAVEINL_SHIFT, CM_WAVEINR_SHIFT, 0),
 	CMIPCI_SB_VOL_STEREO("Synth Playback Volume", SB_DSP4_SYNTH_DEV, 3, 31),
@@ -2132,11 +2133,11 @@
 
 #define DEFINE_SWITCH_ARG(sname, xreg, xmask, xmask_on, xis_byte, xac3) \
 static snd_cmipci_switch_args_t cmipci_switch_arg_##sname = { \
-  reg: xreg, \
-  mask: xmask, \
-  mask_on: xmask_on, \
-  is_byte: xis_byte, \
-  ac3_sensitive: xac3, \
+  .reg = xreg, \
+  .mask = xmask, \
+  .mask_on = xmask_on, \
+  .is_byte = xis_byte, \
+  .ac3_sensitive = xac3, \
 }
 	
 #define DEFINE_BIT_SWITCH_ARG(sname, xreg, xmask, xis_byte, xac3) \
@@ -2172,12 +2173,12 @@
 DEFINE_SWITCH_ARG(modem, CM_REG_MISC_CTRL, CM_FLINKON|CM_FLINKOFF, CM_FLINKON, 0, 0);
 
 #define DEFINE_SWITCH(sname, stype, sarg) \
-{ name: sname, \
-  iface: stype, \
-  info: snd_cmipci_uswitch_info, \
-  get: snd_cmipci_uswitch_get, \
-  put: snd_cmipci_uswitch_put, \
-  private_value: (unsigned long)&cmipci_switch_arg_##sarg,\
+{ .name = sname, \
+  .iface = stype, \
+  .info = snd_cmipci_uswitch_info, \
+  .get = snd_cmipci_uswitch_get, \
+  .put = snd_cmipci_uswitch_put, \
+  .private_value = (unsigned long)&cmipci_switch_arg_##sarg,\
 }
 
 #define DEFINE_CARD_SWITCH(sname, sarg) DEFINE_SWITCH(sname, SNDRV_CTL_ELEM_IFACE_CARD, sarg)
@@ -2232,11 +2233,11 @@
 	DEFINE_MIXER_SWITCH("IEC958 Out To DAC", spdo2dac),
 #endif
 	// DEFINE_MIXER_SWITCH("IEC958 Output Switch", spdif_enable),
-	{ name: "IEC958 Output Switch",
-	  iface: SNDRV_CTL_ELEM_IFACE_MIXER,
-	  info: snd_cmipci_uswitch_info,
-	  get: snd_cmipci_spdout_enable_get,
-	  put: snd_cmipci_spdout_enable_put,
+	{ .name = "IEC958 Output Switch",
+	  .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	  .info = snd_cmipci_uswitch_info,
+	  .get = snd_cmipci_spdout_enable_get,
+	  .put = snd_cmipci_spdout_enable_put,
 	},
 	DEFINE_MIXER_SWITCH("IEC958 In Valid", spdi_valid),
 	DEFINE_MIXER_SWITCH("IEC958 Copyright", spdif_copyright),
@@ -2257,6 +2258,7 @@
 	DEFINE_MIXER_SWITCH("Line-In As Bass", line_bass),
 	DEFINE_MIXER_SWITCH("IEC958 In Select", spdif_in_sel2),
 	DEFINE_MIXER_SWITCH("IEC958 In Phase Inverse", spdi_phase2),
+	DEFINE_MIXER_SWITCH("Mic As Center/LFE", spdi_phase), /* same bit as spdi_phase */
 };
 
 /* card control switches */
@@ -2506,7 +2508,7 @@
 	cmipci_t *cm;
 	int err;
 	static snd_device_ops_t ops = {
-		dev_free:	snd_cmipci_dev_free,
+		.dev_free =	snd_cmipci_dev_free,
 	};
 	unsigned int val = 0;
 	int pcm_index, pcm_spdif_index;
@@ -2743,10 +2745,10 @@
 
 
 static struct pci_driver driver = {
-	name: "C-Media PCI",
-	id_table: snd_cmipci_ids,
-	probe: snd_cmipci_probe,
-	remove: __devexit_p(snd_cmipci_remove),
+	.name = "C-Media PCI",
+	.id_table = snd_cmipci_ids,
+	.probe = snd_cmipci_probe,
+	.remove = __devexit_p(snd_cmipci_remove),
 };
 	
 static int __init alsa_card_cmipci_init(void)
diff -Nru a/sound/pci/cs4281.c b/sound/pci/cs4281.c
--- a/sound/pci/cs4281.c	Mon Oct  7 11:25:39 2002
+++ b/sound/pci/cs4281.c	Mon Oct  7 11:25:39 2002
@@ -362,6 +362,8 @@
 #define BA0_SRCSA		0x075c	/* SRC Slot Assignments */
 #define BA0_PPLVC		0x0760	/* PCM Playback Left Volume Control */
 #define BA0_PPRVC		0x0764	/* PCM Playback Right Volume Control */
+#define BA0_PASR		0x0768	/* playback sample rate */
+#define BA0_CASR		0x076C	/* capture sample rate */
 
 /* Source Slot Numbers - Playback */
 #define SRCSLOT_LEFT_PCM_PLAYBACK		0
@@ -465,6 +467,8 @@
 	int frag;			/* period number */
 };
 
+#define SUSPEND_REGISTERS	20
+
 struct snd_cs4281 {
 	int irq;
 
@@ -506,6 +510,11 @@
 	snd_info_entry_t *proc_entry;
 
 	struct snd_cs4281_gameport *gameport;
+
+#ifdef CONFIG_PM
+	u32 suspend_regs[SUSPEND_REGISTERS];
+#endif
+
 };
 
 static void snd_cs4281_interrupt(int irq, void *dev_id, struct pt_regs *regs);
@@ -527,23 +536,38 @@
  *  common I/O routines
  */
 
-static void snd_cs4281_delay(unsigned int delay)
+static void snd_cs4281_delay(unsigned int delay, int can_schedule)
 {
 	if (delay > 999) {
-		signed long end_time;
-		delay = (delay * HZ) / 1000000;
-		if (delay < 1)
-			delay = 1;
-		end_time = jiffies + delay;
-		do {
-			set_current_state(TASK_UNINTERRUPTIBLE);
-			schedule_timeout(1);
-	        } while (end_time - (signed long)jiffies >= 0);
+		if (can_schedule) {
+			signed long end_time;
+			delay = (delay * HZ) / 1000000;
+			if (delay < 1)
+				delay = 1;
+			end_time = jiffies + delay;
+			do {
+				set_current_state(TASK_UNINTERRUPTIBLE);
+				schedule_timeout(1);
+			} while (end_time - (signed long)jiffies >= 0);
+		} else {
+			delay += 999;
+			delay /= 1000;
+			mdelay(delay > 0 ? delay : 1);
+		}
 	} else {
 		udelay(delay);
 	}
 }
 
+inline static void snd_cs4281_delay_long(int can_schedule)
+{
+	if (can_schedule) {
+		set_current_state(TASK_UNINTERRUPTIBLE);
+		schedule_timeout(1);
+	} else
+		mdelay(10);
+}
+
 static inline void snd_cs4281_pokeBA0(cs4281_t *chip, unsigned long offset, unsigned int val)
 {
         writel(val, chip->ba0 + offset);
@@ -605,6 +629,9 @@
 	cs4281_t *chip = snd_magic_cast(cs4281_t, ac97->private_data, return -ENXIO);
 	int count;
 	unsigned short result;
+	// FIXME: volatile is necessary in the following due to a bug of
+	// some gcc versions
+	volatile int ac97_num = ((volatile ac97_t *)ac97)->num;
 
 	/*
 	 *  1. Write ACCAD = Command Address Register = 46Ch for AC97 register address
@@ -615,7 +642,7 @@
 	 *  6. Read ACSTS = Status Register = 464h, check VSTS bit
 	 */
 
-	snd_cs4281_peekBA0(chip, ac97->num ? BA0_ACSDA2 : BA0_ACSDA);
+	snd_cs4281_peekBA0(chip, ac97_num ? BA0_ACSDA2 : BA0_ACSDA);
 
 	/*
 	 *  Setup the AC97 control registers on the CS461x to send the
@@ -634,7 +661,7 @@
 	snd_cs4281_pokeBA0(chip, BA0_ACCDA, 0);
 	snd_cs4281_pokeBA0(chip, BA0_ACCTL, BA0_ACCTL_DCV | BA0_ACCTL_CRW |
 					    BA0_ACCTL_VFRM | BA0_ACCTL_ESYN |
-			   (ac97->num ? BA0_ACCTL_TC : 0));
+			   (ac97_num ? BA0_ACCTL_TC : 0));
 
 
 	/*
@@ -667,7 +694,7 @@
 		 *  ACSTS = Status Register = 464h
 		 *  VSTS - Valid Status
 		 */
-		if (snd_cs4281_peekBA0(chip, ac97->num ? BA0_ACSTS2 : BA0_ACSTS) & BA0_ACSTS_VSTS)
+		if (snd_cs4281_peekBA0(chip, ac97_num ? BA0_ACSTS2 : BA0_ACSTS) & BA0_ACSTS_VSTS)
 			goto __ok2;
 		udelay(10);
 	}
@@ -681,7 +708,7 @@
 	 *  Read the data returned from the AC97 register.
 	 *  ACSDA = Status Data Register = 474h
 	 */
-	result = snd_cs4281_peekBA0(chip, ac97->num ? BA0_ACSDA2 : BA0_ACSDA);
+	result = snd_cs4281_peekBA0(chip, ac97_num ? BA0_ACSDA2 : BA0_ACSDA);
 
       __end:
 	return result;
@@ -717,6 +744,9 @@
 		dma->valDMR &= ~(BA0_DMR_DMA|BA0_DMR_POLL);
 		dma->valDCR |= BA0_DCR_MSK;
 		dma->valFCR &= ~BA0_FCR_FEN;
+		/* Leave wave playback FIFO enabled for FM */
+		if (dma->regFCR != BA0_FCR0)
+			dma->valFCR &= ~BA0_FCR_FEN;
 		break;
 	default:
 		spin_unlock_irqrestore(&chip->reg_lock, flags);
@@ -800,12 +830,18 @@
 		}
 	}
       __skip_src:
+	/* Deactivate wave playback FIFO before changing slot assignments */
+	if (dma->regFCR == BA0_FCR0)
+		snd_cs4281_pokeBA0(chip, dma->regFCR, snd_cs4281_peekBA0(chip, dma->regFCR) & ~BA0_FCR_FEN);
 	/* Initialize FIFO */
 	dma->valFCR = BA0_FCR_LS(dma->left_slot) |
 		      BA0_FCR_RS(capture && (dma->valDMR & BA0_DMR_MONO) ? 31 : dma->right_slot) |
 		      BA0_FCR_SZ(CS4281_FIFO_SIZE) |
 		      BA0_FCR_OF(dma->fifo_offset);
 	snd_cs4281_pokeBA0(chip, dma->regFCR, dma->valFCR | (capture ? BA0_FCR_PSH : 0));
+	/* Activate FIFO again for FM playback */
+	if (dma->regFCR == BA0_FCR0)
+		snd_cs4281_pokeBA0(chip, dma->regFCR, dma->valFCR | BA0_FCR_FEN);
 	/* Clear FIFO Status and Interrupt Control Register */
 	snd_cs4281_pokeBA0(chip, dma->regFSIC, 0);
 }
@@ -855,57 +891,57 @@
 
 	// printk("DCC = 0x%x, buffer_size = 0x%x, jiffies = %li\n", snd_cs4281_peekBA0(chip, dma->regDCC), runtime->buffer_size, jiffies);
 	return runtime->buffer_size -
-	       snd_cs4281_peekBA0(chip, dma->regDCC);
+	       snd_cs4281_peekBA0(chip, dma->regDCC) - 1;
 }
 
 static snd_pcm_hardware_t snd_cs4281_playback =
 {
-	info:			(SNDRV_PCM_INFO_MMAP |
+	.info =			(SNDRV_PCM_INFO_MMAP |
 				 SNDRV_PCM_INFO_INTERLEAVED |
 				 SNDRV_PCM_INFO_MMAP_VALID |
 				 SNDRV_PCM_INFO_PAUSE |
 				 SNDRV_PCM_INFO_SYNC_START),
-	formats:		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8 |
+	.formats =		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8 |
 				SNDRV_PCM_FMTBIT_U16_LE | SNDRV_PCM_FMTBIT_S16_LE |
 				SNDRV_PCM_FMTBIT_U16_BE | SNDRV_PCM_FMTBIT_S16_BE |
 				SNDRV_PCM_FMTBIT_U32_LE | SNDRV_PCM_FMTBIT_S32_LE |
 				SNDRV_PCM_FMTBIT_U32_BE | SNDRV_PCM_FMTBIT_S32_BE,
-	rates:			SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
-	rate_min:		4000,
-	rate_max:		48000,
-	channels_min:		1,
-	channels_max:		2,
-	buffer_bytes_max:	(512*1024),
-	period_bytes_min:	64,
-	period_bytes_max:	(512*1024),
-	periods_min:		1,
-	periods_max:		2,
-	fifo_size:		CS4281_FIFO_SIZE,
+	.rates =		SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+	.rate_min =		4000,
+	.rate_max =		48000,
+	.channels_min =		1,
+	.channels_max =		2,
+	.buffer_bytes_max =	(512*1024),
+	.period_bytes_min =	64,
+	.period_bytes_max =	(512*1024),
+	.periods_min =		1,
+	.periods_max =		2,
+	.fifo_size =		CS4281_FIFO_SIZE,
 };
 
 static snd_pcm_hardware_t snd_cs4281_capture =
 {
-	info:			(SNDRV_PCM_INFO_MMAP |
+	.info =			(SNDRV_PCM_INFO_MMAP |
 				 SNDRV_PCM_INFO_INTERLEAVED |
 				 SNDRV_PCM_INFO_MMAP_VALID |
 				 SNDRV_PCM_INFO_PAUSE |
 				 SNDRV_PCM_INFO_SYNC_START),
-	formats:		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8 |
+	.formats =		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S8 |
 				SNDRV_PCM_FMTBIT_U16_LE | SNDRV_PCM_FMTBIT_S16_LE |
 				SNDRV_PCM_FMTBIT_U16_BE | SNDRV_PCM_FMTBIT_S16_BE |
 				SNDRV_PCM_FMTBIT_U32_LE | SNDRV_PCM_FMTBIT_S32_LE |
 				SNDRV_PCM_FMTBIT_U32_BE | SNDRV_PCM_FMTBIT_S32_BE,
-	rates:			SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
-	rate_min:		4000,
-	rate_max:		48000,
-	channels_min:		1,
-	channels_max:		2,
-	buffer_bytes_max:	(512*1024),
-	period_bytes_min:	64,
-	period_bytes_max:	(512*1024),
-	periods_min:		1,
-	periods_max:		2,
-	fifo_size:		CS4281_FIFO_SIZE,
+	.rates =		SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+	.rate_min =		4000,
+	.rate_max =		48000,
+	.channels_min =		1,
+	.channels_max =		2,
+	.buffer_bytes_max =	(512*1024),
+	.period_bytes_min =	64,
+	.period_bytes_max =	(512*1024),
+	.periods_min =		1,
+	.periods_max =		2,
+	.fifo_size =		CS4281_FIFO_SIZE,
 };
 
 static int snd_cs4281_playback_open(snd_pcm_substream_t * substream)
@@ -965,25 +1001,25 @@
 }
 
 static snd_pcm_ops_t snd_cs4281_playback_ops = {
-	open:		snd_cs4281_playback_open,
-	close:		snd_cs4281_playback_close,
-	ioctl:		snd_pcm_lib_ioctl,
-	hw_params:	snd_cs4281_hw_params,
-	hw_free:	snd_cs4281_hw_free,
-	prepare:	snd_cs4281_playback_prepare,
-	trigger:	snd_cs4281_trigger,
-	pointer:	snd_cs4281_pointer,
+	.open =		snd_cs4281_playback_open,
+	.close =	snd_cs4281_playback_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	snd_cs4281_hw_params,
+	.hw_free =	snd_cs4281_hw_free,
+	.prepare =	snd_cs4281_playback_prepare,
+	.trigger =	snd_cs4281_trigger,
+	.pointer =	snd_cs4281_pointer,
 };
 
 static snd_pcm_ops_t snd_cs4281_capture_ops = {
-	open:		snd_cs4281_capture_open,
-	close:		snd_cs4281_capture_close,
-	ioctl:		snd_pcm_lib_ioctl,
-	hw_params:	snd_cs4281_hw_params,
-	hw_free:	snd_cs4281_hw_free,
-	prepare:	snd_cs4281_capture_prepare,
-	trigger:	snd_cs4281_trigger,
-	pointer:	snd_cs4281_pointer,
+	.open =		snd_cs4281_capture_open,
+	.close =	snd_cs4281_capture_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	snd_cs4281_hw_params,
+	.hw_free =	snd_cs4281_hw_free,
+	.prepare =	snd_cs4281_capture_prepare,
+	.trigger =	snd_cs4281_trigger,
+	.pointer =	snd_cs4281_pointer,
 };
 
 static void snd_cs4281_pcm_free(snd_pcm_t *pcm)
@@ -1024,6 +1060,76 @@
  *  Mixer section
  */
 
+#define CS_VOL_MASK	0x1f
+
+static int snd_cs4281_info_volume(snd_kcontrol_t * kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type              = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count             = 2;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = CS_VOL_MASK;
+	return 0;
+}
+ 
+static int snd_cs4281_get_volume(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	cs4281_t *chip = snd_kcontrol_chip(kcontrol);
+	int regL = (kcontrol->private_value >> 16) & 0xffff;
+	int regR = kcontrol->private_value & 0xffff;
+	int volL, volR;
+
+	volL = CS_VOL_MASK - (snd_cs4281_peekBA0(chip, regL) & CS_VOL_MASK);
+	volR = CS_VOL_MASK - (snd_cs4281_peekBA0(chip, regR) & CS_VOL_MASK);
+
+	ucontrol->value.integer.value[0] = volL;
+	ucontrol->value.integer.value[1] = volR;
+	return 0;
+}
+
+static int snd_cs4281_put_volume(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	cs4281_t *chip = snd_kcontrol_chip(kcontrol);
+	int change = 0;
+	int regL = (kcontrol->private_value >> 16) & 0xffff;
+	int regR = kcontrol->private_value & 0xffff;
+	int volL, volR;
+
+	volL = CS_VOL_MASK - (snd_cs4281_peekBA0(chip, regL) & CS_VOL_MASK);
+	volR = CS_VOL_MASK - (snd_cs4281_peekBA0(chip, regR) & CS_VOL_MASK);
+
+	if (ucontrol->value.integer.value[0] != volL) {
+		volL = CS_VOL_MASK - (ucontrol->value.integer.value[0] & CS_VOL_MASK);
+		snd_cs4281_pokeBA0(chip, regL, volL);
+		change = 1;
+	}
+	if (ucontrol->value.integer.value[0] != volL) {
+		volR = CS_VOL_MASK - (ucontrol->value.integer.value[1] & CS_VOL_MASK);
+		snd_cs4281_pokeBA0(chip, regR, volR);
+		change = 1;
+	}
+	return change;
+}
+
+static snd_kcontrol_new_t snd_cs4281_fm_vol = 
+{
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "Synth Playback Volume",
+	.info = snd_cs4281_info_volume, 
+	.get = snd_cs4281_get_volume,
+	.put = snd_cs4281_put_volume, 
+	.private_value = ((BA0_FMLVC << 16) | BA0_FMRVC),
+};
+
+static snd_kcontrol_new_t snd_cs4281_pcm_vol = 
+{
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "PCM Stream Playback Volume",
+	.info = snd_cs4281_info_volume, 
+	.get = snd_cs4281_get_volume,
+	.put = snd_cs4281_put_volume, 
+	.private_value = ((BA0_PPLVC << 16) | BA0_PPRVC),
+};
+
 static void snd_cs4281_mixer_free_ac97(ac97_t *ac97)
 {
 	cs4281_t *chip = snd_magic_cast(cs4281_t, ac97->private_data, return);
@@ -1051,11 +1157,16 @@
 		if ((err = snd_ac97_mixer(card, &ac97, &chip->ac97_secondary)) < 0)
 			return err;
 	}
+	if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_cs4281_fm_vol, chip))) < 0)
+		return err;
+	if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_cs4281_pcm_vol, chip))) < 0)
+		return err;
 	return 0;
 }
 
-/*
 
+/*
+ * proc interface
  */
 
 static void snd_cs4281_proc_read(snd_info_entry_t *entry, 
@@ -1127,11 +1238,11 @@
 }
 
 static struct snd_info_entry_ops snd_cs4281_proc_ops_BA0 = {
-	read: snd_cs4281_BA0_read,
+	.read = snd_cs4281_BA0_read,
 };
 
 static struct snd_info_entry_ops snd_cs4281_proc_ops_BA1 = {
-	read: snd_cs4281_BA1_read,
+	.read = snd_cs4281_BA1_read,
 };
 
 static void __devinit snd_cs4281_proc_init(cs4281_t * chip)
@@ -1337,17 +1448,21 @@
 	return snd_cs4281_free(chip);
 }
 
+static int snd_cs4281_chip_init(cs4281_t *chip, int can_schedule); /* defined below */
+#ifdef CONFIG_PM
+static int snd_cs4281_set_power_state(snd_card_t *card, unsigned int power_state);
+#endif
+
 static int __devinit snd_cs4281_create(snd_card_t * card,
-				    struct pci_dev *pci,
-				    cs4281_t ** rchip,
+				       struct pci_dev *pci,
+				       cs4281_t ** rchip,
 				       int dual_codec)
 {
 	cs4281_t *chip;
 	unsigned int tmp;
-	signed long end_time;
 	int err;
 	static snd_device_ops_t ops = {
-		dev_free:	snd_cs4281_dev_free,
+		.dev_free =	snd_cs4281_dev_free,
 	};
 
 	*rchip = NULL;
@@ -1393,13 +1508,39 @@
 		return -ENOMEM;
 	}
 	
+	tmp = snd_cs4281_chip_init(chip, 1);
+	if (tmp) {
+		snd_cs4281_free(chip);
+		return tmp;
+	}
+
+	snd_cs4281_proc_init(chip);
+
+#ifdef CONFIG_PM
+	card->set_power_state = snd_cs4281_set_power_state;
+	card->power_state_private_data = chip;
+#endif
+
+	if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
+		snd_cs4281_free(chip);
+		return err;
+	}
+
+	*rchip = chip;
+	return 0;
+}
+
+static int snd_cs4281_chip_init(cs4281_t *chip, int can_schedule)
+{
+	unsigned int tmp;
+	int timeout;
+
 	tmp = snd_cs4281_peekBA0(chip, BA0_CFLR);
 	if (tmp != BA0_CFLR_DEFAULT) {
 		snd_cs4281_pokeBA0(chip, BA0_CFLR, BA0_CFLR_DEFAULT);
 		tmp = snd_cs4281_peekBA0(chip, BA0_CFLR);
 		if (tmp != BA0_CFLR_DEFAULT) {
 			snd_printk(KERN_ERR "CFLR setup failed (0x%x)\n", tmp);
-			snd_cs4281_free(chip);
 			return -EIO;
 		}
 	}
@@ -1411,12 +1552,10 @@
 	
 	if ((tmp = snd_cs4281_peekBA0(chip, BA0_SERC1)) != (BA0_SERC1_SO1EN | BA0_SERC1_AC97)) {
 		snd_printk(KERN_ERR "SERC1 AC'97 check failed (0x%x)\n", tmp);
-		snd_cs4281_free(chip);
 		return -EIO;
 	}
 	if ((tmp = snd_cs4281_peekBA0(chip, BA0_SERC2)) != (BA0_SERC2_SI1EN | BA0_SERC2_AC97)) {
 		snd_printk(KERN_ERR "SERC2 AC'97 check failed (0x%x)\n", tmp);
-		snd_cs4281_free(chip);
 		return -EIO;
 	}
 
@@ -1445,7 +1584,7 @@
 	snd_cs4281_pokeBA0(chip, BA0_SPMC, 0);
 	udelay(50);
 	snd_cs4281_pokeBA0(chip, BA0_SPMC, BA0_SPMC_RSTN);
-	snd_cs4281_delay(50000);
+	snd_cs4281_delay(50000, can_schedule);
 
 	if (chip->dual_codec)
 		snd_cs4281_pokeBA0(chip, BA0_SPMC, BA0_SPMC_RSTN | BA0_SPMC_ASDI2E);
@@ -1461,13 +1600,13 @@
 	 *  Start the DLL Clock logic.
 	 */
 	snd_cs4281_pokeBA0(chip, BA0_CLKCR1, BA0_CLKCR1_DLLP);
-	snd_cs4281_delay(50000);
+	snd_cs4281_delay(50000, can_schedule);
 	snd_cs4281_pokeBA0(chip, BA0_CLKCR1, BA0_CLKCR1_SWCE | BA0_CLKCR1_DLLP);
 
 	/*
 	 * Wait for the DLL ready signal from the clock logic.
 	 */
-	end_time = (jiffies + HZ / 4) + 1;
+	timeout = HZ;
 	do {
 		/*
 		 *  Read the AC97 status register to see if we've seen a CODEC
@@ -1475,12 +1614,10 @@
 		 */
 		if (snd_cs4281_peekBA0(chip, BA0_CLKCR1) & BA0_CLKCR1_DLLRDY)
 			goto __ok0;
-		set_current_state(TASK_UNINTERRUPTIBLE);
-		schedule_timeout(1);
-        } while (end_time - (signed long)jiffies >= 0);
+		snd_cs4281_delay_long(can_schedule);
+	} while (timeout-- > 0);
 
 	snd_printk(KERN_ERR "DLLRDY not seen\n");
-	snd_cs4281_free(chip);
 	return -EIO;
 
       __ok0:
@@ -1495,7 +1632,7 @@
 	/*
 	 * Wait for the codec ready signal from the AC97 codec.
 	 */
-	end_time = (jiffies + (3 * HZ) / 4) + 1;
+	timeout = HZ;
 	do {
 		/*
 		 *  Read the AC97 status register to see if we've seen a CODEC
@@ -1503,23 +1640,20 @@
 		 */
 		if (snd_cs4281_peekBA0(chip, BA0_ACSTS) & BA0_ACSTS_CRDY)
 			goto __ok1;
-		set_current_state(TASK_UNINTERRUPTIBLE);
-		schedule_timeout(1);
-        } while (end_time - (signed long)jiffies >= 0);
+		snd_cs4281_delay_long(can_schedule);
+	} while (timeout-- > 0);
 
 	snd_printk(KERN_ERR "never read codec ready from AC'97 (0x%x)\n", snd_cs4281_peekBA0(chip, BA0_ACSTS));
-	snd_cs4281_free(chip);
 	return -EIO;
 
       __ok1:
 	if (chip->dual_codec) {
-		end_time = (jiffies + (3 * HZ) / 4) + 1;
+		timeout = HZ;
 		do {
 			if (snd_cs4281_peekBA0(chip, BA0_ACSTS2) & BA0_ACSTS_CRDY)
 				goto __codec2_ok;
-			set_current_state(TASK_UNINTERRUPTIBLE);
-			schedule_timeout(1);
-		} while (end_time - (signed long)jiffies >= 0);
+			snd_cs4281_delay_long(can_schedule);
+		} while (timeout-- > 0);
 		snd_printk(KERN_INFO "secondary codec doesn't respond. disable it...\n");
 		chip->dual_codec = 0;
 	__codec2_ok: ;
@@ -1537,7 +1671,7 @@
 	 *  the codec is pumping ADC data across the AC-link.
 	 */
 
-	end_time = (jiffies + (5 * HZ) / 4) + 1;
+	timeout = HZ;
 	do {
 		/*
 		 *  Read the input slot valid register and see if input slots 3
@@ -1545,12 +1679,10 @@
 		 */
                 if ((snd_cs4281_peekBA0(chip, BA0_ACISV) & (BA0_ACISV_SLV(3) | BA0_ACISV_SLV(4))) == (BA0_ACISV_SLV(3) | BA0_ACISV_SLV(4)))
                         goto __ok2;
-                set_current_state(TASK_UNINTERRUPTIBLE);
-                schedule_timeout(1);
-        } while (end_time - (signed long)jiffies >= 0);
+		snd_cs4281_delay_long(can_schedule);
+	} while (timeout-- > 0);
 
 	snd_printk(KERN_ERR "never read ISV3 and ISV4 from AC'97\n");
-	snd_cs4281_free(chip);
 	return -EIO;
 
       __ok2:
@@ -1588,12 +1720,21 @@
 	chip->src_left_rec_slot = 10;	/* AC'97 left PCM record (3) */
 	chip->src_right_rec_slot = 11;	/* AC'97 right PCM record (4) */
 
+	/* Activate wave playback FIFO for FM playback */
+	chip->dma[0].valFCR = BA0_FCR_FEN | BA0_FCR_LS(0) |
+		              BA0_FCR_RS(1) |
+ 	  	              BA0_FCR_SZ(CS4281_FIFO_SIZE) |
+		              BA0_FCR_OF(chip->dma[0].fifo_offset);
+	snd_cs4281_pokeBA0(chip, chip->dma[0].regFCR, chip->dma[0].valFCR);
+	snd_cs4281_pokeBA0(chip, BA0_SRCSA, (chip->src_left_play_slot << 0) |
+					    (chip->src_right_play_slot << 8) |
+					    (chip->src_left_rec_slot << 16) |
+					    (chip->src_right_rec_slot << 24));
+
 	/* Initialize digital volume */
 	snd_cs4281_pokeBA0(chip, BA0_PPLVC, 0);
 	snd_cs4281_pokeBA0(chip, BA0_PPRVC, 0);
 
-	snd_cs4281_proc_init(chip);
-
 	/* Enable IRQs */
 	snd_cs4281_pokeBA0(chip, BA0_HICR, BA0_HICR_EOI);
 	/* Unmask interrupts */
@@ -1606,12 +1747,6 @@
 					BA0_HISR_DMA(3)));
 	synchronize_irq(chip->irq);
 
-	if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
-		snd_cs4281_free(chip);
-		return err;
-	}
-
-	*rchip = chip;
 	return 0;
 }
 
@@ -1749,16 +1884,16 @@
 
 static snd_rawmidi_ops_t snd_cs4281_midi_output =
 {
-	open:           snd_cs4281_midi_output_open,
-	close:          snd_cs4281_midi_output_close,
-	trigger:        snd_cs4281_midi_output_trigger,
+	.open =		snd_cs4281_midi_output_open,
+	.close =	snd_cs4281_midi_output_close,
+	.trigger =	snd_cs4281_midi_output_trigger,
 };
 
 static snd_rawmidi_ops_t snd_cs4281_midi_input =
 {
-	open:           snd_cs4281_midi_input_open,
-	close:          snd_cs4281_midi_input_close,
-	trigger:        snd_cs4281_midi_input_trigger,
+	.open = 	snd_cs4281_midi_input_open,
+	.close =	snd_cs4281_midi_input_close,
+	.trigger =	snd_cs4281_midi_input_trigger,
 };
 
 static int __devinit snd_cs4281_midi(cs4281_t * chip, int device, snd_rawmidi_t **rrawmidi)
@@ -1917,22 +2052,182 @@
 		return err;
 	}
 
-	pci_set_drvdata(pci, card);
+	pci_set_drvdata(pci, chip);
 	dev++;
 	return 0;
 }
 
 static void __devexit snd_cs4281_remove(struct pci_dev *pci)
 {
-	snd_card_free(pci_get_drvdata(pci));
+	cs4281_t *chip = pci_get_drvdata(pci);
+	snd_card_free(chip->card);
 	pci_set_drvdata(pci, NULL);
 }
 
+/*
+ * Power Management
+ */
+#ifdef CONFIG_PM
+
+static int saved_regs[SUSPEND_REGISTERS] = {
+	BA0_JSCTL,
+	BA0_GPIOR,
+	BA0_SSCR,
+	BA0_MIDCR,
+	BA0_SRCSA,
+	BA0_PASR,
+	BA0_CASR,
+	BA0_DACSR,
+	BA0_ADCSR,
+	BA0_FMLVC,
+	BA0_FMRVC,
+	BA0_PPLVC,
+	BA0_PPRVC,
+};
+
+#define number_of(array)	(sizeof(array) / sizeof(array[0]))
+
+#define CLKCR1_CKRA                             0x00010000L
+
+static void cs4281_suspend(cs4281_t *chip)
+{
+	snd_card_t *card = chip->card;
+	u32 ulCLK;
+	int i;
+
+	snd_power_lock(card);
+	if (card->power_state == SNDRV_CTL_POWER_D3hot)
+		goto __skip;
+
+	snd_pcm_suspend_all(chip->pcm);
+
+	ulCLK = snd_cs4281_peekBA0(chip, BA0_CLKCR1);
+	ulCLK |= CLKCR1_CKRA;
+	snd_cs4281_pokeBA0(chip, BA0_CLKCR1, ulCLK);
+
+	/* Disable interrupts. */
+	snd_cs4281_pokeBA0(chip, BA0_HICR, BA0_HICR_CHGM);
+
+	/* remember the status registers */
+	for (i = 0; number_of(saved_regs); i++)
+		if (saved_regs[i])
+			chip->suspend_regs[i] = snd_cs4281_peekBA0(chip, saved_regs[i]);
+
+	/* Turn off the serial ports. */
+	snd_cs4281_pokeBA0(chip, BA0_SERMC, 0);
+
+	/* Power off FM, Joystick, AC link, */
+	snd_cs4281_pokeBA0(chip, BA0_SSPM, 0);
+
+	/* DLL off. */
+	snd_cs4281_pokeBA0(chip, BA0_CLKCR1, 0);
+
+	/* AC link off. */
+	snd_cs4281_pokeBA0(chip, BA0_SPMC, 0);
+
+	ulCLK = snd_cs4281_peekBA0(chip, BA0_CLKCR1);
+	ulCLK &= ~CLKCR1_CKRA;
+	snd_cs4281_pokeBA0(chip, BA0_CLKCR1, ulCLK);
+
+	snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
+ __skip:
+      	snd_power_unlock(card);
+}
+
+static void cs4281_resume(cs4281_t *chip)
+{
+	snd_card_t *card = chip->card;
+	int i;
+	u32 ulCLK;
+
+	snd_power_lock(card);
+	if (card->power_state == SNDRV_CTL_POWER_D0)
+		goto __skip;
+
+	pci_enable_device(chip->pci);
+
+	ulCLK = snd_cs4281_peekBA0(chip, BA0_CLKCR1);
+	ulCLK |= CLKCR1_CKRA;
+	snd_cs4281_pokeBA0(chip, BA0_CLKCR1, ulCLK);
+
+	snd_cs4281_chip_init(chip, 0);
+
+	/* restore the status registers */
+	for (i = 0; number_of(saved_regs); i++)
+		if (saved_regs[i])
+			snd_cs4281_pokeBA0(chip, saved_regs[i], chip->suspend_regs[i]);
+
+	if (chip->ac97)
+		snd_ac97_resume(chip->ac97);
+	if (chip->ac97_secondary)
+		snd_ac97_resume(chip->ac97_secondary);
+
+	ulCLK = snd_cs4281_peekBA0(chip, BA0_CLKCR1);
+	ulCLK &= ~CLKCR1_CKRA;
+	snd_cs4281_pokeBA0(chip, BA0_CLKCR1, ulCLK);
+
+	snd_power_change_state(card, SNDRV_CTL_POWER_D0);
+      __skip:
+      	snd_power_unlock(card);
+}
+
+#ifndef PCI_OLD_SUSPEND
+static int snd_cs4281_suspend(struct pci_dev *dev, u32 state)
+{
+	cs4281_t *chip = snd_magic_cast(cs4281_t, pci_get_drvdata(dev), return -ENXIO);
+	cs4281_suspend(chip);
+	return 0;
+}
+static int snd_cs4281_resume(struct pci_dev *dev)
+{
+	cs4281_t *chip = snd_magic_cast(cs4281_t, pci_get_drvdata(dev), return -ENXIO);
+	cs4281_resume(chip);
+	return 0;
+}
+#else
+static void snd_cs4281_suspend(struct pci_dev *dev)
+{
+	cs4281_t *chip = snd_magic_cast(cs4281_t, pci_get_drvdata(dev), return);
+	cs4281_suspend(chip);
+}
+static void snd_cs4281_resume(struct pci_dev *dev)
+{
+	cs4281_t *chip = snd_magic_cast(cs4281_t, pci_get_drvdata(dev), return);
+	cs4281_resume(chip);
+}
+#endif
+
+/* callback */
+static int snd_cs4281_set_power_state(snd_card_t *card, unsigned int power_state)
+{
+	cs4281_t *chip = snd_magic_cast(cs4281_t, card->power_state_private_data, return -ENXIO);
+	switch (power_state) {
+	case SNDRV_CTL_POWER_D0:
+	case SNDRV_CTL_POWER_D1:
+	case SNDRV_CTL_POWER_D2:
+		cs4281_resume(chip);
+		break;
+	case SNDRV_CTL_POWER_D3hot:
+	case SNDRV_CTL_POWER_D3cold:
+		cs4281_suspend(chip);
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+#endif /* CONFIG_PM */
+
 static struct pci_driver driver = {
-	name: "CS4281",
-	id_table: snd_cs4281_ids,
-	probe: snd_cs4281_probe,
-	remove: __devexit_p(snd_cs4281_remove),
+	.name = "CS4281",
+	.id_table = snd_cs4281_ids,
+	.probe = snd_cs4281_probe,
+	.remove = __devexit_p(snd_cs4281_remove),
+#ifdef CONFIG_PM
+	.suspend = snd_cs4281_suspend,
+	.resume = snd_cs4281_resume,
+#endif
 };
 	
 static int __init alsa_card_cs4281_init(void)
diff -Nru a/sound/pci/cs46xx/cs46xx_lib.c b/sound/pci/cs46xx/cs46xx_lib.c
--- a/sound/pci/cs46xx/cs46xx_lib.c	Mon Oct  7 11:25:25 2002
+++ b/sound/pci/cs46xx/cs46xx_lib.c	Mon Oct  7 11:25:25 2002
@@ -4,17 +4,31 @@
  *                   Cirrus Logic, Inc.
  *  Routines for control of Cirrus Logic CS461x chips
  *
- *  BUGS:
- *    --
+ *  KNOWN BUGS:
+ *    - Sometimes the SPDIF input DSP tasks get's unsynchronized
+ *      and the SPDIF get somewhat "distorcionated", or/and left right channel
+ *      are swapped. To get around this problem when it happens, mute and unmute 
+ *      the SPDIF input mixer controll.
+ *    - On the Hercules Game Theater XP the amplifier are sometimes turned
+ *      off on inadecuate moments which causes distorcions on sound.
  *
  *  TODO:
- *    SPDIF input.
- *    Secondary CODEC on some soundcards
+ *    - Secondary CODEC on some soundcards
+ *    - SPDIF input support for other sample rates then 48khz
+ *    - Independent PCM channels for rear output
+ *    - Posibility to mix the SPDIF output with analog sources.
  *
  *  NOTE: with CONFIG_SND_CS46XX_NEW_DSP unset uses old DSP image (which
  *        is default configuration), no SPDIF, no secondary codec, no
  *        multi channel PCM.  But known to work.
  *
+ *  FINALLY: A credit to the developers Tom and Jordan 
+ *           at Cirrus for have helping me out with the DSP, however we
+ *           still dont have sufficient documentation and technical
+ *           references to be able to implement all fancy feutures
+ *           supported by the cs46xx DPS's. 
+ *           Benny <benny@hostmobility.com>
+ *                
  *   This program is free software; you can redistribute it and/or modify
  *   it under the terms of the GNU General Public License as published by
  *   the Free Software Foundation; either version 2 of the License, or
@@ -31,7 +45,6 @@
  *
  */
 
-#define __NO_VERSION__
 #include <sound/driver.h>
 #include <asm/io.h>
 #include <linux/delay.h>
@@ -47,6 +60,7 @@
 #include <linux/gameport.h>
 #endif
 #include "cs46xx_lib.h"
+#include "dsp_spos.h"
 
 static unsigned short snd_cs46xx_codec_read(cs46xx_t *chip,
 					    unsigned short reg,
@@ -292,7 +306,11 @@
 	 */
 	 
 	/* CD mute change ? */
-	
+
+	/* Benny: this hack dont seems to make any sense to me, at least on the Game Theater XP,
+	   Turning of the amplifier just make the PCM sound very distorcionated.
+	   is this really needed ????????????????
+	*/
 	if (reg == AC97_CD) {
 		/* Mute bit change ? */
 		if ((val2^val)&0x8000 || ((val2 == 0x1f1f || val == 0x1f1f) && val2 != val)) {
@@ -829,12 +847,15 @@
 	char *hwbuf;
 	cs46xx_pcm_t *cpcm = snd_magic_cast(cs46xx_pcm_t, substream->runtime->private_data, return -ENXIO);
 
+	snd_assert(runtime->dma_area, return -EINVAL);
+
 	hwoffb = hwoff << cpcm->shift;
 	bytes = frames << cpcm->shift;
 	hwbuf = runtime->dma_area + hwoffb;
 
 	if (copy_from_user(hwbuf, src, bytes))
 		return -EFAULT;
+
 	spin_lock_irq(&runtime->lock);
 	snd_cs46xx_playback_transfer(substream, frames);
 	spin_unlock_irq(&runtime->lock);
@@ -865,15 +886,17 @@
 {
 	cs46xx_t *chip = snd_pcm_substream_chip(substream);
 	/*snd_pcm_runtime_t *runtime = substream->runtime;*/
+	int result = 0;
+
 #ifdef CONFIG_SND_CS46XX_NEW_DSP
 	cs46xx_pcm_t *cpcm = snd_magic_cast(cs46xx_pcm_t, substream->runtime->private_data, return -ENXIO);
+#else
+	spin_lock(&chip->reg_lock);
 #endif
-	int result = 0;
 
-	spin_lock(&chip->reg_lock);
 #ifdef CONFIG_SND_CS46XX_NEW_DSP
+
 	if (! cpcm->pcm_channel) {
-		spin_unlock(&chip->reg_lock);
 		return -ENXIO;
 	}
 #endif
@@ -885,6 +908,9 @@
 			cs46xx_dsp_pcm_link(chip,cpcm->pcm_channel);
 		if (substream->runtime->periods != CS46XX_FRAGS)
 			snd_cs46xx_playback_transfer(substream, 0);
+
+		/* raise playback volume */
+		snd_cs46xx_poke(chip, (cpcm->pcm_channel->pcm_reader_scb->address + 0xE) << 2, 0x80008000);
 #else
 		if (substream->runtime->periods != CS46XX_FRAGS)
 			snd_cs46xx_playback_transfer(substream, 0);
@@ -898,6 +924,8 @@
 	case SNDRV_PCM_TRIGGER_STOP:
 	case SNDRV_PCM_TRIGGER_SUSPEND:
 #ifdef CONFIG_SND_CS46XX_NEW_DSP
+        /* mute channel */
+		snd_cs46xx_poke(chip, (cpcm->pcm_channel->pcm_reader_scb->address + 0xE) << 2, 0xffffffff);
 		if (!cpcm->pcm_channel->unlinked)
 			cs46xx_dsp_pcm_unlink(chip,cpcm->pcm_channel);
 #else
@@ -912,7 +940,11 @@
 		result = -EINVAL;
 		break;
 	}
+
+#ifndef CONFIG_SND_CS46XX_NEW_DSP
 	spin_unlock(&chip->reg_lock);
+#endif
+
 	return result;
 }
 
@@ -942,6 +974,7 @@
 		break;
 	}
 	spin_unlock(&chip->reg_lock);
+
 	return result;
 }
 
@@ -972,6 +1005,7 @@
 			return err;
 		substream->ops = &snd_cs46xx_playback_indirect_ops;
 	}
+
 	return 0;
 }
 
@@ -989,6 +1023,7 @@
 	runtime->dma_area = NULL;
 	runtime->dma_addr = 0;
 	runtime->dma_bytes = 0;
+
 	return 0;
 }
 
@@ -1003,7 +1038,8 @@
 	cpcm = snd_magic_cast(cs46xx_pcm_t, runtime->private_data, return -ENXIO);
 
 #ifdef CONFIG_SND_CS46XX_NEW_DSP
-	down (&chip->dsp_spos_instance->pcm_mutex);
+    down (&chip->spos_mutex);
+
 	if ( cpcm->pcm_channel->src_scb->ref_count == 1 &&
 	     cpcm->pcm_channel->sample_rate != runtime->rate) {
 		/* sample rate not set or we can reuse
@@ -1012,23 +1048,25 @@
 		cs46xx_dsp_set_src_sample_rate (chip,cpcm->pcm_channel->src_scb,runtime->rate);
 		cpcm->pcm_channel->sample_rate = runtime->rate;
 	} 
-	up (&chip->dsp_spos_instance->pcm_mutex);
 
 	if (cpcm->pcm_channel->sample_rate != runtime->rate &&
 	    cpcm->pcm_channel->src_scb->ref_count != 1) {
+		int unlinked = cpcm->pcm_channel->unlinked;
 		cs46xx_dsp_destroy_pcm_channel (chip,cpcm->pcm_channel);
 
-		if ( (cpcm->pcm_channel = cs46xx_dsp_create_pcm_channel (chip,runtime->rate,cpcm)) == NULL) {
+		if ( (cpcm->pcm_channel = cs46xx_dsp_create_pcm_channel (chip, runtime->rate, cpcm, cpcm->hw_addr)) == NULL) {
 			snd_printk(KERN_ERR "cs46xx: failed to re-create virtual PCM channel\n");
+			up (&chip->spos_mutex);
 			return -ENXIO;
 		}
 
-		cs46xx_dsp_pcm_unlink (chip,cpcm->pcm_channel);
+		if (!unlinked) cs46xx_dsp_pcm_link (chip,cpcm->pcm_channel);
 		cpcm->pcm_channel->sample_rate = runtime->rate;
 	}
 
 	pfie = snd_cs46xx_peek(chip, (cpcm->pcm_channel->pcm_reader_scb->address + 1) << 2 );
 	pfie &= ~0x0000f03f;
+	up (&chip->spos_mutex);
 #else
 	/* old dsp */
 	pfie = snd_cs46xx_peek(chip, BA1_PFIE);
@@ -1063,9 +1101,6 @@
 	cpcm->appl_ptr = 0;
 
 #ifdef CONFIG_SND_CS46XX_NEW_DSP
-	/* playback address */
-	snd_cs46xx_poke(chip, (cpcm->pcm_channel->pcm_reader_scb->address + 2) << 2, cpcm->hw_addr);
-
 	tmp = snd_cs46xx_peek(chip, (cpcm->pcm_channel->pcm_reader_scb->address) << 2);
 	tmp &= ~0x000003ff;
 	tmp |= (4 << cpcm->shift) - 1;
@@ -1111,6 +1146,7 @@
 			return err;
 		substream->ops = &snd_cs46xx_capture_indirect_ops;
 	}
+
 	return 0;
 }
 
@@ -1124,6 +1160,7 @@
 	runtime->dma_area = NULL;
 	runtime->dma_addr = 0;
 	runtime->dma_bytes = 0;
+
 	return 0;
 }
 
@@ -1139,6 +1176,7 @@
 	chip->capt.hw_data = chip->capt.hw_io = chip->capt.hw_ready = 0;
 	chip->capt.appl_ptr = 0;
 	snd_cs46xx_set_capture_sample_rate(chip, runtime->rate);
+
 	return 0;
 }
 
@@ -1164,6 +1202,7 @@
 
 #ifdef CONFIG_SND_CS46XX_NEW_DSP
 	status2 = snd_cs46xx_peekBA0(chip, BA0_HSR0);
+
 	for (i = 0; i < DSP_MAX_PCM_CHANNELS; ++i) {
 		if (i <= 15) {
 			if ( status1 & (1 << i) ) {
@@ -1174,7 +1213,7 @@
 					if (ins->pcm_channels[i].active &&
 					    ins->pcm_channels[i].private_data &&
 					    !ins->pcm_channels[i].unlinked) {
-						cpcm = snd_magic_cast(cs46xx_pcm_t, ins->pcm_channels[i].private_data, goto _invalid_pointer);
+						cpcm = snd_magic_cast(cs46xx_pcm_t, ins->pcm_channels[i].private_data, continue);
 						snd_pcm_period_elapsed(cpcm->substream);
 					}
 				}
@@ -1184,16 +1223,13 @@
 				if (ins->pcm_channels[i].active && 
 				    ins->pcm_channels[i].private_data &&
 				    !ins->pcm_channels[i].unlinked) {
-					cpcm = snd_magic_cast(cs46xx_pcm_t, ins->pcm_channels[i].private_data, goto _invalid_pointer);
+					cpcm = snd_magic_cast(cs46xx_pcm_t, ins->pcm_channels[i].private_data, continue);
 					snd_pcm_period_elapsed(cpcm->substream);
 				}
 			}
 		}
-
-		continue;
-	_invalid_pointer:
-		printk (KERN_ERR "cs46xx: (interrupt) invalid pointer at pcm_channel[%d]\n",i);
 	}
+
 #else
 	/* old dsp */
 	if ((status1 & HISR_VC0) && chip->playback_pcm) {
@@ -1236,44 +1272,44 @@
 
 static snd_pcm_hardware_t snd_cs46xx_playback =
 {
-	info:			(SNDRV_PCM_INFO_MMAP |
+	.info =			(SNDRV_PCM_INFO_MMAP |
 				 SNDRV_PCM_INFO_INTERLEAVED | 
 				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
 				 SNDRV_PCM_INFO_RESUME),
-	formats:		(SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 |
+	.formats =		(SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 |
 				 SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE |
 				 SNDRV_PCM_FMTBIT_U16_LE | SNDRV_PCM_FMTBIT_U16_BE),
-	.rates			= SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
-	.rate_min		= 5500,
-	.rate_max		= 48000,
-	.channels_min		= 1,
-	.channels_max		= 2,
-	.buffer_bytes_max	= (256 * 1024),
-	.period_bytes_min	= CS46XX_PERIOD_SIZE,
-	.period_bytes_max	= CS46XX_PERIOD_SIZE,
-	.periods_min		= CS46XX_FRAGS,
-	.periods_max		= 1024,
-	.fifo_size		= 0,
+	.rates =		SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+	.rate_min =		5500,
+	.rate_max =		48000,
+	.channels_min =		1,
+	.channels_max =		2,
+	.buffer_bytes_max =	(256 * 1024),
+	.period_bytes_min =	CS46XX_PERIOD_SIZE,
+	.period_bytes_max =	CS46XX_PERIOD_SIZE,
+	.periods_min =		CS46XX_FRAGS,
+	.periods_max =		1024,
+	.fifo_size =		0,
 };
 
 static snd_pcm_hardware_t snd_cs46xx_capture =
 {
-	info:			(SNDRV_PCM_INFO_MMAP |
+	.info =			(SNDRV_PCM_INFO_MMAP |
 				 SNDRV_PCM_INFO_INTERLEAVED |
 				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
 				 SNDRV_PCM_INFO_RESUME),
-	.formats		= SNDRV_PCM_FMTBIT_S16_LE,
-	.rates			= SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
-	.rate_min		= 5500,
-	.rate_max		= 48000,
-	.channels_min		= 2,
-	.channels_max		= 2,
-	.buffer_bytes_max	= (256 * 1024),
-	.period_bytes_min	= CS46XX_PERIOD_SIZE,
-	.period_bytes_max	= CS46XX_PERIOD_SIZE,
-	.periods_min		= CS46XX_FRAGS,
-	.periods_max		= 1024,
-	.fifo_size		= 0,
+	.formats =		SNDRV_PCM_FMTBIT_S16_LE,
+	.rates =		SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+	.rate_min =		5500,
+	.rate_max =		48000,
+	.channels_min =		2,
+	.channels_max =		2,
+	.buffer_bytes_max =	(256 * 1024),
+	.period_bytes_min =	CS46XX_PERIOD_SIZE,
+	.period_bytes_max =	CS46XX_PERIOD_SIZE,
+	.periods_min =		CS46XX_FRAGS,
+	.periods_max =		1024,
+	.fifo_size =		0,
 };
 
 static void snd_cs46xx_pcm_free_substream(snd_pcm_runtime_t *runtime)
@@ -1305,16 +1341,18 @@
 
 	cpcm->substream = substream;
 #ifdef CONFIG_SND_CS46XX_NEW_DSP
-	cpcm->pcm_channel = cs46xx_dsp_create_pcm_channel (chip,runtime->rate,cpcm);
+	down (&chip->spos_mutex);
+	cpcm->pcm_channel = cs46xx_dsp_create_pcm_channel (chip, runtime->rate, cpcm, cpcm->hw_addr);
 
 	if (cpcm->pcm_channel == NULL) {
 		snd_printk(KERN_ERR "cs46xx: failed to create virtual PCM channel\n");
 		snd_free_pci_pages(chip->pci, cpcm->hw_size, cpcm->hw_area, cpcm->hw_addr);
 		snd_magic_kfree(cpcm);
+		up (&chip->spos_mutex);
 		return -ENOMEM;
 	}
 
-	cs46xx_dsp_pcm_unlink (chip,cpcm->pcm_channel);
+	up (&chip->spos_mutex);
 #else
 	chip->playback_pcm = cpcm; /* HACK */
 #endif
@@ -1323,6 +1361,7 @@
 		substream->runtime->hw.info |= SNDRV_PCM_INFO_MMAP_VALID;
 	chip->active_ctrl(chip, 1);
 	chip->amplifier_ctrl(chip, 1);
+
 	return 0;
 }
 
@@ -1334,10 +1373,13 @@
 		return -ENOMEM;
 	chip->capt.substream = substream;
 	substream->runtime->hw = snd_cs46xx_capture;
+
 	if (chip->accept_valid)
-		substream->runtime->hw.info |= SNDRV_PCM_INFO_MMAP_VALID;
+      substream->runtime->hw.info |= SNDRV_PCM_INFO_MMAP_VALID;
+
 	chip->active_ctrl(chip, 1);
 	chip->amplifier_ctrl(chip, 1);
+
 	return 0;
 }
 
@@ -1350,10 +1392,12 @@
 	cpcm = snd_magic_cast(cs46xx_pcm_t, runtime->private_data, return -ENXIO);
 
 #ifdef CONFIG_SND_CS46XX_NEW_DSP
+	down (&chip->spos_mutex);
 	if (cpcm->pcm_channel) {
 		cs46xx_dsp_destroy_pcm_channel(chip,cpcm->pcm_channel);
 		cpcm->pcm_channel = NULL;
 	}
+	up (&chip->spos_mutex);
 #else
 	chip->playback_pcm = NULL;
 #endif
@@ -1362,6 +1406,7 @@
 	snd_free_pci_pages(chip->pci, cpcm->hw_size, cpcm->hw_area, cpcm->hw_addr);
 	chip->active_ctrl(chip, -1);
 	chip->amplifier_ctrl(chip, -1);
+
 	return 0;
 }
 
@@ -1373,53 +1418,54 @@
 	snd_free_pci_pages(chip->pci, chip->capt.hw_size, chip->capt.hw_area, chip->capt.hw_addr);
 	chip->active_ctrl(chip, -1);
 	chip->amplifier_ctrl(chip, -1);
+
 	return 0;
 }
 
 snd_pcm_ops_t snd_cs46xx_playback_ops = {
-	.open			= snd_cs46xx_playback_open,
-	.close			= snd_cs46xx_playback_close,
-	.ioctl			= snd_pcm_lib_ioctl,
-	.hw_params		= snd_cs46xx_playback_hw_params,
-	.hw_free		= snd_cs46xx_playback_hw_free,
-	.prepare		= snd_cs46xx_playback_prepare,
-	.trigger		= snd_cs46xx_playback_trigger,
-	.pointer		= snd_cs46xx_playback_direct_pointer,
+	.open =			snd_cs46xx_playback_open,
+	.close =		snd_cs46xx_playback_close,
+	.ioctl =		snd_pcm_lib_ioctl,
+	.hw_params =		snd_cs46xx_playback_hw_params,
+	.hw_free =		snd_cs46xx_playback_hw_free,
+	.prepare =		snd_cs46xx_playback_prepare,
+	.trigger =		snd_cs46xx_playback_trigger,
+	.pointer =		snd_cs46xx_playback_direct_pointer,
 };
 
 snd_pcm_ops_t snd_cs46xx_playback_indirect_ops = {
-	.open			= snd_cs46xx_playback_open,
-	.close			= snd_cs46xx_playback_close,
-	.ioctl			= snd_pcm_lib_ioctl,
-	.hw_params		= snd_cs46xx_playback_hw_params,
-	.hw_free		= snd_cs46xx_playback_hw_free,
-	.prepare		= snd_cs46xx_playback_prepare,
-	.trigger		= snd_cs46xx_playback_trigger,
-	.copy			= snd_cs46xx_playback_copy,
-	.pointer		= snd_cs46xx_playback_indirect_pointer,
+	.open =			snd_cs46xx_playback_open,
+	.close =		snd_cs46xx_playback_close,
+	.ioctl =		snd_pcm_lib_ioctl,
+	.hw_params =		snd_cs46xx_playback_hw_params,
+	.hw_free =		snd_cs46xx_playback_hw_free,
+	.prepare =		snd_cs46xx_playback_prepare,
+	.trigger =		snd_cs46xx_playback_trigger,
+	.copy =			snd_cs46xx_playback_copy,
+	.pointer =		snd_cs46xx_playback_indirect_pointer,
 };
 
 snd_pcm_ops_t snd_cs46xx_capture_ops = {
-	.open			= snd_cs46xx_capture_open,
-	.close			= snd_cs46xx_capture_close,
-	.ioctl			= snd_pcm_lib_ioctl,
-	.hw_params		= snd_cs46xx_capture_hw_params,
-	.hw_free		= snd_cs46xx_capture_hw_free,
-	.prepare		= snd_cs46xx_capture_prepare,
-	.trigger		= snd_cs46xx_capture_trigger,
-	.pointer		= snd_cs46xx_capture_direct_pointer,
+	.open =			snd_cs46xx_capture_open,
+	.close =		snd_cs46xx_capture_close,
+	.ioctl =		snd_pcm_lib_ioctl,
+	.hw_params =		snd_cs46xx_capture_hw_params,
+	.hw_free =		snd_cs46xx_capture_hw_free,
+	.prepare =		snd_cs46xx_capture_prepare,
+	.trigger =		snd_cs46xx_capture_trigger,
+	.pointer =		snd_cs46xx_capture_direct_pointer,
 };
 
 snd_pcm_ops_t snd_cs46xx_capture_indirect_ops = {
-	.open			= snd_cs46xx_capture_open,
-	.close			= snd_cs46xx_capture_close,
-	.ioctl			= snd_pcm_lib_ioctl,
-	.hw_params		= snd_cs46xx_capture_hw_params,
-	.hw_free		= snd_cs46xx_capture_hw_free,
-	.prepare		= snd_cs46xx_capture_prepare,
-	.trigger		= snd_cs46xx_capture_trigger,
-	.copy			= snd_cs46xx_capture_copy,
-	.pointer		= snd_cs46xx_capture_indirect_pointer,
+	.open =			snd_cs46xx_capture_open,
+	.close =		snd_cs46xx_capture_close,
+	.ioctl =		snd_pcm_lib_ioctl,
+	.hw_params =		snd_cs46xx_capture_hw_params,
+	.hw_free =		snd_cs46xx_capture_hw_free,
+	.prepare =		snd_cs46xx_capture_prepare,
+	.trigger =		snd_cs46xx_capture_trigger,
+	.copy =			snd_cs46xx_capture_copy,
+	.pointer =		snd_cs46xx_capture_indirect_pointer,
 };
 
 static void snd_cs46xx_pcm_free(snd_pcm_t *pcm)
@@ -1510,14 +1556,34 @@
 			    (0xffff - ucontrol->value.integer.value[1]));
 	unsigned int old = snd_cs46xx_peek(chip, reg);
 	int change = (old != val);
-	if (change)
+	if (change) {
 		snd_cs46xx_poke(chip, reg, val);
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+		/* NOTE: this updates the current left and right volume
+		   that should be automatically updated by the DSP and
+		   not touched by the host. But for some strange reason
+		   the DSP only updates the right channel volume, so with
+		   this dirty hack we force updating the right and left
+		   channel volume. 
+		*/
+		snd_cs46xx_poke(chip, reg + 4, val);
+
+		/* shadow the SPDIF input volume */
+		if (reg == (ASYNCRX_SCB_ADDR + 0xE) << 2) {
+			/* FIXME: I known this is uggly ...
+			   any other suggestion ? 
+			*/
+			chip->dsp_spos_instance->spdif_input_volume = val;
+		}
+#endif
+	}
+
 	return change;
 }
 
 #ifdef CONFIG_SND_CS46XX_NEW_DSP
 
-static int snd_cs46xx_iec958_info(snd_kcontrol_t *kcontrol, 
+static int snd_mixer_boolean_info(snd_kcontrol_t *kcontrol, 
 				  snd_ctl_elem_info_t *uinfo)
 {
 	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
@@ -1531,7 +1597,13 @@
                                  snd_ctl_elem_value_t *ucontrol)
 {
 	cs46xx_t *chip = snd_kcontrol_chip(kcontrol);
-	ucontrol->value.integer.value[0] = chip->dsp_spos_instance->spdif_status_out;
+	int reg = kcontrol->private_value;
+
+	if (reg == CS46XX_MIXER_SPDIF_OUTPUT_ELEMENT)
+		ucontrol->value.integer.value[0] = chip->dsp_spos_instance->spdif_status_out;
+	else
+		ucontrol->value.integer.value[0] = chip->dsp_spos_instance->spdif_status_in;
+
 	return 0;
 }
 
@@ -1539,17 +1611,208 @@
                                   snd_ctl_elem_value_t *ucontrol)
 {
 	cs46xx_t *chip = snd_kcontrol_chip(kcontrol);
-	int change = chip->dsp_spos_instance->spdif_status_out;
+	int change, res;
+
+	switch (kcontrol->private_value) {
+	case CS46XX_MIXER_SPDIF_OUTPUT_ELEMENT:
+		change = chip->dsp_spos_instance->spdif_status_out;
+		if (ucontrol->value.integer.value[0] && !change) 
+			cs46xx_dsp_enable_spdif_out(chip);
+		else if (change && !ucontrol->value.integer.value[0])
+			cs46xx_dsp_disable_spdif_out(chip);
 
-	if (ucontrol->value.integer.value[0] && !change) 
-		cs46xx_dsp_enable_spdif_out(chip);
-	else if (change)
-		cs46xx_dsp_disable_spdif_out(chip);
-	return (change != chip->dsp_spos_instance->spdif_status_out);
+		res = (change != chip->dsp_spos_instance->spdif_status_out);
+		break;
+	case CS46XX_MIXER_SPDIF_INPUT_ELEMENT:
+		change = chip->dsp_spos_instance->spdif_status_in;
+		if (ucontrol->value.integer.value[0] && !change) {
+			cs46xx_dsp_enable_spdif_in(chip);
+			/* restore volume */
+		}
+		else if (change && !ucontrol->value.integer.value[0])
+			cs46xx_dsp_disable_spdif_in(chip);
+		
+		res = (change != chip->dsp_spos_instance->spdif_status_in);
+		break;
+	default:
+		snd_assert(0, return -EINVAL);
+	}
+
+	return res;
+}
+
+static int snd_cs46xx_adc_capture_get(snd_kcontrol_t *kcontrol, 
+                                      snd_ctl_elem_value_t *ucontrol)
+{
+	cs46xx_t *chip = snd_kcontrol_chip(kcontrol);
+	dsp_spos_instance_t * ins = chip->dsp_spos_instance;
+
+	if (ins->adc_input != NULL) 
+		ucontrol->value.integer.value[0] = 1;
+	else 
+		ucontrol->value.integer.value[0] = 0;
+	
+	return 0;
+}
+
+static int snd_cs46xx_adc_capture_put(snd_kcontrol_t *kcontrol, 
+                                      snd_ctl_elem_value_t *ucontrol)
+{
+	cs46xx_t *chip = snd_kcontrol_chip(kcontrol);
+	dsp_spos_instance_t * ins = chip->dsp_spos_instance;
+	int change = 0;
+
+	if (ucontrol->value.integer.value[0] && !ins->adc_input) {
+		cs46xx_dsp_enable_adc_capture(chip);
+		change = 1;
+	} else  if (!ucontrol->value.integer.value[0] && ins->adc_input) {
+		cs46xx_dsp_disable_adc_capture(chip);
+		change = 1;
+	}
+	return change;
+}
+
+static int snd_cs46xx_pcm_capture_get(snd_kcontrol_t *kcontrol, 
+                                      snd_ctl_elem_value_t *ucontrol)
+{
+	cs46xx_t *chip = snd_kcontrol_chip(kcontrol);
+	dsp_spos_instance_t * ins = chip->dsp_spos_instance;
+
+	if (ins->pcm_input != NULL) 
+		ucontrol->value.integer.value[0] = 1;
+	else 
+		ucontrol->value.integer.value[0] = 0;
+
+	return 0;
+}
+
+static int snd_cs46xx_pcm_capture_put(snd_kcontrol_t *kcontrol, 
+                                      snd_ctl_elem_value_t *ucontrol)
+{
+	cs46xx_t *chip = snd_kcontrol_chip(kcontrol);
+	dsp_spos_instance_t * ins = chip->dsp_spos_instance;
+	int change = 0;
+
+	if (ucontrol->value.integer.value[0] && !ins->pcm_input) {
+		cs46xx_dsp_enable_pcm_capture(chip);
+		change = 1;
+	} else  if (!ucontrol->value.integer.value[0] && ins->pcm_input) {
+		cs46xx_dsp_disable_pcm_capture(chip);
+		change = 1;
+	}
+
+	return change;
+}
+
+static int snd_herc_spdif_select_get(snd_kcontrol_t *kcontrol, 
+                                     snd_ctl_elem_value_t *ucontrol)
+{
+	cs46xx_t *chip = snd_kcontrol_chip(kcontrol);
+
+	int val1 = snd_cs46xx_peekBA0(chip, BA0_EGPIODR);
+
+	if (val1 & EGPIODR_GPOE0)
+		ucontrol->value.integer.value[0] = 1;
+	else
+		ucontrol->value.integer.value[0] = 0;
+
+	return 0;
+}
+
+/*
+ *	Game Theatre XP card - EGPIO[0] is used to select SDPIF input optical or coaxial.
+ */ 
+static int snd_herc_spdif_select_put(snd_kcontrol_t *kcontrol, 
+                                       snd_ctl_elem_value_t *ucontrol)
+{
+	cs46xx_t *chip = snd_kcontrol_chip(kcontrol);
+	int val1 = snd_cs46xx_peekBA0(chip, BA0_EGPIODR);
+	int val2 = snd_cs46xx_peekBA0(chip, BA0_EGPIOPTR);
+
+	if (ucontrol->value.integer.value[0]) {
+		/* optical is default */
+		snd_cs46xx_pokeBA0(chip, BA0_EGPIODR, 
+				   EGPIODR_GPOE0 | val1);  /* enable EGPIO0 output */
+		snd_cs46xx_pokeBA0(chip, BA0_EGPIOPTR, 
+				   EGPIOPTR_GPPT0 | val2); /* open-drain on output */
+	} else {
+		/* coaxial */
+		snd_cs46xx_pokeBA0(chip, BA0_EGPIODR,  val1 & ~EGPIODR_GPOE0); /* disable */
+		snd_cs46xx_pokeBA0(chip, BA0_EGPIOPTR, val2 & ~EGPIOPTR_GPPT0); /* disable */
+	}
+
+	/* checking diff from the EGPIO direction register 
+	   should be enough */
+	return (val1 != snd_cs46xx_peekBA0(chip, BA0_EGPIODR));
 }
 
 #endif /* CONFIG_SND_CS46XX_NEW_DSP */
 
+#ifdef CONFIG_SND_CS46XX_DEBUG_GPIO
+static int snd_cs46xx_egpio_select_info(snd_kcontrol_t *kcontrol, 
+                                        snd_ctl_elem_info_t *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 8;
+	return 0;
+}
+
+static int snd_cs46xx_egpio_select_get(snd_kcontrol_t *kcontrol, 
+                                       snd_ctl_elem_value_t *ucontrol)
+{
+	cs46xx_t *chip = snd_kcontrol_chip(kcontrol);
+	ucontrol->value.integer.value[0] = chip->current_gpio;
+
+	return 0;
+}
+
+static int snd_cs46xx_egpio_select_put(snd_kcontrol_t *kcontrol, 
+                                       snd_ctl_elem_value_t *ucontrol)
+{
+	cs46xx_t *chip = snd_kcontrol_chip(kcontrol);
+	int change = (chip->current_gpio != ucontrol->value.integer.value[0]);
+	chip->current_gpio = ucontrol->value.integer.value[0];
+
+	return change;
+}
+
+
+static int snd_cs46xx_egpio_get(snd_kcontrol_t *kcontrol, 
+                                       snd_ctl_elem_value_t *ucontrol)
+{
+	cs46xx_t *chip = snd_kcontrol_chip(kcontrol);
+	int reg = kcontrol->private_value;
+
+	snd_printdd ("put: reg = %04x, gpio %02x\n",reg,chip->current_gpio);
+	ucontrol->value.integer.value[0] = 
+		(snd_cs46xx_peekBA0(chip, reg) & (1 << chip->current_gpio)) ? 1 : 0;
+  
+	return 0;
+}
+
+static int snd_cs46xx_egpio_put(snd_kcontrol_t *kcontrol, 
+                                       snd_ctl_elem_value_t *ucontrol)
+{
+	cs46xx_t *chip = snd_kcontrol_chip(kcontrol);
+	int reg = kcontrol->private_value;
+	int val = snd_cs46xx_peekBA0(chip, reg);
+	int oldval = val;
+	snd_printdd ("put: reg = %04x, gpio %02x\n",reg,chip->current_gpio);
+
+	if (ucontrol->value.integer.value[0])
+		val |= (1 << chip->current_gpio);
+	else
+		val &= ~(1 << chip->current_gpio);
+
+	snd_cs46xx_pokeBA0(chip, reg,val);
+	snd_printdd ("put: val %08x oldval %08x\n",val,oldval);
+
+	return (oldval != val);
+}
+#endif /* CONFIG_SND_CS46XX_DEBUG_GPIO */
+
 static snd_kcontrol_new_t snd_cs46xx_controls[] __devinitdata = {
 {
 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -1557,26 +1820,114 @@
 	.info = snd_cs46xx_vol_info,
 	.get = snd_cs46xx_vol_get,
 	.put = snd_cs46xx_vol_put,
+
+#ifndef CONFIG_SND_CS46XX_NEW_DSP
 	.private_value = BA1_PVOL,
+#else
+	.private_value = (MASTERMIX_SCB_ADDR + 0xE) << 2,
+#endif
 },
+
 {
 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 	.name = "ADC Volume",
 	.info = snd_cs46xx_vol_info,
 	.get = snd_cs46xx_vol_get,
 	.put = snd_cs46xx_vol_put,
+#ifndef CONFIG_SND_CS46XX_NEW_DSP
 	.private_value = BA1_CVOL,
+#else
+	.private_value = (VARIDECIMATE_SCB_ADDR + 0xE) << 2,
+#endif
 },
 #ifdef CONFIG_SND_CS46XX_NEW_DSP
 {
 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-	.name = "IEC 958 output",
-	.info = snd_cs46xx_iec958_info,
+	.name = "ADC Capture Switch",
+	.info = snd_mixer_boolean_info,
+	.get = snd_cs46xx_adc_capture_get,
+	.put = snd_cs46xx_adc_capture_put
+},
+{
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "DAC Capture Switch",
+	.info = snd_mixer_boolean_info,
+	.get = snd_cs46xx_pcm_capture_get,
+	.put = snd_cs46xx_pcm_capture_put
+},
+{
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "IEC 958 Output Switch",
+	.info = snd_mixer_boolean_info,
+	.get = snd_cs46xx_iec958_get,
+	.put = snd_cs46xx_iec958_put,
+	.private_value = CS46XX_MIXER_SPDIF_OUTPUT_ELEMENT,
+},
+{
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "IEC 958 Input Switch",
+	.info = snd_mixer_boolean_info,
 	.get = snd_cs46xx_iec958_get,
 	.put = snd_cs46xx_iec958_put,
+	.private_value = CS46XX_MIXER_SPDIF_INPUT_ELEMENT,
+},
+{
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "IEC 958 Input Volume",
+	.info = snd_cs46xx_vol_info,
+	.get = snd_cs46xx_vol_get,
+	.put = snd_cs46xx_vol_put,
+	.private_value = (ASYNCRX_SCB_ADDR + 0xE) << 2,
 },
 #endif
-	};
+#ifdef CONFIG_SND_CS46XX_DEBUG_GPIO
+{
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "EGPIO select",
+	.info = snd_cs46xx_egpio_select_info,
+	.get = snd_cs46xx_egpio_select_get,
+	.put = snd_cs46xx_egpio_select_put,
+	.private_value = 0,
+},
+{
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "EGPIO Input/Output",
+	.info = snd_mixer_boolean_info,
+	.get = snd_cs46xx_egpio_get,
+	.put = snd_cs46xx_egpio_put,
+	.private_value = BA0_EGPIODR,
+},
+{
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "EGPIO CMOS/Open drain",
+	.info = snd_mixer_boolean_info,
+	.get = snd_cs46xx_egpio_get,
+	.put = snd_cs46xx_egpio_put,
+	.private_value = BA0_EGPIOPTR,
+},
+{
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "EGPIO On/Off",
+	.info = snd_mixer_boolean_info,
+	.get = snd_cs46xx_egpio_get,
+	.put = snd_cs46xx_egpio_put,
+	.private_value = BA0_EGPIOSR,
+},
+#endif
+};
+
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+/* Only available on the Hercules Game Theater XP soundcard */
+static snd_kcontrol_new_t snd_hercules_controls[] __devinitdata = {
+{
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "Optical/Coaxial SPDIF Input Switch",
+	.info = snd_mixer_boolean_info,
+	.get = snd_herc_spdif_select_get,
+	.put = snd_herc_spdif_select_put,
+},
+};
+#endif
 
 int __devinit snd_cs46xx_mixer(cs46xx_t *chip)
 {
@@ -1656,6 +2007,13 @@
     
 	/* add cs4630 mixer controls */
  _end:
+
+	/* dosoundcard specific mixer setup */
+	if (chip->mixer_init) {
+		snd_printdd ("calling chip->mixer_init(chip);\n");
+		chip->mixer_init(chip);
+	}
+    
 #endif /* CONFIG_SND_CS46XX_NEW_DSP */
 
 	for (idx = 0; idx < sizeof(snd_cs46xx_controls) / 
@@ -1671,7 +2029,7 @@
 	id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
 	strcpy(id.name, "External Amplifier Power Down");
 	chip->eapd_switch = snd_ctl_find_id(chip->card, &id);
-
+    
 	return 0;
 }
 
@@ -1815,16 +2173,16 @@
 
 static snd_rawmidi_ops_t snd_cs46xx_midi_output =
 {
-	.open           = snd_cs46xx_midi_output_open,
-	.close          = snd_cs46xx_midi_output_close,
-	.trigger        = snd_cs46xx_midi_output_trigger,
+	.open =		snd_cs46xx_midi_output_open,
+	.close =	snd_cs46xx_midi_output_close,
+	.trigger =	snd_cs46xx_midi_output_trigger,
 };
 
 static snd_rawmidi_ops_t snd_cs46xx_midi_input =
 {
-	.open           = snd_cs46xx_midi_input_open,
-	.close          = snd_cs46xx_midi_input_close,
-	.trigger        = snd_cs46xx_midi_input_trigger,
+	.open =		snd_cs46xx_midi_input_open,
+	.close =	snd_cs46xx_midi_input_close,
+	.trigger =	snd_cs46xx_midi_input_trigger,
 };
 
 int __devinit snd_cs46xx_midi(cs46xx_t *chip, int device, snd_rawmidi_t **rrawmidi)
@@ -2118,7 +2476,7 @@
 		chip->active_ctrl(chip, -chip->amplifier);
 
 #ifdef CONFIG_SND_CS46XX_NEW_DSP
-	cs46xx_dsp_spos_destroy(chip->dsp_spos_instance);
+	cs46xx_dsp_spos_destroy(chip);
 #endif
 	snd_magic_kfree(chip);
 	return 0;
@@ -2432,9 +2790,14 @@
 	snd_cs46xx_poke(chip, BA1_CVOL, 0x80008000);
 
 #ifdef CONFIG_SND_CS46XX_NEW_DSP
+	/* time countdown enable */
+	cs46xx_poke_via_dsp (chip,SP_ASER_COUNTDOWN, 0x80000000);
+        
+	/* SPDIF input MASTER ENABLE */
+	cs46xx_poke_via_dsp (chip,SP_SPDIN_CONTROL, 0x800003ff);
+
 	/* mute spdif out */
 	cs46xx_dsp_disable_spdif_out(chip);
-	cs46xx_dsp_disable_spdif_in(chip);
 #endif
 
 	return 0;
@@ -2454,95 +2817,94 @@
 static int voyetra_setup_eapd_slot(cs46xx_t *chip)
 {
 	int i;
-    u32 idx;
-    u16 modem_power,pin_config,logic_type,valid_slots,status;
+	u32 idx;
+	u16 modem_power,pin_config,logic_type,valid_slots,status;
 
 	snd_printd ("cs46xx: cs46xx_setup_eapd_slot()+\n");
 	/*
-	* Clear PRA.  The Bonzo chip will be used for GPIO not for modem
-	* stuff.
-	*/
-	if(chip->nr_ac97_codecs != 2)
-	{
-      snd_printk (KERN_ERR "cs46xx: cs46xx_setup_eapd_slot() - no secondary codec configured\n");
-      return -EINVAL;
+	 * Clear PRA.  The Bonzo chip will be used for GPIO not for modem
+	 * stuff.
+	 */
+	if(chip->nr_ac97_codecs != 2) {
+		snd_printk (KERN_ERR "cs46xx: cs46xx_setup_eapd_slot() - no secondary codec configured\n");
+		return -EINVAL;
 	}
 
 	modem_power = snd_cs46xx_codec_read (chip, 
-                                         BA0_AC97_EXT_MODEM_POWER,
-                                         CS46XX_SECONDARY_CODEC_INDEX);
+					     BA0_AC97_EXT_MODEM_POWER,
+					     CS46XX_SECONDARY_CODEC_INDEX);
 	modem_power &=0xFEFF;
 
 	snd_cs46xx_codec_write(chip, 
-                           BA0_AC97_EXT_MODEM_POWER, modem_power,
-                           CS46XX_SECONDARY_CODEC_INDEX);
+			       BA0_AC97_EXT_MODEM_POWER, modem_power,
+			       CS46XX_SECONDARY_CODEC_INDEX);
 
-    /*
-     * Set GPIO pin's 7 and 8 so that they are configured for output.
-     */
+	/*
+	 * Set GPIO pin's 7 and 8 so that they are configured for output.
+	 */
 	pin_config = snd_cs46xx_codec_read (chip, 
-                                        BA0_AC97_GPIO_PIN_CONFIG,
-                                        CS46XX_SECONDARY_CODEC_INDEX);
+					    BA0_AC97_GPIO_PIN_CONFIG,
+					    CS46XX_SECONDARY_CODEC_INDEX);
 	pin_config &=0x27F;
 
 	snd_cs46xx_codec_write(chip, 
-                           BA0_AC97_GPIO_PIN_CONFIG, pin_config,
-                           CS46XX_SECONDARY_CODEC_INDEX);
+			       BA0_AC97_GPIO_PIN_CONFIG, pin_config,
+			       CS46XX_SECONDARY_CODEC_INDEX);
     
-    /*
-     * Set GPIO pin's 7 and 8 so that they are compatible with CMOS logic.
-     */
+	/*
+	 * Set GPIO pin's 7 and 8 so that they are compatible with CMOS logic.
+	 */
 
 	logic_type = snd_cs46xx_codec_read(chip, BA0_AC97_GPIO_PIN_TYPE,
-                                       CS46XX_SECONDARY_CODEC_INDEX);
+					   CS46XX_SECONDARY_CODEC_INDEX);
 	logic_type &=0x27F;
 	snd_cs46xx_codec_write (chip, BA0_AC97_GPIO_PIN_TYPE, logic_type,
-                            CS46XX_SECONDARY_CODEC_INDEX);
+				CS46XX_SECONDARY_CODEC_INDEX);
 
 	valid_slots = snd_cs46xx_peekBA0(chip, BA0_ACOSV);
 	valid_slots |= 0x200;
 	snd_cs46xx_pokeBA0(chip, BA0_ACOSV, valid_slots);
 
-    /*
-     * Fill slots 12 with the correct value for the GPIO pins. 
-     */
+	/*
+	 * Fill slots 12 with the correct value for the GPIO pins. 
+	 */
 	for(idx = 0x90; idx <= 0x9F; idx++) {
 
-      /*
-       * Initialize the fifo so that bits 7 and 8 are on.
-       *
-       * Remember that the GPIO pins in bonzo are shifted by 4 bits to
-       * the left.  0x1800 corresponds to bits 7 and 8.
-       */
-      snd_cs46xx_pokeBA0(chip, BA0_SERBWP, 0x1800);
+		/*
+		 * Initialize the fifo so that bits 7 and 8 are on.
+		 *
+		 * Remember that the GPIO pins in bonzo are shifted by 4 bits to
+		 * the left.  0x1800 corresponds to bits 7 and 8.
+		 */
+		snd_cs46xx_pokeBA0(chip, BA0_SERBWP, 0x1800);
       
-      /*
-       * Make sure the previous FIFO write operation has completed.
-       */
-      for(i = 0; i < 5; i++){
-        status = snd_cs46xx_peekBA0(chip, BA0_SERBST);
+		/*
+		 * Make sure the previous FIFO write operation has completed.
+		 */
+		for(i = 0; i < 5; i++){
+			status = snd_cs46xx_peekBA0(chip, BA0_SERBST);
 
-        if( !(status & SERBST_WBSY) ) {
-          break;
-        }
-        mdelay(100);
-      }
+			if( !(status & SERBST_WBSY) ) {
+				break;
+			}
+			mdelay(100);
+		}
 
-      if(status & SERBST_WBSY) {
-        snd_printk( KERN_ERR "cs46xx: cs46xx_setup_eapd_slot() " \
-                             "Failure to write the GPIO pins for slot 12.\n");
-         return -EINVAL;
-      }
+		if(status & SERBST_WBSY) {
+			snd_printk( KERN_ERR "cs46xx: cs46xx_setup_eapd_slot() " \
+				    "Failure to write the GPIO pins for slot 12.\n");
+			return -EINVAL;
+		}
       
-      /*
-       * Write the serial port FIFO index.
-       */
-      snd_cs46xx_pokeBA0(chip, BA0_SERBAD, idx);
+		/*
+		 * Write the serial port FIFO index.
+		 */
+		snd_cs46xx_pokeBA0(chip, BA0_SERBAD, idx);
       
-      /*
-       * Tell the serial port to load the new value into the FIFO location.
-       */
-      snd_cs46xx_pokeBA0(chip, BA0_SERBCM, SERBCM_WRC);
+		/*
+		 * Tell the serial port to load the new value into the FIFO location.
+		 */
+		snd_cs46xx_pokeBA0(chip, BA0_SERBCM, SERBCM_WRC);
 	}
 
 	return 0;
@@ -2581,31 +2943,71 @@
 	}
 
 #ifdef CONFIG_SND_CS46XX_NEW_DSP
-    if (chip->amplifier && !old) {
-      voyetra_setup_eapd_slot(chip);
-    }
+	if (chip->amplifier && !old) {
+		voyetra_setup_eapd_slot(chip);
+	}
 #endif
 }
 
+static void hercules_init(cs46xx_t *chip) 
+{
+	/* default: AMP off, and SPDIF input optical */
+	snd_cs46xx_pokeBA0(chip, BA0_EGPIODR, EGPIODR_GPOE0);
+	snd_cs46xx_pokeBA0(chip, BA0_EGPIOPTR, EGPIODR_GPOE0);
+}
+
 
 /*
  *	Game Theatre XP card - EGPIO[2] is used to enable the external amp.
- */
- 
+ */ 
 static void amp_hercules(cs46xx_t *chip, int change)
 {
 	int old = chip->amplifier;
+	int val1 = snd_cs46xx_peekBA0(chip, BA0_EGPIODR);
+	int val2 = snd_cs46xx_peekBA0(chip, BA0_EGPIOPTR);
 
 	chip->amplifier += change;
 	if (chip->amplifier && !old) {
+		snd_printdd ("Hercules amplifier ON\n");
+
 		snd_cs46xx_pokeBA0(chip, BA0_EGPIODR, 
-				   EGPIODR_GPOE2);     /* enable EGPIO2 output */
+				   EGPIODR_GPOE2 | val1);     /* enable EGPIO2 output */
 		snd_cs46xx_pokeBA0(chip, BA0_EGPIOPTR, 
-				   EGPIOPTR_GPPT2);   /* open-drain on output */
+				   EGPIOPTR_GPPT2 | val2);   /* open-drain on output */
 	} else if (old && !chip->amplifier) {
-		snd_cs46xx_pokeBA0(chip, BA0_EGPIODR, 0); /* disable */
-		snd_cs46xx_pokeBA0(chip, BA0_EGPIOPTR, 0); /* disable */
+		snd_printdd ("Hercules amplifier OFF\n");
+		snd_cs46xx_pokeBA0(chip, BA0_EGPIODR,  val1 & ~EGPIODR_GPOE2); /* disable */
+		snd_cs46xx_pokeBA0(chip, BA0_EGPIOPTR, val2 & ~EGPIOPTR_GPPT2); /* disable */
+	}
+}
+
+static void hercules_mixer_init (cs46xx_t *chip)
+{
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+	int idx,err;
+	snd_card_t *card = chip->card;
+#endif
+
+	/* set EGPIO to default */
+	hercules_init(chip);
+
+	snd_printdd ("initializing Hercules mixer\n");
+
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+	/* turnon Amplifier and leave it on */
+	chip->amplifier_ctrl(chip, 1);
+
+	for (idx = 0 ; idx < sizeof(snd_hercules_controls) / 
+		     sizeof(snd_hercules_controls[0]) ; idx++) {
+		snd_kcontrol_t *kctl;
+
+		kctl = snd_ctl_new1(&snd_hercules_controls[idx], chip);
+		if ((err = snd_ctl_add(card, kctl)) < 0) {
+			printk (KERN_ERR "cs46xx: failed to initialize Hercules mixer (%d)\n",err);
+			break;
+		}
 	}
+#endif
 }
 
 
@@ -2706,24 +3108,25 @@
 	void (*init)(cs46xx_t *);
 	void (*amp)(cs46xx_t *, int);
 	void (*active)(cs46xx_t *, int);
+	void (*mixer_init)(cs46xx_t *);
 };
 
 static struct cs_card_type __initdata cards[] = {
-	{0x1489, 0x7001, "Genius Soundmaker 128 value", NULL, amp_none, NULL},
-	{0x5053, 0x3357, "Voyetra", NULL, amp_voyetra, NULL},
-	{0x1071, 0x6003, "Mitac MI6020/21", NULL, amp_voyetra, NULL},
-	{0x14AF, 0x0050, "Hercules Game Theatre XP", NULL, amp_hercules, NULL},
-	{0x1681, 0x0050, "Hercules Game Theatre XP", NULL, amp_hercules, NULL},
-	{0x1681, 0x0051, "Hercules Game Theatre XP", NULL, amp_hercules, NULL},
-	{0x1681, 0x0052, "Hercules Game Theatre XP", NULL, amp_hercules, NULL},
-	{0x1681, 0x0053, "Hercules Game Theatre XP", NULL, amp_hercules, NULL},
-	{0x1681, 0x0054, "Hercules Game Theatre XP", NULL, amp_hercules, NULL},
+	{0x1489, 0x7001, "Genius Soundmaker 128 value", NULL, amp_none, NULL, NULL},
+	{0x5053, 0x3357, "Voyetra", NULL, amp_voyetra, NULL, NULL},
+	{0x1071, 0x6003, "Mitac MI6020/21", NULL, amp_voyetra, NULL, NULL},
+	{0x14AF, 0x0050, "Hercules Game Theatre XP", NULL, amp_hercules, NULL, hercules_mixer_init},
+	{0x1681, 0x0050, "Hercules Game Theatre XP", NULL, amp_hercules, NULL, hercules_mixer_init},
+	{0x1681, 0x0051, "Hercules Game Theatre XP", NULL, amp_hercules, NULL, hercules_mixer_init},
+	{0x1681, 0x0052, "Hercules Game Theatre XP", NULL, amp_hercules, NULL, hercules_mixer_init},
+	{0x1681, 0x0053, "Hercules Game Theatre XP", NULL, amp_hercules, NULL, hercules_mixer_init},
+	{0x1681, 0x0054, "Hercules Game Theatre XP", NULL, amp_hercules, NULL, hercules_mixer_init},
 	/* Not sure if the 570 needs the clkrun hack */
-	{PCI_VENDOR_ID_IBM, 0x0132, "Thinkpad 570", clkrun_init, NULL, clkrun_hack},
-	{PCI_VENDOR_ID_IBM, 0x0153, "Thinkpad 600X/A20/T20", clkrun_init, NULL, clkrun_hack},
-	{PCI_VENDOR_ID_IBM, 0x1010, "Thinkpad 600E (unsupported)", NULL, NULL, NULL},
-	{0, 0, "Card without SSID set", NULL, NULL, NULL },
-	{0, 0, NULL, NULL, NULL, NULL}
+	{PCI_VENDOR_ID_IBM, 0x0132, "Thinkpad 570", clkrun_init, NULL, clkrun_hack, NULL},
+	{PCI_VENDOR_ID_IBM, 0x0153, "Thinkpad 600X/A20/T20", clkrun_init, NULL, clkrun_hack, NULL},
+	{PCI_VENDOR_ID_IBM, 0x1010, "Thinkpad 600E (unsupported)", NULL, NULL, NULL, NULL},
+	{0, 0, "Card without SSID set", NULL, NULL, NULL, NULL },
+	{0, 0, NULL, NULL, NULL, NULL, NULL}
 };
 
 
@@ -2823,7 +3226,7 @@
 	struct cs_card_type *cp;
 	u16 ss_card, ss_vendor;
 	static snd_device_ops_t ops = {
-		.dev_free	= snd_cs46xx_dev_free,
+		.dev_free =	snd_cs46xx_dev_free,
 	};
 	
 	*rchip = NULL;
@@ -2836,6 +3239,9 @@
 	if (chip == NULL)
 		return -ENOMEM;
 	spin_lock_init(&chip->reg_lock);
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+	init_MUTEX(&chip->spos_mutex);
+#endif
 	chip->card = card;
 	chip->pci = pci;
 	chip->capt.hw_size = PAGE_SIZE;
@@ -2885,6 +3291,7 @@
 				cp->init(chip);
 			chip->amplifier_ctrl = cp->amp;
 			chip->active_ctrl = cp->active;
+			chip->mixer_init = cp->mixer_init;
 			break;
 		}
 	}
diff -Nru a/sound/pci/cs46xx/cs46xx_lib.h b/sound/pci/cs46xx/cs46xx_lib.h
--- a/sound/pci/cs46xx/cs46xx_lib.h	Mon Oct  7 11:25:26 2002
+++ b/sound/pci/cs46xx/cs46xx_lib.h	Mon Oct  7 11:25:26 2002
@@ -28,11 +28,11 @@
  *  constants
  */
 
-#define CS46XX_BA0_SIZE		0x1000
-#define CS46XX_BA1_DATA0_SIZE	0x3000
-#define CS46XX_BA1_DATA1_SIZE	0x3800
-#define CS46XX_BA1_PRG_SIZE	0x7000
-#define CS46XX_BA1_REG_SIZE	0x0100
+#define CS46XX_BA0_SIZE		  0x1000
+#define CS46XX_BA1_DATA0_SIZE 0x3000
+#define CS46XX_BA1_DATA1_SIZE 0x3800
+#define CS46XX_BA1_PRG_SIZE	  0x7000
+#define CS46XX_BA1_REG_SIZE	  0x0100
 
 
 #define CS46XX_PERIOD_SIZE 2048
@@ -84,7 +84,7 @@
 }
 
 dsp_spos_instance_t *  cs46xx_dsp_spos_create (cs46xx_t * chip);
-void                   cs46xx_dsp_spos_destroy (dsp_spos_instance_t * instance);
+void                   cs46xx_dsp_spos_destroy (cs46xx_t * chip);
 int                    cs46xx_dsp_load_module (cs46xx_t * chip,dsp_module_desc_t * module);
 symbol_entry_t *       cs46xx_dsp_lookup_symbol (cs46xx_t * chip,char * symbol_name,int symbol_type);
 symbol_entry_t *       cs46xx_dsp_lookup_symbol_addr (cs46xx_t * chip,u32 address,int symbol_type);
@@ -99,6 +99,10 @@
 int                    cs46xx_dsp_disable_spdif_out (cs46xx_t *chip);
 int                    cs46xx_dsp_enable_spdif_in (cs46xx_t *chip);
 int                    cs46xx_dsp_disable_spdif_in (cs46xx_t *chip);
+int                    cs46xx_dsp_enable_pcm_capture (cs46xx_t *chip);
+int                    cs46xx_dsp_disable_pcm_capture (cs46xx_t *chip);
+int                    cs46xx_dsp_enable_adc_capture (cs46xx_t *chip);
+int                    cs46xx_dsp_disable_adc_capture (cs46xx_t *chip);
 int                    cs46xx_poke_via_dsp (cs46xx_t *chip,u32 address,u32 data);
 dsp_scb_descriptor_t * cs46xx_dsp_create_scb (cs46xx_t *chip,char * name, u32 * scb_data,u32 dest);
 void                   cs46xx_dsp_proc_free_scb_desc (dsp_scb_descriptor_t * scb);
@@ -181,11 +185,15 @@
                                                           dsp_scb_descriptor_t * snoop_scb,
                                                           dsp_scb_descriptor_t * parent_scb,
                                                           int scb_child_type);
-pcm_channel_descriptor_t * cs46xx_dsp_create_pcm_channel (cs46xx_t * chip,u32 sample_rate, void * private_data);
+pcm_channel_descriptor_t * cs46xx_dsp_create_pcm_channel (cs46xx_t * chip,u32 sample_rate, void * private_data, u32 hw_dma_addr);
 void                       cs46xx_dsp_destroy_pcm_channel (cs46xx_t * chip,
                                                            pcm_channel_descriptor_t * pcm_channel);
 void                       cs46xx_dsp_set_src_sample_rate(cs46xx_t * chip,dsp_scb_descriptor_t * src, 
                                                           u32 rate);
 int                        cs46xx_dsp_pcm_unlink (cs46xx_t * chip,pcm_channel_descriptor_t * pcm_channel);
 int                        cs46xx_dsp_pcm_link (cs46xx_t * chip,pcm_channel_descriptor_t * pcm_channel);
+dsp_scb_descriptor_t *     cs46xx_add_record_source (cs46xx_t *chip,dsp_scb_descriptor_t * source,
+                                                     u16 addr,char * scb_name);
+int                         cs46xx_src_unlink(cs46xx_t *chip,dsp_scb_descriptor_t * src);
+int                         cs46xx_src_link(cs46xx_t *chip,dsp_scb_descriptor_t * src);
 #endif /* __CS46XX_LIB_H__ */
diff -Nru a/sound/pci/cs46xx/dsp_spos.c b/sound/pci/cs46xx/dsp_spos.c
--- a/sound/pci/cs46xx/dsp_spos.c	Mon Oct  7 11:25:37 2002
+++ b/sound/pci/cs46xx/dsp_spos.c	Mon Oct  7 11:25:37 2002
@@ -20,7 +20,6 @@
  */
 
 
-#define __NO_VERSION__
 #include <sound/driver.h>
 #include <asm/io.h>
 #include <linux/delay.h>
@@ -37,74 +36,6 @@
 #include "cs46xx_lib.h"
 #include "dsp_spos.h"
 
-#if 0
-/* OBSOLETE NOW */
-
-static void handle_goto (cs46xx_t * chip,u32  * hival,u32 * loval,char * format, u32 overlay_begin_address);
-
-typedef struct _dsp_opcode_desc_t {
-	u32 hival,loval;
-	u32 himask,lomask;
-	void (*handler_func)(cs46xx_t * chip,u32 * hival,u32 * loval,char * format, u32 overlay_begin_address);
-	char * format;
-} dsp_opcode_desc_t;
-
-/* NOTE: theese opcodes are known needed to be reallocated,
-   there may be more. */
-dsp_opcode_desc_t dsp_opcodes[] = {
-	{ 0x01000,0x02730, 0xFF000,0x07FFF,handle_goto,"goto %s" },
-	{ 0x01000,0x00730, 0xFF000,0x07FFF,handle_goto,"goto %s after" },
-	{ 0x01000,0x02630, 0xFF000,0x07FFF,handle_goto,"if (tb) goto %s" },
-	{ 0x01000,0x00630, 0xFF000,0x07FFF,handle_goto,"if (tb) goto %s after" },
-	{ 0x01000,0x00530, 0xFF000,0x07FFF,handle_goto,"if (lt) goto %s after" },
-	{ 0x01000,0x02530, 0xFF000,0x07FFF,handle_goto,"if (lt) goto %s" },
-	{ 0x01000,0x006B0, 0xFF000,0x07FFF,handle_goto,"if (!tb) goto %s after" },
-	{ 0x01000,0x026B0, 0xFF000,0x07FFF,handle_goto,"if (!tb) goto %s" },
-	{ 0x01000,0x00FB0, 0xFF000,0x07FFF,handle_goto,"if (gt) goto %s after" },
-	{ 0x01000,0x02FB0, 0xFF000,0x07FFF,handle_goto,"if (gt) goto %s" },
-	{ 0x01000,0x02734, 0xFF000,0x07FFF,NULL,"goto *ind" },
-	{ 0x01000,0x00734, 0xFF000,0x07FFF,NULL,"goto *ind after" },
-	{ 0x01000,0x02130, 0xFF000,0x07FFF,handle_goto,"if (N) goto %s" },
-	{ 0x01000,0x00130, 0xFF000,0x07FFF,handle_goto,"if (N) goto %s after" },
-	{ 0x01000,0x020F2, 0xFF000,0x07FFF,handle_goto,"tb = Z, if(!Z) goto %s" },
-	{ 0x01000,0x000F2, 0xFF000,0x07FFF,handle_goto,"tb = Z, if(!Z) goto %s after" },
-	{ 0x01000,0x04030, 0xFF000,0x07FFF,handle_goto,"if(Z) goto %s" },
-	{ 0x01000,0x00030, 0xFF000,0x07FFF,handle_goto,"if(Z) goto %s after" },
-	{ 0x01000,0x020B0, 0xFF000,0x07FFF,handle_goto,"if(!Z) goto %s" },
-	{ 0x01000,0x000B0, 0xFF000,0x07FFF,handle_goto,"if(!Z) goto %s after" },
-	{ 0x01000,0x02731, 0xFF000,0x07FFF,handle_goto,"%s();" },
-};
-
-static void handle_goto (cs46xx_t * chip,u32  * hival,u32 * loval,
-			 char * format, u32 overlay_begin_address)
-{
-	u32 address ;
-	dsp_spos_instance_t * ins = chip->dsp_spos_instance;
-
-	address  = (*hival & 0x00FFF) << 5;
-	address |=  *loval >> 15;
- 
-	snd_printdd("handle_goto[1]: %05x:%05x addr %04x\n",*hival,*loval,address);
-
-	if ( !(address & 0x8000) ) {
-		address += (ins->code.offset / 2) - overlay_begin_address;
-	} else {
-		snd_printdd("handle_goto[1]: ROM symbol not reallocated\n");
-	}
-
-	*hival &= 0xFF000;
-	*loval &= 0x07FFF;
-
-	*hival |= ( (address >> 5)  & 0x00FFF);
-	*loval |= ( (address << 15) & 0xF8000);
-
-	address  = (*hival & 0x00FFF) << 5;
-	address |=  *loval >> 15;
-
-	snd_printdd("handle_goto:[2] %05x:%05x addr %04x\n",*hival,*loval,address);
-}
-#endif /* #if 0 */
-
 static wide_opcode_t wide_opcodes[] = { 
 	WIDE_FOR_BEGIN_LOOP,
 	WIDE_FOR_BEGIN_LOOP2,
@@ -134,18 +65,6 @@
 		hival = data[i++];
 
 		if (ins->code.offset > 0) {
-#if 0
-			/* OBSOLETE NOW */
-			for (j = 0;j < sizeof(dsp_opcodes) / sizeof(dsp_opcode_desc_t); ++j) {
-				if ( (hival & dsp_opcodes[j].himask) == dsp_opcodes[j].hival &&
-				     (loval & dsp_opcodes[j].lomask) == dsp_opcodes[j].loval &&
-				     dsp_opcodes[j].handler_func != NULL) {
-					dsp_opcodes[j].handler_func (chip,&hival,&loval,dsp_opcodes[j].format, overlay_begin_address);
-					nreallocated ++;
-				}
-			}
-#endif
-
 			mop_operands = (hival >> 6) & 0x03fff;
 			mop_type = mop_operands >> 10;
       
@@ -251,8 +170,8 @@
 
 			ins->symbol_table.nsymbols++;
 		} else {
-			// if (0) printk ("dsp_spos: symbol <%s> duplicated, probably nothing wrong with that (Cirrus?)\n",
-			//                module->symbol_table.symbols[i].symbol_name);
+          /* if (0) printk ("dsp_spos: symbol <%s> duplicated, probably nothing wrong with that (Cirrus?)\n",
+                             module->symbol_table.symbols[i].symbol_name); */
 		}
 	}
 
@@ -303,16 +222,13 @@
 		return NULL;
 	memset(ins, 0, sizeof(*ins));
 
-	init_MUTEX(&ins->scb_mutex);
-	init_MUTEX(&ins->pcm_mutex);
-
 	/* better to use vmalloc for this big table */
 	ins->symbol_table.nsymbols = 0;
 	ins->symbol_table.symbols = vmalloc(sizeof(symbol_entry_t) * DSP_MAX_SYMBOLS);
 	ins->symbol_table.highest_frag_index = 0;
 
 	if (ins->symbol_table.symbols == NULL) {
-		cs46xx_dsp_spos_destroy(ins);
+		cs46xx_dsp_spos_destroy(chip);
 		return NULL;
 	}
 
@@ -321,7 +237,7 @@
 	ins->code.data = kmalloc(DSP_CODE_BYTE_SIZE, GFP_KERNEL);
 
 	if (ins->code.data == NULL) {
-		cs46xx_dsp_spos_destroy(ins);
+		cs46xx_dsp_spos_destroy(chip);
 		return NULL;
 	}
 
@@ -332,26 +248,33 @@
 	ins->modules = kmalloc(sizeof(dsp_module_desc_t) * DSP_MAX_MODULES, GFP_KERNEL);
 
 	if (ins->modules == NULL) {
-		cs46xx_dsp_spos_destroy(ins);
+		cs46xx_dsp_spos_destroy(chip);
 		return NULL;
 	}
 
+	/* default SPDIF input sample rate
+	   to 48000 khz */
+	ins->spdif_in_sample_rate = 48000;
+
+	/* maximize volume */
+	ins->spdif_input_volume = 0x80008000;
+
 	return ins;
 }
 
-void  cs46xx_dsp_spos_destroy (dsp_spos_instance_t * ins)
+void  cs46xx_dsp_spos_destroy (cs46xx_t * chip)
 {
 	int i;
+	dsp_spos_instance_t * ins = chip->dsp_spos_instance;
 
 	snd_assert(ins != NULL, return);
 
-	down(&ins->scb_mutex);
+	down(&chip->spos_mutex);
 	for (i = 0; i < ins->nscb; ++i) {
 		if (ins->scbs[i].deleted) continue;
 
 		cs46xx_dsp_proc_free_scb_desc ( (ins->scbs + i) );
 	}
-	up(&ins->scb_mutex);
 
 	if (ins->code.data)
 		kfree(ins->code.data);
@@ -360,6 +283,7 @@
 	if (ins->modules)
 		kfree(ins->modules);
 	kfree(ins);  
+	up(&chip->spos_mutex);
 }
 
 int cs46xx_dsp_load_module (cs46xx_t * chip, dsp_module_desc_t * module)
@@ -553,6 +477,7 @@
 	dsp_spos_instance_t * ins = chip->dsp_spos_instance;
 	int i,j;
 
+	down(&chip->spos_mutex);
 	snd_iprintf(buffer, "MODULES:\n");
 	for ( i = 0; i < ins->nmodules; ++i ) {
 		snd_iprintf(buffer, "\n%s:\n", ins->modules[i].module_name);
@@ -565,6 +490,7 @@
 				    desc->segment_type,desc->offset, desc->size);
 		}
 	}
+	up(&chip->spos_mutex);
 }
 
 static void cs46xx_dsp_proc_task_tree_read (snd_info_entry_t *entry, snd_info_buffer_t * buffer)
@@ -574,6 +500,7 @@
 	int i,j,col;
 	unsigned long dst = chip->region.idx[1].remap_addr + DSP_PARAMETER_BYTE_OFFSET;
 
+	down(&chip->spos_mutex);
 	snd_iprintf(buffer, "TASK TREES:\n");
 	for ( i = 0; i < ins->ntask; ++i) {
 		snd_iprintf(buffer,"\n%04x %s:\n",ins->tasks[i].address,ins->tasks[i].task_name);
@@ -590,6 +517,7 @@
 	}
 
 	snd_iprintf(buffer,"\n");  
+	up(&chip->spos_mutex);
 }
 
 static void cs46xx_dsp_proc_scb_read (snd_info_entry_t *entry, snd_info_buffer_t * buffer)
@@ -598,6 +526,7 @@
 	dsp_spos_instance_t * ins = chip->dsp_spos_instance;
 	int i;
 
+	down(&chip->spos_mutex);
 	snd_iprintf(buffer, "SCB's:\n");
 	for ( i = 0; i < ins->nscb; ++i) {
 		if (ins->scbs[i].deleted)
@@ -620,6 +549,7 @@
 	}
 
 	snd_iprintf(buffer,"\n");
+	up(&chip->spos_mutex);
 }
 
 static void cs46xx_dsp_proc_parameter_dump_read (snd_info_entry_t *entry, snd_info_buffer_t * buffer)
@@ -652,7 +582,6 @@
 static void cs46xx_dsp_proc_sample_dump_read (snd_info_entry_t *entry, snd_info_buffer_t * buffer)
 {
 	cs46xx_t *chip = snd_magic_cast(cs46xx_t, entry->private_data, return);
-	/*dsp_spos_instance_t * ins = chip->dsp_spos_instance; */
 	int i,col = 0;
 	unsigned long dst = chip->region.idx[2].remap_addr;
 
@@ -688,7 +617,7 @@
 
 	snd_iprintf(buffer,"\n\n");
 	col = 0;
-	for (i = SPDIFO_IP_OUTPUT_BUFFER1 - 0x80;i < SPDIFO_IP_OUTPUT_BUFFER1; i += sizeof(u32),col ++) {
+	for (i = SPDIFI_IP_OUTPUT_BUFFER1;i < SPDIFI_IP_OUTPUT_BUFFER1 + 0x40; i += sizeof(u32),col ++) {
 		if (col == 4) {
 			snd_iprintf(buffer,"\n");
 			col = 0;
@@ -884,12 +813,14 @@
 	}
 	ins->proc_scb_info_entry = entry;
 
+	down(&chip->spos_mutex);
 	/* register/update SCB's entries on proc */
 	for (i = 0; i < ins->nscb; ++i) {
 		if (ins->scbs[i].deleted) continue;
 
 		cs46xx_dsp_proc_register_scb_desc (chip, (ins->scbs + i));
 	}
+	up(&chip->spos_mutex);
 
 	return 0;
 }
@@ -929,12 +860,12 @@
 		ins->proc_task_info_entry = NULL;
 	}
 
-	down(&ins->scb_mutex);
+	down(&chip->spos_mutex);
 	for (i = 0; i < ins->nscb; ++i) {
 		if (ins->scbs[i].deleted) continue;
 		cs46xx_dsp_proc_free_scb_desc ( (ins->scbs + i) );
 	}
-	up(&ins->scb_mutex);
+	up(&chip->spos_mutex);
 
 	if (ins->proc_dsp_dir) {
 		snd_info_unregister (ins->proc_dsp_dir);
@@ -1005,6 +936,7 @@
 	ins->scbs[index].proc_info = NULL;
 	ins->scbs[index].ref_count = 1;
 	ins->scbs[index].deleted = 0;
+	spin_lock_init(&ins->scbs[index].lock);
 
 	desc = (ins->scbs + index);
 	ins->scbs[index].scb_symbol = add_symbol (chip, name, dest, SYMBOL_PARAMETER);
@@ -1083,17 +1015,15 @@
 	dsp_scb_descriptor_t * timing_master_scb;
 	dsp_scb_descriptor_t * codec_out_scb;
 	dsp_scb_descriptor_t * codec_in_scb;
-	dsp_scb_descriptor_t * pcm_reader_scb;
 	dsp_scb_descriptor_t * src_task_scb;
 	dsp_scb_descriptor_t * master_mix_scb;
+	dsp_scb_descriptor_t * record_mix_scb;
 	dsp_scb_descriptor_t * write_back_scb;
 	dsp_scb_descriptor_t * vari_decimate_scb;
 	dsp_scb_descriptor_t * pcm_serial_input_task;
 	dsp_scb_descriptor_t * asynch_tx_scb;
-	dsp_scb_descriptor_t * asynch_rx_scb;
 	dsp_scb_descriptor_t * sec_codec_out_scb;
 	dsp_scb_descriptor_t * magic_snoop_scb;
-	dsp_scb_descriptor_t * sec_magic_snoop_scb;
 
 	spos_control_block_t sposcb = {
 		/* 0 */ HFG_TREE_SCB,HFG_STACK,
@@ -1310,13 +1240,9 @@
 				0,0
 			}
 		};
-		// debug_tree = 1;
 		cs46xx_dsp_create_task_tree(chip,"BGtaskTreeHdr",(u32 *)&bg_task_tree_hdr,BG_TREE_SCB_ADDR,0x35);
-		// debug_tree = 0;
-    
 	}
 
-	//debug_scb = 1;
 	/* create timing master SCB */
 	timing_master_scb = cs46xx_dsp_create_timing_master_scb(chip);
 
@@ -1335,21 +1261,6 @@
 	ins->master_mix_scb = master_mix_scb;
 
 	if (!master_mix_scb) goto _fail_end;
-#if 0
-	/* the sample rate converter SCB */
-	src_task_scb = cs46xx_dsp_create_src_task_scb(chip,"SrcTaskSCB_I",
-						      SRC_OUTPUT_BUF1,
-						      SRC_DELAY_BUF1,SRCTASK_SCB_ADDR,
-						      asynch_tx_scb,
-
-						      SCB_ON_PARENT_SUBLIST_SCB);
-	/* create the PCM reader SCB */
-	pcm_reader_scb = cs46xx_dsp_create_pcm_reader_scb(chip,"PCMReaderSCB_I",
-							  PCM_READER_BUF1,PCMREADER_SCB_ADDR,
-							  0, /* playback how hw addr */
-							  src_task_scb,
-							  SCB_ON_PARENT_SUBLIST_SCB);
-#endif
 
 	/* create codec in */
 	codec_in_scb = cs46xx_dsp_create_codec_in_scb(chip,"CodecInSCB",0x0010,0x00A0,
@@ -1357,7 +1268,8 @@
                                                   CODECIN_SCB_ADDR,codec_out_scb,
                                                   SCB_ON_PARENT_NEXT_SCB);
 	if (!codec_in_scb) goto _fail_end;
-  
+	ins->codec_in_scb = codec_in_scb;
+
 	/* create write back scb */
 	write_back_scb = cs46xx_dsp_create_mix_to_ostream_scb(chip,"WriteBackSCB",
 							      WRITE_BACK_BUF1,WRITE_BACK_SPB,
@@ -1385,25 +1297,15 @@
 								SCB_ON_PARENT_SUBLIST_SCB);
 	if (!vari_decimate_scb) goto _fail_end;
 
+	/* create the record mixer SCB */
+	record_mix_scb = cs46xx_dsp_create_mix_only_scb(chip,"RecordMixerSCB",
+							MIX_SAMPLE_BUF2,
+							RECORD_MIXER_SCB_ADDR,
+							vari_decimate_scb,
+							SCB_ON_PARENT_SUBLIST_SCB);
+	ins->record_mixer_scb = record_mix_scb;
 
-	/* pcm input */
-	pcm_serial_input_task = cs46xx_dsp_create_pcm_serial_input_scb(chip,"PCMSerialInput",
-								       PCMSERIALIN_SCB_ADDR,
-								       codec_in_scb, vari_decimate_scb,
-								       SCB_ON_PARENT_SUBLIST_SCB);
-	if (!pcm_serial_input_task) goto _fail_end;
-  
-
-#if 0  /* asynch. receive task */
-	asynch_rx_scb = cs46xx_dsp_create_asynch_fg_rx_scb(chip,"AsynchFGRxSCB",
-							   ASYNCRX_SCB_ADDR,
-							   SPDIFI_SCB_INST,
-							   SPDIFI_IP_OUTPUT_BUFFER1,
-							   master_mix_scb,
-							   SCB_ON_PARENT_NEXT_SCB);
-	if (!asynch_rx_scb) goto _fail_end;
-#endif
-
+	if (!record_mix_scb) goto _fail_end;
 
 	/* create secondary CODEC output */
 	sec_codec_out_scb = cs46xx_dsp_create_codec_out_scb(chip,"CodecOutSCB_II",0x0010,0x0040,
@@ -1418,7 +1320,10 @@
 							     codec_out_scb,
 							     sec_codec_out_scb,
 							     SCB_ON_PARENT_SUBLIST_SCB);
+
 	if (!magic_snoop_scb) goto _fail_end;
+	ins->ref_snoop_scb = magic_snoop_scb;
+
 
 	/* The asynch. transfer task */
 	asynch_tx_scb = cs46xx_dsp_create_asynch_fg_tx_scb(chip,"AsynchFGTxSCB",ASYNCTX_SCB_ADDR,
@@ -1444,17 +1349,21 @@
 		goto _fail_end;
 
 
-	/* the magic snooper */
-#if 0
-	sec_magic_snoop_scb = cs46xx_dsp_create_magic_snoop_scb (chip,"MagicSnoopSCB_II",OUTPUTSNOOPII_SCB_ADDR,
-								 MIX_SAMPLE_BUF1,
-								 master_mix_scb,
-								 asynch_tx_scb,
-								 SCB_ON_PARENT_SUBLIST_SCB);
-	if (!sec_magic_scb) goto _fail_end;
-#endif
+	/* SPDIF input sampel rate converter */
+	src_task_scb = cs46xx_dsp_create_src_task_scb(chip,"SrcTaskSCB_SPDIFI",
+						      SRC_OUTPUT_BUF1,
+						      SRC_DELAY_BUF1,SRCTASK_SCB_ADDR,
+						      master_mix_scb,
+						      SCB_ON_PARENT_SUBLIST_SCB);
+
+	if (!src_task_scb) goto _fail_end;
+
+	cs46xx_src_unlink(chip,src_task_scb);
+
+	/* NOTE: when we now how to detect the SPDIF input
+	   sample rate we will use this SRC to adjust it */
+	ins->spdif_in_src = src_task_scb;
 
-	//debug_scb = 0;
 	cs46xx_dsp_async_init(chip,timing_master_scb);
 	return 0;
 
@@ -1493,15 +1402,19 @@
 		spdifoscb_t spdifo_scb = {
 			/* 0 */ DSP_SPOS_UUUU,
 			{
-				/* 1 */ 0xb0, //DSP_SPOS_UUUU,
-				/* 2 */ 0, //DSP_SPOS_UUUU,
-				/* 3 */ 0, //DSP_SPOS_UUUU, 
-				/* 4 */ 0, //DSP_SPOS_UUUU,
+				/* 1 */ 0xb0, 
+				/* 2 */ 0, 
+				/* 3 */ 0, 
+				/* 4 */ 0, 
 			},
-			/* 5 */ 0x00000085,
+			/* NOTE: the SPDIF output task read samples in mono
+			   format, the AsynchFGTxSCB task writes to buffer
+			   in stereo format
+			*/
+			/* 5 */ RSCONFIG_SAMPLE_16MONO + RSCONFIG_MODULO_256,
 			/* 6 */ ( SPDIFO_IP_OUTPUT_BUFFER1 << 0x10 )  |  0xFFFC,
-			/* 7 */ 0,0, // DSP_SPOS_UU,1, //DSP_SPOS_DC,DSP_SPOS_UU, 
-			/* 8 */ 0, //DSP_SPOS_UUUU,      
+			/* 7 */ 0,0, 
+			/* 8 */ 0, 
 			/* 9 */ FG_TASK_HEADER_ADDR, NULL_SCB_ADDR, 
 			/* A */ spdifo_task->address,
 			SPDIFO_SCB_INST + SPDIFOFIFOPointer,
@@ -1528,8 +1441,11 @@
 			/* 9 */ SPDIFO_SCB_INST, NULL_SCB_ADDR,
 			/* A */ spdifi_task->address,
 			SPDIFI_SCB_INST + SPDIFIFIFOPointer,
-              
-			/* B */ 0x00000083,				
+			/* NOTE: The SPDIF input task write the sample in mono
+			   format from the HW FIFO, the AsynchFGRxSCB task  reads 
+			   them in stereo 
+			*/
+			/* B */ RSCONFIG_SAMPLE_16MONO + RSCONFIG_MODULO_128,
 			/* C */ (SPDIFI_IP_OUTPUT_BUFFER1 << 0x10) | 0xFFFC,
 			/* D */ 0x8048,0,
 			/* E */ 0x01f0,0x0001,
@@ -1543,7 +1459,7 @@
 			/* 2 */ 0,
 			/* 3 */ 1,4000,
 			/* 4 */ 0x0118,0x0001,
-			/* 5 */ 0x00000083,
+			/* 5 */ RSCONFIG_SAMPLE_16MONO + RSCONFIG_MODULO_64,
 			/* 6 */ (ASYNC_IP_OUTPUT_BUFFER1 << 0x10) | 0xFFFC,
 			/* 7 */ DSP_SPOS_UU,0x3,
 			/* 8 */ DSP_SPOS_UUUU,
@@ -1614,8 +1530,8 @@
 		   is the FG task tree */
 		fg_entry->parent_scb_ptr = spdifo_scb_desc;
 
-		/* dirty hack to start forground task ... */
-		//snd_cs46xx_poke(chip,DSP_PARAMETER_BYTE_OFFSET  + 0x070 * sizeof(u32),0x066a0ba0);
+		/* Async MASTER ENABLE, affects both SPDIF input and output */
+		snd_cs46xx_pokeBA0(chip, BA0_ASER_MASTER, 0x1 );
 	}
 
 	return 0;
@@ -1626,19 +1542,7 @@
 	dsp_spos_instance_t * ins = chip->dsp_spos_instance;
 
 	/* set SPDIF output FIFO slot */
-	snd_cs46xx_pokeBA0(chip, BA0_ASER_FADDR, ( 0x8100 | ((SP_SPDOUT_FIFO >> 4) << 4) ));
-
-	/* Async MASTER ENABLE */
-	snd_cs46xx_pokeBA0(chip, BA0_ASER_MASTER, 0x1 );
-
-#if 0
-	/* reset buffers */
-	snd_cs46xx_poke (chip, (SPDIFO_SCB_INST + 6) << 2,(SPDIFO_IP_OUTPUT_BUFFER1 << 0x10)  |  0xFFFC);
-	snd_cs46xx_poke (chip, (ASYNCTX_SCB_ADDR + 12) << 2, (SPDIFO_IP_OUTPUT_BUFFER1) << 0x10);
-
-	/* insert the foreground asynch tranfer task */
-	snd_cs46xx_poke (chip, (MASTERMIX_SCB_ADDR + 9) << 2, ASYNCTX_SCB_ADDR << 0x10 /*| ASYNCRX_SCB_ADDR*/);
-#endif
+	snd_cs46xx_pokeBA0(chip, BA0_ASER_FADDR, ( 0x8000 | ((SP_SPDOUT_FIFO >> 4) << 4) ));
 
 	/* SPDIF output MASTER ENABLE */
 	cs46xx_poke_via_dsp (chip,SP_SPDOUT_CONTROL, 0x80000000);
@@ -1658,13 +1562,8 @@
 {
 	dsp_spos_instance_t * ins = chip->dsp_spos_instance;
 
-#if 0
-	/* unlink the foreground asynch tranfer task */
-	snd_cs46xx_poke (chip, (MASTERMIX_SCB_ADDR + 9) << 2, SRCTASK_SCB_ADDR << 0x10 /* | ASYNCRX_SCB_ADDR*/);
-#endif
-
-	/* Async MASTER DISABLE */
-	snd_cs46xx_pokeBA0(chip, BA0_ASER_MASTER, 0x0 );
+	/* disable  SPDIF output FIFO slot */
+	snd_cs46xx_pokeBA0(chip, BA0_ASER_FADDR, 0);
 
 	/* SPDIF output MASTER DISABLE */
 	cs46xx_poke_via_dsp (chip,SP_SPDOUT_CONTROL, 0x0);
@@ -1679,17 +1578,44 @@
 {
 	dsp_spos_instance_t * ins = chip->dsp_spos_instance;
 
-	/* Async MASTER ENABLE */
-	snd_cs46xx_pokeBA0(chip, BA0_ASER_MASTER, 0x1 );
+	/* turn on amplifier */
+	chip->active_ctrl(chip, 1);
+	chip->amplifier_ctrl(chip, 1);
+
+	snd_assert (ins->asynch_rx_scb == NULL,return -EINVAL);
+	snd_assert (ins->spdif_in_src != NULL,return -EINVAL);
+
+	down(&chip->spos_mutex);
+	/* create and start the asynchronous receiver SCB */
+	ins->asynch_rx_scb = cs46xx_dsp_create_asynch_fg_rx_scb(chip,"AsynchFGRxSCB",
+								ASYNCRX_SCB_ADDR,
+								SPDIFI_SCB_INST,
+								SPDIFI_IP_OUTPUT_BUFFER1,
+								ins->spdif_in_src,
+								SCB_ON_PARENT_SUBLIST_SCB);
 
-	/* Time countdown enable */
-	cs46xx_poke_via_dsp (chip,SP_ASER_COUNTDOWN, 0x80000000);
+	spin_lock_irq(&chip->reg_lock);
 
-	/* SPDIF input MASTER ENABLE */
-	cs46xx_poke_via_dsp (chip,SP_SPDIN_CONTROL, 0x800003ff);
+	/* reset SPDIF input sample buffer pointer */
+	snd_cs46xx_poke (chip, (SPDIFI_SCB_INST + 0x0c) << 2,
+			 (SPDIFI_IP_OUTPUT_BUFFER1 << 0x10) | 0xFFFC);
+
+	/* reset FIFO ptr */
+	cs46xx_poke_via_dsp (chip,SP_SPDIN_FIFOPTR, 0x0);
+	cs46xx_src_link(chip,ins->spdif_in_src);
+
+	/* restore SPDIF input volume */
+	snd_cs46xx_poke(chip, (ASYNCRX_SCB_ADDR + 0xE) << 2, ins->spdif_input_volume);
+	snd_cs46xx_poke(chip, (ASYNCRX_SCB_ADDR + 0xF) << 2, ins->spdif_input_volume);
+	spin_unlock_irq(&chip->reg_lock);
+
+	/* set SPDIF input sample rate and unmute
+	   NOTE: only 48khz support for SPDIF input this time */
+	cs46xx_dsp_set_src_sample_rate(chip,ins->spdif_in_src,48000);
 
 	/* monitor state */
 	ins->spdif_status_in = 1;
+	up(&chip->spos_mutex);
 
 	return 0;
 }
@@ -1698,15 +1624,86 @@
 {
 	dsp_spos_instance_t * ins = chip->dsp_spos_instance;
 
-	/* SPDIF input MASTER DISABLE */
-	cs46xx_poke_via_dsp (chip,SP_SPDIN_CONTROL, 0x000003ff);
+	snd_assert (ins->asynch_rx_scb != NULL, return -EINVAL);
+	snd_assert (ins->spdif_in_src != NULL,return -EINVAL);
+
+	down(&chip->spos_mutex);
+	/* Remove the asynchronous receiver SCB */
+	cs46xx_dsp_remove_scb (chip,ins->asynch_rx_scb);
+	ins->asynch_rx_scb = NULL;
+
+	cs46xx_src_unlink(chip,ins->spdif_in_src);
 
 	/* monitor state */
 	ins->spdif_status_in = 0;
+	up(&chip->spos_mutex);
+
+	/* restore amplifier */
+	chip->active_ctrl(chip, -1);
+	chip->amplifier_ctrl(chip, -1);
+
+	return 0;
+}
+
+int cs46xx_dsp_enable_pcm_capture (cs46xx_t *chip)
+{
+	dsp_spos_instance_t * ins = chip->dsp_spos_instance;
+
+	snd_assert (ins->pcm_input == NULL,return -EINVAL);
+	snd_assert (ins->ref_snoop_scb != NULL,return -EINVAL);
+
+	down(&chip->spos_mutex);
+	ins->pcm_input = cs46xx_add_record_source(chip,ins->ref_snoop_scb,PCMSERIALIN_PCM_SCB_ADDR,
+                                                  "PCMSerialInput_Wave");
+	up(&chip->spos_mutex);
+
+	return 0;
+}
+
+int cs46xx_dsp_disable_pcm_capture (cs46xx_t *chip)
+{
+	dsp_spos_instance_t * ins = chip->dsp_spos_instance;
+
+	snd_assert (ins->pcm_input != NULL,return -EINVAL);
+
+	down(&chip->spos_mutex);
+	cs46xx_dsp_remove_scb (chip,ins->pcm_input);
+	ins->pcm_input = NULL;
+	up(&chip->spos_mutex);
+
+	return 0;
+}
+
+int cs46xx_dsp_enable_adc_capture (cs46xx_t *chip)
+{
+	dsp_spos_instance_t * ins = chip->dsp_spos_instance;
+
+	snd_assert (ins->adc_input == NULL,return -EINVAL);
+	snd_assert (ins->codec_in_scb != NULL,return -EINVAL);
+
+	down(&chip->spos_mutex);
+	ins->adc_input = cs46xx_add_record_source(chip,ins->codec_in_scb,PCMSERIALIN_SCB_ADDR,
+						  "PCMSerialInput_ADC");
+	up(&chip->spos_mutex);
+
+	return 0;
+}
+
+int cs46xx_dsp_disable_adc_capture (cs46xx_t *chip)
+{
+	dsp_spos_instance_t * ins = chip->dsp_spos_instance;
+
+	snd_assert (ins->adc_input != NULL,return -EINVAL);
+
+    down(&chip->spos_mutex);
+	cs46xx_dsp_remove_scb (chip,ins->adc_input);
+	ins->adc_input = NULL;
+    up(&chip->spos_mutex);
+
 	return 0;
 }
 
-int  cs46xx_poke_via_dsp (cs46xx_t *chip,u32 address,u32 data)
+int cs46xx_poke_via_dsp (cs46xx_t *chip,u32 address,u32 data)
 {
 	u32 temp;
 	int  i;
@@ -1721,7 +1718,7 @@
 
 	snd_cs46xx_poke(chip,( SPIOWRITE_SCB_ADDR      << 2), temp);
 	snd_cs46xx_poke(chip,((SPIOWRITE_SCB_ADDR + 1) << 2), data); /* offset 1 <-- data1 */
-	snd_cs46xx_poke(chip,((SPIOWRITE_SCB_ADDR + 2) << 2), data); /* offset 1 <-- data2*/
+	snd_cs46xx_poke(chip,((SPIOWRITE_SCB_ADDR + 2) << 2), data); /* offset 1 <-- data2 */
     
 	/* Poke this location to tell the task to start */
 	snd_cs46xx_poke(chip,((SPIOWRITE_SCB_ADDR + 6) << 2), SPIOWRITE_SCB_ADDR << 0x10);
diff -Nru a/sound/pci/cs46xx/dsp_spos.h b/sound/pci/cs46xx/dsp_spos.h
--- a/sound/pci/cs46xx/dsp_spos.h	Mon Oct  7 11:25:25 2002
+++ b/sound/pci/cs46xx/dsp_spos.h	Mon Oct  7 11:25:25 2002
@@ -23,6 +23,10 @@
  * 2002-07 Benny Sjostrand benny@hostmobility.com
  */
 
+#ifdef  CONFIG_SND_CS46XX_NEW_DSP /* hack ... */
+#ifndef __DSP_SPOS_H__
+#define __DSP_SPOS_H__
+
 #define DSP_MAX_SYMBOLS 1024
 #define DSP_MAX_MODULES 64
 
@@ -40,20 +44,20 @@
    needs to be reallocated when load
    code into DSP */
 typedef enum  {
-  WIDE_FOR_BEGIN_LOOP = 0x20,
-  WIDE_FOR_BEGIN_LOOP2,
+	WIDE_FOR_BEGIN_LOOP = 0x20,
+	WIDE_FOR_BEGIN_LOOP2,
 
-  WIDE_COND_GOTO_ADDR = 0x30,
-  WIDE_COND_GOTO_CALL,
+	WIDE_COND_GOTO_ADDR = 0x30,
+	WIDE_COND_GOTO_CALL,
 
-  WIDE_TBEQ_COND_GOTO_ADDR = 0x70,
-  WIDE_TBEQ_COND_CALL_ADDR,
-  WIDE_TBEQ_NCOND_GOTO_ADDR,
-  WIDE_TBEQ_NCOND_CALL_ADDR,
-  WIDE_TBEQ_COND_GOTO1_ADDR,
-  WIDE_TBEQ_COND_CALL1_ADDR,
-  WIDE_TBEQ_NCOND_GOTOI_ADDR,
-  WIDE_TBEQ_NCOND_CALL1_ADDR,
+	WIDE_TBEQ_COND_GOTO_ADDR = 0x70,
+	WIDE_TBEQ_COND_CALL_ADDR,
+	WIDE_TBEQ_NCOND_GOTO_ADDR,
+	WIDE_TBEQ_NCOND_CALL_ADDR,
+	WIDE_TBEQ_COND_GOTO1_ADDR,
+	WIDE_TBEQ_COND_CALL1_ADDR,
+	WIDE_TBEQ_NCOND_GOTOI_ADDR,
+	WIDE_TBEQ_NCOND_CALL1_ADDR,
 } wide_opcode_t;
 
 /* SAMPLE segment */
@@ -69,9 +73,7 @@
 #define SPDIFI_IP_OUTPUT_BUFFER1 0x0E00
 #define SPDIFO_IP_OUTPUT_BUFFER1 0x1000
 #define MIX_SAMPLE_BUF1          0x1400
-
-// #define SRC_OUTPUT_BUF2          0x1280
-// #define SRC_DELAY_BUF2           0x1288
+#define MIX_SAMPLE_BUF2          0x3000
 
 /* Task stack address */
 #define HFG_STACK                0x066A
@@ -100,6 +102,8 @@
 #define SPIOWRITE_SCB_ADDR       0x130
 #define SEC_CODECOUT_SCB_ADDR    0x140
 #define OUTPUTSNOOPII_SCB_ADDR   0x150
+#define PCMSERIALIN_PCM_SCB_ADDR 0x160
+#define RECORD_MIXER_SCB_ADDR    0x170
 
 /* hyperforground SCB's*/
 #define HFG_TREE_SCB             0xBA0
@@ -123,19 +127,45 @@
 /* conf */
 #define UseASER1Input 1
 
-/* constants */
+
+
+/*
+ * The following defines are for the flags in the rsConfig01/23 registers of
+ * the SP.
+ */
+
+#define RSCONFIG_MODULO_SIZE_MASK               0x0000000FL
+#define RSCONFIG_MODULO_16                      0x00000001L
+#define RSCONFIG_MODULO_32                      0x00000002L
+#define RSCONFIG_MODULO_64                      0x00000003L
+#define RSCONFIG_MODULO_128                     0x00000004L
+#define RSCONFIG_MODULO_256                     0x00000005L
+#define RSCONFIG_MODULO_512                     0x00000006L
+#define RSCONFIG_MODULO_1024                    0x00000007L
+#define RSCONFIG_MODULO_4                       0x00000008L
+#define RSCONFIG_MODULO_8                       0x00000009L
+#define RSCONFIG_SAMPLE_SIZE_MASK               0x000000C0L
+#define RSCONFIG_SAMPLE_8MONO                   0x00000000L
+#define RSCONFIG_SAMPLE_8STEREO                 0x00000040L
+#define RSCONFIG_SAMPLE_16MONO                  0x00000080L
+#define RSCONFIG_SAMPLE_16STEREO                0x000000C0L
+#define RSCONFIG_UNDERRUN_ZERO                  0x00004000L
+#define RSCONFIG_DMA_TO_HOST                    0x00008000L
+#define RSCONFIG_STREAM_NUM_MASK                0x00FF0000L
+#define RSCONFIG_MAX_DMA_SIZE_MASK              0x1F000000L
+#define RSCONFIG_DMA_ENABLE                     0x20000000L
+#define RSCONFIG_PRIORITY_MASK                  0xC0000000L
+#define RSCONFIG_PRIORITY_HIGH                  0x00000000L
+#define RSCONFIG_PRIORITY_MEDIUM_HIGH           0x40000000L
+#define RSCONFIG_PRIORITY_MEDIUM_LOW            0x80000000L
+#define RSCONFIG_PRIORITY_LOW                   0xC0000000L
+#define RSCONFIG_STREAM_NUM_SHIFT               16L
+#define RSCONFIG_MAX_DMA_SIZE_SHIFT             24L
+
+/* SP constants */
 #define FG_INTERVAL_TIMER_PERIOD                0x0051
 #define BG_INTERVAL_TIMER_PERIOD                0x0100
-#define RSCONFIG_MODULO_32                      0x00000002
-#define RSCONFIG_MODULO_64                      0x00000003
-#define RSCONFIG_MODULO_256                     0x00000005
-#define RSCONFIG_MODULO_8                       0x00000009
-#define RSCONFIG_SAMPLE_16STEREO                0x000000C0
-#define RSCONFIG_SAMPLE_16MONO                  0x00000080
-#define RSCONFIG_DMA_TO_HOST                    0x00008000
-#define RSCONFIG_DMA_ENABLE                     0x20000000
-#define RSCONFIG_STREAM_NUM_SHIFT               16
-#define RSCONFIG_MAX_DMA_SIZE_SHIFT             24
+
 
 /* Only SP accesible registers */
 #define SP_ASER_COUNTDOWN 0x8040
@@ -148,3 +178,6 @@
 #define SP_SPDOUT_STATUS  0x804C
 #define SP_SPDOUT_CONTROL 0x804D
 #define SP_SPDOUT_CSUV    0x808E
+
+#endif /* __DSP_SPOS_H__ */
+#endif /* CONFIG_SND_CS46XX_NEW_DSP  */
diff -Nru a/sound/pci/cs46xx/dsp_spos_scb_lib.c b/sound/pci/cs46xx/dsp_spos_scb_lib.c
--- a/sound/pci/cs46xx/dsp_spos_scb_lib.c	Mon Oct  7 11:25:39 2002
+++ b/sound/pci/cs46xx/dsp_spos_scb_lib.c	Mon Oct  7 11:25:39 2002
@@ -21,7 +21,6 @@
  */
 
 
-#define __NO_VERSION__
 #include <sound/driver.h>
 #include <asm/io.h>
 #include <linux/delay.h>
@@ -76,7 +75,7 @@
 
 	ins = chip->dsp_spos_instance;
 
-	down(&ins->scb_mutex);
+	down(&chip->spos_mutex);
 	snd_iprintf(buffer,"%04x %s:\n",scb->address,scb->scb_name);
 
 	for (col = 0,j = 0;j < 0x10; j++,col++) {
@@ -104,7 +103,7 @@
 		    scb->task_entry->address);
 
 	snd_iprintf(buffer,"index [%d] ref_count [%d]\n",scb->index,scb->ref_count);  
-	up(&ins->scb_mutex);
+	up(&chip->spos_mutex);
 }
 
 static void _dsp_unlink_scb (cs46xx_t *chip,dsp_scb_descriptor_t * scb)
@@ -138,7 +137,7 @@
 				scb->next_scb_ptr = ins->the_null_scb;
 			}
 		} else {
-			snd_assert ( (scb->sub_list_ptr == ins->the_null_scb), return);
+			/* snd_assert ( (scb->sub_list_ptr == ins->the_null_scb), return); */
 			scb->parent_scb_ptr->next_scb_ptr = scb->next_scb_ptr;
 
 			if (scb->next_scb_ptr != ins->the_null_scb) {
@@ -149,31 +148,42 @@
 		}
 
 		spin_lock_irqsave(&chip->reg_lock, flags);    
-		/* update entry in DSP RAM */
-		snd_cs46xx_poke(chip,
-				(scb->address + SCBsubListPtr) << 2,
-				(scb->sub_list_ptr->address << 0x10) |
-				(scb->next_scb_ptr->address));
-		/* update parent entry in DSP RAM */
+
+		/* update parent first entry in DSP RAM */
 		snd_cs46xx_poke(chip,
 				(scb->parent_scb_ptr->address + SCBsubListPtr) << 2,
 				(scb->parent_scb_ptr->sub_list_ptr->address << 0x10) |
 				(scb->parent_scb_ptr->next_scb_ptr->address));
+
+		/* then update entry in DSP RAM */
+		snd_cs46xx_poke(chip,
+				(scb->address + SCBsubListPtr) << 2,
+				(scb->sub_list_ptr->address << 0x10) |
+				(scb->next_scb_ptr->address));
     
 		scb->parent_scb_ptr = NULL;
 		spin_unlock_irqrestore(&chip->reg_lock, flags);
 	}
 }
 
+static void _dsp_clear_sample_buffer (cs46xx_t *chip, u32 sample_buffer_addr, int dword_count) 
+{
+	u32 dst = chip->region.idx[2].remap_addr + sample_buffer_addr;
+	int i;
+  
+	for (i = 0; i < dword_count ; ++i ) {
+		writel(0, dst);
+	}  
+}
+
 void cs46xx_dsp_remove_scb (cs46xx_t *chip, dsp_scb_descriptor_t * scb)
 {
 	dsp_spos_instance_t * ins = chip->dsp_spos_instance;
 
-	down(&ins->scb_mutex);
 	/* check integrety */
 	snd_assert ( (scb->index >= 0 && 
 		      scb->index < ins->nscb && 
-		      (ins->scbs + scb->index) == scb), goto _end );
+		      (ins->scbs + scb->index) == scb), return );
 
 #if 0
 	/* cant remove a SCB with childs before 
@@ -183,10 +193,12 @@
 		     goto _end);
 #endif
 
+	spin_lock(&scb->lock);
 	_dsp_unlink_scb (chip,scb);
-  
+	spin_unlock(&scb->lock);
+
 	cs46xx_dsp_proc_free_scb_desc(scb);
-	snd_assert (scb->scb_symbol != NULL, goto _end);
+	snd_assert (scb->scb_symbol != NULL, return );
 	remove_symbol (chip,scb->scb_symbol);
 
 	ins->scbs[scb->index].deleted = 1;
@@ -208,11 +220,6 @@
 		ins->scbs[i - 1].index = i - 1;
 	}
 #endif
-
-#ifdef CONFIG_SND_DEBUG
- _end:
-#endif
-	up(&ins->scb_mutex);
 }
 
 
@@ -237,7 +244,6 @@
 	snd_info_entry_t * entry;
 	proc_scb_info_t * scb_info;
 
-	down(&ins->scb_mutex);
 	/* register to proc */
 	if (ins->snd_card != NULL && ins->proc_dsp_dir != NULL &&
 	    scb->proc_info == NULL) {
@@ -264,7 +270,6 @@
 
 		scb->proc_info = entry;
 	}
-	up(&ins->scb_mutex);
 }
 
 static dsp_scb_descriptor_t * 
@@ -278,8 +283,7 @@
   
 	unsigned long flags;
 
-	down(&ins->scb_mutex);
-	snd_assert (ins->the_null_scb != NULL,goto _fail_end);
+	snd_assert (ins->the_null_scb != NULL,return NULL);
 
 	/* fill the data that will be wroten to DSP */
 	scb_data[SCBsubListPtr] = 
@@ -310,17 +314,17 @@
 		/* link to  parent SCB */
 		if (scb_child_type == SCB_ON_PARENT_NEXT_SCB) {
 			snd_assert ( (scb->parent_scb_ptr->next_scb_ptr == ins->the_null_scb),
-				     goto _fail_end);
+				     return NULL);
 
 			scb->parent_scb_ptr->next_scb_ptr = scb;
 
 		} else if (scb_child_type == SCB_ON_PARENT_SUBLIST_SCB) {
 			snd_assert ( (scb->parent_scb_ptr->sub_list_ptr == ins->the_null_scb),
-				     goto _fail_end);
+				     return NULL);
 
 			scb->parent_scb_ptr->sub_list_ptr = scb;
 		} else {
-			snd_assert (0,goto _fail_end);
+			snd_assert (0,return NULL);
 		}
 
 		spin_lock_irqsave(&chip->reg_lock, flags);
@@ -334,17 +338,9 @@
 	}
 
 
-	up(&ins->scb_mutex);
-
 	cs46xx_dsp_proc_register_scb_desc (chip,scb);
 
 	return scb;
-#ifdef CONFIG_SND_DEBUG
- _fail_end:
-
-	up(&ins->scb_mutex);
-	return NULL;
-#endif
 }
 
 dsp_scb_descriptor_t * 
@@ -563,9 +559,10 @@
 		/* Fractional increment per output sample in the input sample buffer */
 		0, 
 		{
-			/* Standard stereo volume control */
-			0x8000,0x8000,
-			0x8000,0x8000
+			/* Standard stereo volume control
+			   default muted */
+			0xffff,0xffff,
+			0xffff,0xffff
 		}
 	};
 
@@ -613,8 +610,8 @@
 		src_buffer_addr << 0x10,
 		0x04000000,
 		{ 
-			0x8000,0x8000,
-			0xFFFF,0xFFFF
+			0xffff,0xffff,
+			0xffff,0xffff
 		}
 	};
 
@@ -628,6 +625,10 @@
 		}    
 	}
 
+	/* clear buffers */
+	_dsp_clear_sample_buffer (chip,src_buffer_addr,8);
+	_dsp_clear_sample_buffer (chip,src_delay_buffer_addr,32);
+
 	scb = _dsp_create_generic_scb(chip,scb_name,(u32 *)&src_task_scb,
 				      dest,ins->s16_up,parent_scb,
 				      scb_child_type);
@@ -642,37 +643,37 @@
                                dsp_scb_descriptor_t * parent_scb,
                                int scb_child_type)
 {
-  dsp_scb_descriptor_t * scb;
+	dsp_scb_descriptor_t * scb;
   
-  mix_only_scb_t master_mix_scb = {
-    /* 0 */ { 0,
-    /* 1 */   0,
-    /* 2 */  mix_buffer_addr,
-    /* 3 */  0
-    /*   */ },
-    {
-    /* 4 */  0,
-    /* 5 */  0,
-    /* 6 */  0,
-    /* 7 */  0,
-    /* 8 */  0x00000080
-    },
-    /* 9 */ 0,0,
-    /* A */ 0,0,
-    /* B */ RSCONFIG_SAMPLE_16STEREO + RSCONFIG_MODULO_64,
-    /* C */ (mix_buffer_addr  + (32 * 4)) << 0x10, 
-    /* D */ 0,
-    {
-    /* E */ 0x8000,0x8000,
-    /* F */ 0xFFFF,0xFFFF
-    }
-  };
-
-
-  scb = cs46xx_dsp_create_generic_scb(chip,scb_name,(u32 *)&master_mix_scb,
-                                      dest,"S16_MIX",parent_scb,
-                                      scb_child_type);
-  return scb;
+	mix_only_scb_t master_mix_scb = {
+		/* 0 */ { 0,
+			  /* 1 */   0,
+			  /* 2 */  mix_buffer_addr,
+			  /* 3 */  0
+			  /*   */ },
+		{
+			/* 4 */  0,
+			/* 5 */  0,
+			/* 6 */  0,
+			/* 7 */  0,
+			/* 8 */  0x00000080
+		},
+		/* 9 */ 0,0,
+		/* A */ 0,0,
+		/* B */ RSCONFIG_SAMPLE_16STEREO + RSCONFIG_MODULO_64,
+		/* C */ (mix_buffer_addr  + (32 * 4)) << 0x10, 
+		/* D */ 0,
+		{
+			/* E */ 0x8000,0x8000,
+			/* F */ 0x8000,0x8000
+		}
+	};
+
+
+	scb = cs46xx_dsp_create_generic_scb(chip,scb_name,(u32 *)&master_mix_scb,
+					    dest,"S16_MIX",parent_scb,
+					    scb_child_type);
+	return scb;
 }
 
 
@@ -800,7 +801,7 @@
 		0,0,
 		0,0,
 
-		0x000000c1,
+		RSCONFIG_SAMPLE_16STEREO + RSCONFIG_MODULO_16,
 		0,
 		0,input_scb->address, 
 		{
@@ -844,9 +845,9 @@
 		0,0,
 		0,dest + AFGTxAccumPhi,
     
-		0x000000c5,                       /* Stereo, 256 dword */
+		RSCONFIG_SAMPLE_16STEREO + RSCONFIG_MODULO_256, /* Stereo, 256 dword */
 		(asynch_buffer_address) << 0x10,  /* This should be automagically synchronized
-						     to the producer pointer */
+                                             to the producer pointer */
     
 		/* There is no correct initial value, it will depend upon the detected
 		   rate etc  */
@@ -874,9 +875,9 @@
 	dsp_scb_descriptor_t * scb;
 
 	asynch_fg_rx_scb_t asynch_fg_rx_scb = {
-		0xff00,0x00ff,      /* Prototype sample buffer size of 512 dwords */
+		0xfe00,0x01ff,      /*  Prototype sample buffer size of 128 dwords */
 		0x0064,0x001c,      /* Min Delta 7 dwords == 28 bytes */
-		/* : Max delta 25 dwords == 100 bytes */
+		                    /* : Max delta 25 dwords == 100 bytes */
 		0,hfg_scb_address,  /* Point to HFG task SCB */
 		0,0,				/* Initialize current Delta and Consumer ptr adjustment count */
 		{
@@ -890,15 +891,18 @@
 		0,0,
 		0,dest,
     
-		0x000000c3,                            /* Stereo, 512 dword */
-		(asynch_buffer_address  << 0x10),      /* This should be automagically 
-							  synchrinized to the producer pointer */
+		RSCONFIG_MODULO_128 |
+        RSCONFIG_SAMPLE_16STEREO,                         /* Stereo, 128 dword */
+		( (asynch_buffer_address + (16 * 4))  << 0x10),   /* This should be automagically 
+							                                  synchrinized to the producer pointer */
     
 		/* There is no correct initial value, it will depend upon the detected
 		   rate etc  */
-		0,         
+		0x18000000,         
+
+		/* Mute stream */
 		0x8000,0x8000,       
-		0xFFFF,0xFFFF
+		0xffff,0xffff
 	};
 
 	scb = cs46xx_dsp_create_generic_scb(chip,scb_name,(u32 *)&asynch_fg_rx_scb,
@@ -936,7 +940,7 @@
 		0,0,
 		0,0,
     
-		0x000000c3,
+		RSCONFIG_SAMPLE_16STEREO + RSCONFIG_MODULO_64,
 		snoop_buffer_address << 0x10,  
 		0,0,
 		0,
@@ -1088,17 +1092,16 @@
 	0x2B00,
 };
 
-pcm_channel_descriptor_t * cs46xx_dsp_create_pcm_channel (cs46xx_t * chip,u32 sample_rate, void * private_data)
+
+pcm_channel_descriptor_t * cs46xx_dsp_create_pcm_channel (cs46xx_t * chip,u32 sample_rate, void * private_data, u32 hw_dma_addr)
 {
 	dsp_spos_instance_t * ins = chip->dsp_spos_instance;
 	dsp_scb_descriptor_t * src_scb = NULL,* pcm_scb;
-	dsp_scb_descriptor_t * pcm_parent_scb;
+	/*dsp_scb_descriptor_t * pcm_parent_scb;*/
 	char scb_name[DSP_MAX_SCB_NAME];
 	int i,pcm_index = -1, insert_point, src_index = -1;
 	unsigned long flags;
 
-	down(&ins->pcm_mutex); 
-
 	/* default sample rate is 44100 */
 	if (!sample_rate) sample_rate = 44100;
 
@@ -1123,7 +1126,7 @@
 
 	if (pcm_index == -1) {
 		snd_printk (KERN_ERR "dsp_spos: no free PCM channel\n");
-		goto _end;
+		return NULL;
 	}
 
 	if (src_scb == NULL) {
@@ -1131,7 +1134,7 @@
 
 		if (ins->nsrc_scb >= DSP_MAX_SRC_NR) {
 			snd_printk(KERN_ERR "dsp_spos: to many SRC instances\n!");
-			goto _end;
+			return NULL;
 		}
 
 		/* find a free slot */
@@ -1142,7 +1145,7 @@
 				break;
 			}
 		}
-		snd_assert (src_index != -1,goto _end);
+		snd_assert (src_index != -1,return NULL);
 
 		/* we need to create a new SRC SCB */
 		if (ins->master_mix_scb->sub_list_ptr == ins->the_null_scb) {
@@ -1166,23 +1169,13 @@
 
 		if (!src_scb) {
 			snd_printk (KERN_ERR "dsp_spos: failed to create SRCtaskSCB\n");
-			goto _end;
+			return NULL;
 		}
 
 		cs46xx_dsp_set_src_sample_rate(chip,src_scb,sample_rate);
 
 		ins->nsrc_scb ++;
-
-		/* insert point for the PCMreader task */
-		pcm_parent_scb = src_scb;
-		insert_point = SCB_ON_PARENT_SUBLIST_SCB;
-	} else {
-		snd_assert (src_scb->sub_list_ptr != ins->the_null_scb, goto _end);
-		pcm_parent_scb = find_next_free_scb(chip,src_scb->sub_list_ptr);
-    
-		insert_point = SCB_ON_PARENT_NEXT_SCB;
-	}
-  
+	} 
   
   
 	snprintf (scb_name,DSP_MAX_SCB_NAME,"PCMReader_SCB%d",pcm_index);
@@ -1193,21 +1186,22 @@
 						   pcm_reader_buffer_addr[pcm_index],
 						   /* 0x200 - 400 PCMreader SCBs */
 						   (pcm_index * 0x10) + 0x200,
-						   pcm_index, /* virtual channel 0-31 */
-						   0, /* pcm hw addr */
-						   pcm_parent_scb,
-						   insert_point);
+						   pcm_index,    /* virtual channel 0-31 */
+						   hw_dma_addr,  /* pcm hw addr */
+                           NULL,         /* parent SCB ptr */
+                           0             /* insert point */ 
+                           );
 
 	if (!pcm_scb) {
 		snd_printk (KERN_ERR "dsp_spos: failed to create PCMreaderSCB\n");
-		goto _end;
+		return NULL;
 	}
 
 	spin_lock_irqsave(&chip->reg_lock, flags);
 	ins->pcm_channels[pcm_index].sample_rate = sample_rate;
 	ins->pcm_channels[pcm_index].pcm_reader_scb = pcm_scb;
 	ins->pcm_channels[pcm_index].src_scb = src_scb;
-	ins->pcm_channels[pcm_index].unlinked = 0;  
+	ins->pcm_channels[pcm_index].unlinked = 1;
 	ins->pcm_channels[pcm_index].private_data = private_data;
 	ins->pcm_channels[pcm_index].src_slot = src_index;
 	ins->pcm_channels[pcm_index].active = 1;
@@ -1215,12 +1209,7 @@
 	ins->npcm_channels ++;
 	spin_unlock_irqrestore(&chip->reg_lock, flags);
 
-	up(&ins->pcm_mutex);  
 	return (ins->pcm_channels + pcm_index);
- _end:
-
-	up(&ins->pcm_mutex);
-	return NULL;
 }
 
 void cs46xx_dsp_destroy_pcm_channel (cs46xx_t * chip,pcm_channel_descriptor_t * pcm_channel)
@@ -1228,11 +1217,9 @@
 	dsp_spos_instance_t * ins = chip->dsp_spos_instance;
 	unsigned long flags;
 
-	down(&ins->pcm_mutex);
-  
-	snd_assert(pcm_channel->active,goto _end);
-	snd_assert(ins->npcm_channels > 0,goto _end);
-	snd_assert(pcm_channel->src_scb->ref_count > 0,goto _end);
+	snd_assert(pcm_channel->active, return );
+	snd_assert(ins->npcm_channels > 0, return );
+	snd_assert(pcm_channel->src_scb->ref_count > 0, return );
 
 	spin_lock_irqsave(&chip->reg_lock, flags);
 	pcm_channel->unlinked = 1;
@@ -1248,18 +1235,11 @@
 		cs46xx_dsp_remove_scb(chip,pcm_channel->src_scb);
 
 		snd_assert (pcm_channel->src_slot >= 0 && pcm_channel->src_slot <= DSP_MAX_SRC_NR,
-			    goto _end);
+			    return );
 
 		ins->src_scb_slots[pcm_channel->src_slot] = 0;
 		ins->nsrc_scb --;
 	}
-
-
-#ifdef CONFIG_SND_DEBUG
- _end:
-#endif
-
-	up(&ins->pcm_mutex);
 }
 
 int cs46xx_dsp_pcm_unlink (cs46xx_t * chip,pcm_channel_descriptor_t * pcm_channel)
@@ -1267,14 +1247,15 @@
 	dsp_spos_instance_t * ins = chip->dsp_spos_instance;
 	unsigned long flags;
 
-	down(&ins->pcm_mutex);
-	down(&ins->scb_mutex);
+	snd_assert(pcm_channel->active,return -EIO);
+	snd_assert(ins->npcm_channels > 0,return -EIO);
 
-	snd_assert(pcm_channel->active,goto _end);
-	snd_assert(ins->npcm_channels > 0,goto _end);
+	spin_lock(&pcm_channel->src_scb->lock);
 
-	if (pcm_channel->unlinked)
-		goto _end;
+	if (pcm_channel->unlinked) {
+		spin_unlock(&pcm_channel->src_scb->lock);
+		return -EIO;
+	}
 
 	spin_lock_irqsave(&chip->reg_lock, flags);
 	pcm_channel->unlinked = 1;
@@ -1282,10 +1263,7 @@
 
 	_dsp_unlink_scb (chip,pcm_channel->pcm_reader_scb);
 
- _end:
-	up(&ins->scb_mutex);
-	up(&ins->pcm_mutex);
-
+	spin_unlock(&pcm_channel->src_scb->lock);
 	return 0;
 }
 
@@ -1296,26 +1274,33 @@
 	dsp_scb_descriptor_t * src_scb = pcm_channel->src_scb;
 	unsigned long flags;
 
-	down(&ins->pcm_mutex);
-	down(&ins->scb_mutex);
+	spin_lock(&pcm_channel->src_scb->lock);
+
+	if (pcm_channel->unlinked == 0) {
+		spin_unlock(&pcm_channel->src_scb->lock);
+		return -EIO;
+	}
 
-	if (pcm_channel->unlinked == 0)
-		goto _end;
+	parent_scb = src_scb;
 
-	if (src_scb->sub_list_ptr == ins->the_null_scb) {
-		parent_scb = src_scb;
-		parent_scb->sub_list_ptr = pcm_channel->pcm_reader_scb;
-	} else {
-		parent_scb = find_next_free_scb(chip,src_scb->sub_list_ptr);
-		parent_scb->next_scb_ptr = pcm_channel->pcm_reader_scb;
+	if (src_scb->sub_list_ptr != ins->the_null_scb) {
+		src_scb->sub_list_ptr->parent_scb_ptr = pcm_channel->pcm_reader_scb;
+		pcm_channel->pcm_reader_scb->next_scb_ptr = src_scb->sub_list_ptr;
 	}
-  
+
+	src_scb->sub_list_ptr = pcm_channel->pcm_reader_scb;
+
 	snd_assert (pcm_channel->pcm_reader_scb->parent_scb_ptr == NULL, ; );
 	pcm_channel->pcm_reader_scb->parent_scb_ptr = parent_scb;
 
 	/* update entry in DSP RAM */
 	spin_lock_irqsave(&chip->reg_lock, flags);
 	snd_cs46xx_poke(chip,
+			(pcm_channel->pcm_reader_scb->address + SCBsubListPtr) << 2,
+			(pcm_channel->pcm_reader_scb->sub_list_ptr->address << 0x10) |
+			(pcm_channel->pcm_reader_scb->next_scb_ptr->address));
+    
+	snd_cs46xx_poke(chip,
 			(parent_scb->address + SCBsubListPtr) << 2,
 			(parent_scb->sub_list_ptr->address << 0x10) |
 			(parent_scb->next_scb_ptr->address));
@@ -1323,13 +1308,11 @@
 	pcm_channel->unlinked = 0;
 	spin_unlock_irqrestore(&chip->reg_lock, flags);
 
- _end:
-	up(&ins->scb_mutex);
-	up(&ins->pcm_mutex);
-
+	spin_unlock(&pcm_channel->src_scb->lock);
 	return 0;
 }
 
+
 #define GOF_PER_SEC 200
   
 void cs46xx_dsp_set_src_sample_rate(cs46xx_t *chip,dsp_scb_descriptor_t * src, u32 rate)
@@ -1374,10 +1357,78 @@
 	 */
 	spin_lock_irqsave(&chip->reg_lock, flags);
 
+	/* mute SCB */
+	snd_cs46xx_poke(chip, (src->address + 0xE) << 2, 0xffffffff);
 	snd_cs46xx_poke(chip, (src->address + SRCCorPerGof) << 2,
 	  ((correctionPerSec << 16) & 0xFFFF0000) | (correctionPerGOF & 0xFFFF));
 
 	snd_cs46xx_poke(chip, (src->address + SRCPhiIncr6Int26Frac) << 2, phiIncr);
 
+	/* raise volume */
+	snd_cs46xx_poke(chip, (src->address + 0xE) << 2, 0x80008000);
 	spin_unlock_irqrestore(&chip->reg_lock, flags);
+}
+
+dsp_scb_descriptor_t * cs46xx_add_record_source (cs46xx_t *chip,dsp_scb_descriptor_t * source,
+                                                 u16 addr,char * scb_name)
+{
+  	dsp_spos_instance_t * ins = chip->dsp_spos_instance;
+	dsp_scb_descriptor_t * parent;
+	dsp_scb_descriptor_t * pcm_input;
+	int insert_point;
+
+	snd_assert (ins->record_mixer_scb != NULL,return NULL);
+
+	if (ins->record_mixer_scb->sub_list_ptr != ins->the_null_scb) {
+		parent = find_next_free_scb (chip,ins->record_mixer_scb->sub_list_ptr);
+		insert_point = SCB_ON_PARENT_NEXT_SCB;
+	} else {
+		parent = ins->record_mixer_scb;
+		insert_point = SCB_ON_PARENT_SUBLIST_SCB;
+	}
+
+	pcm_input = cs46xx_dsp_create_pcm_serial_input_scb(chip,scb_name,addr,
+							   source, parent,
+							   insert_point);
+
+	return pcm_input;
+}
+
+int cs46xx_src_unlink(cs46xx_t *chip,dsp_scb_descriptor_t * src)
+{
+	snd_assert (src->parent_scb_ptr != NULL,  return -EINVAL );
+
+	/* mute SCB */
+	snd_cs46xx_poke(chip, (src->address + 0xE) << 2, 0xffffffff);
+
+	_dsp_unlink_scb (chip,src);
+
+	return 0;
+}
+
+int cs46xx_src_link(cs46xx_t *chip,dsp_scb_descriptor_t * src)
+{
+	dsp_spos_instance_t * ins = chip->dsp_spos_instance;
+	dsp_scb_descriptor_t * parent_scb;
+
+	snd_assert (src->parent_scb_ptr == NULL,   return -EINVAL );
+	snd_assert(ins->master_mix_scb !=NULL,   return -EINVAL );
+
+	if (ins->master_mix_scb->sub_list_ptr != ins->the_null_scb) {
+		parent_scb = find_next_free_scb (chip,ins->master_mix_scb->sub_list_ptr);
+		parent_scb->next_scb_ptr = src;
+	} else {
+		parent_scb = ins->master_mix_scb;
+		parent_scb->sub_list_ptr = src;
+	}
+
+	src->parent_scb_ptr = parent_scb;
+
+	/* update entry in DSP RAM */
+	snd_cs46xx_poke(chip,
+			(parent_scb->address + SCBsubListPtr) << 2,
+			(parent_scb->sub_list_ptr->address << 0x10) |
+			(parent_scb->next_scb_ptr->address));
+  
+	return 0;
 }
diff -Nru a/sound/pci/emu10k1/emu10k1.c b/sound/pci/emu10k1/emu10k1.c
--- a/sound/pci/emu10k1/emu10k1.c	Mon Oct  7 11:25:25 2002
+++ b/sound/pci/emu10k1/emu10k1.c	Mon Oct  7 11:25:25 2002
@@ -199,10 +199,10 @@
 }
 
 static struct pci_driver driver = {
-	name: "EMU10K1/Audigy",
-	id_table: snd_emu10k1_ids,
-	probe: snd_card_emu10k1_probe,
-	remove: __devexit_p(snd_card_emu10k1_remove),
+	.name = "EMU10K1/Audigy",
+	.id_table = snd_emu10k1_ids,
+	.probe = snd_card_emu10k1_probe,
+	.remove = __devexit_p(snd_card_emu10k1_remove),
 };
 
 static int __init alsa_card_emu10k1_init(void)
diff -Nru a/sound/pci/emu10k1/emu10k1_callback.c b/sound/pci/emu10k1/emu10k1_callback.c
--- a/sound/pci/emu10k1/emu10k1_callback.c	Mon Oct  7 11:25:29 2002
+++ b/sound/pci/emu10k1/emu10k1_callback.c	Mon Oct  7 11:25:29 2002
@@ -18,7 +18,6 @@
  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  */
 
-#define __NO_VERSION__
 #include "emu10k1_synth_local.h"
 #include <sound/asoundef.h>
 
@@ -61,16 +60,16 @@
  * set up operators
  */
 static snd_emux_operators_t emu10k1_ops = {
-	owner:		THIS_MODULE,
-	get_voice:	get_voice,
-	prepare:	start_voice,
-	trigger:	trigger_voice,
-	release:	release_voice,
-	update:		update_voice,
-	terminate:	terminate_voice,
-	free_voice:	free_voice,
-	sample_new:	snd_emu10k1_sample_new,
-	sample_free:	snd_emu10k1_sample_free,
+	.owner =	THIS_MODULE,
+	.get_voice =	get_voice,
+	.prepare =	start_voice,
+	.trigger =	trigger_voice,
+	.release =	release_voice,
+	.update =	update_voice,
+	.terminate =	terminate_voice,
+	.free_voice =	free_voice,
+	.sample_new =	snd_emu10k1_sample_new,
+	.sample_free =	snd_emu10k1_sample_free,
 };
 
 void
diff -Nru a/sound/pci/emu10k1/emu10k1_main.c b/sound/pci/emu10k1/emu10k1_main.c
--- a/sound/pci/emu10k1/emu10k1_main.c	Mon Oct  7 11:25:34 2002
+++ b/sound/pci/emu10k1/emu10k1_main.c	Mon Oct  7 11:25:34 2002
@@ -25,7 +25,6 @@
  *
  */
 
-#define __NO_VERSION__
 #include <sound/driver.h>
 #include <linux/delay.h>
 #include <linux/init.h>
@@ -534,24 +533,31 @@
 {
 	emu10k1_t *emu;
 	int err;
+	int is_audigy;
 	static snd_device_ops_t ops = {
-		dev_free:	snd_emu10k1_dev_free,
+		.dev_free =	snd_emu10k1_dev_free,
 	};
 	
 	*remu = NULL;
 
+	// is_audigy = (int)pci->driver_data;
+	is_audigy = (pci->device == 0x0004);
+
 	/* enable PCI device */
 	if ((err = pci_enable_device(pci)) < 0)
 		return err;
-	/* check, if we can restrict PCI DMA transfers to 31 bits */
-	if (!pci_dma_supported(pci, 0x7fffffff)) {
-		snd_printk("architecture does not support 31bit PCI busmaster DMA\n");
-		return -ENXIO;
+	/* set the DMA transfer mask */
+	if (is_audigy) {
+		if (pci_set_dma_mask(pci, 0xffffffff) < 0) {
+			snd_printk(KERN_ERR "architecture does not support 32bit PCI busmaster DMA\n");
+			return -ENXIO;
+		}
+	} else {
+		if (pci_set_dma_mask(pci, 0x1fffffff) < 0) {
+			snd_printk(KERN_ERR "architecture does not support 29bit PCI busmaster DMA\n");
+			return -ENXIO;
+		}
 	}
-	if (pci_get_drvdata(pci))
-		pci_set_dma_mask(pci, 0xffffffff); /* audigy */
-	else
-		pci_set_dma_mask(pci, 0x7fffffff);
 
 	emu = snd_magic_kcalloc(emu10k1_t, 0, GFP_KERNEL);
 	if (emu == NULL)
@@ -572,11 +578,8 @@
 	emu->get_synth_voice = NULL;
 	emu->port = pci_resource_start(pci, 0);
 
-	// emu->audigy = (int)pci->driver_data;
-	if (pci->device == 0x0004)
-		emu->audigy = 1;
-
-	if (emu->audigy)
+	emu->audigy = is_audigy;
+	if (is_audigy)
 		emu->gpr_base = A_FXGPREGBASE;
 	else
 		emu->gpr_base = FXGPREGBASE;
diff -Nru a/sound/pci/emu10k1/emu10k1_patch.c b/sound/pci/emu10k1/emu10k1_patch.c
--- a/sound/pci/emu10k1/emu10k1_patch.c	Mon Oct  7 11:25:31 2002
+++ b/sound/pci/emu10k1/emu10k1_patch.c	Mon Oct  7 11:25:31 2002
@@ -22,7 +22,6 @@
  * chip specific here.  Just the actual writing to the board.
  */
 
-#define __NO_VERSION__
 #include "emu10k1_synth_local.h"
 
 /*
diff -Nru a/sound/pci/emu10k1/emufx.c b/sound/pci/emu10k1/emufx.c
--- a/sound/pci/emu10k1/emufx.c	Mon Oct  7 11:25:35 2002
+++ b/sound/pci/emu10k1/emufx.c	Mon Oct  7 11:25:35 2002
@@ -25,7 +25,6 @@
  *
  */
 
-#define __NO_VERSION__
 #include <sound/driver.h>
 #include <linux/delay.h>
 #include <linux/slab.h>
@@ -92,14 +91,14 @@
 	/* 0x01 */ "AC97 Right",
 	/* 0x02 */ "Audigy CD Left",
 	/* 0x03 */ "Audigy CD Right",
-	/* 0x04 */ NULL,
-	/* 0x05 */ NULL,
+	/* 0x04 */ "Optical IEC958 Left",
+	/* 0x05 */ "Optical IEC958 Right",
 	/* 0x06 */ NULL,
 	/* 0x07 */ NULL,
 	/* 0x08 */ "Line/Mic 2 Left",
 	/* 0x09 */ "Line/Mic 2 Right",
-	/* 0x0a */ NULL,
-	/* 0x0b */ NULL,
+	/* 0x0a */ "SPDIF Left",
+	/* 0x0b */ "SPDIF Right",
 	/* 0x0c */ "Aux2 Left",
 	/* 0x0d */ "Aux2 Right",
 	/* 0x0e */ NULL,
@@ -693,20 +692,20 @@
 
 static snd_pcm_hardware_t snd_emu10k1_fx8010_playback =
 {
-	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
 				 /* SNDRV_PCM_INFO_MMAP_VALID | */ SNDRV_PCM_INFO_PAUSE),
-	formats:		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
-	rates:			SNDRV_PCM_RATE_48000,
-	rate_min:		48000,
-	rate_max:		48000,
-	channels_min:		1,
-	channels_max:		1,
-	buffer_bytes_max:	(128*1024),
-	period_bytes_min:	1024,
-	period_bytes_max:	(128*1024),
-	periods_min:		1,
-	periods_max:		1024,
-	fifo_size:		0,
+	.formats =		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
+	.rates =		SNDRV_PCM_RATE_48000,
+	.rate_min =		48000,
+	.rate_max =		48000,
+	.channels_min =		1,
+	.channels_max =		1,
+	.buffer_bytes_max =	(128*1024),
+	.period_bytes_min =	1024,
+	.period_bytes_max =	(128*1024),
+	.periods_min =		1,
+	.periods_max =		1024,
+	.fifo_size =		0,
 };
 
 static int snd_emu10k1_fx8010_playback_open(snd_pcm_substream_t * substream)
@@ -740,15 +739,15 @@
 }
 
 static snd_pcm_ops_t snd_emu10k1_fx8010_playback_ops = {
-	open:			snd_emu10k1_fx8010_playback_open,
-	close:			snd_emu10k1_fx8010_playback_close,
-	ioctl:			snd_pcm_lib_ioctl,
-	hw_params:		snd_emu10k1_fx8010_playback_hw_params,
-	hw_free:		snd_emu10k1_fx8010_playback_hw_free,
-	prepare:		snd_emu10k1_fx8010_playback_prepare,
-	trigger:		snd_emu10k1_fx8010_playback_trigger,
-	copy:			snd_emu10k1_fx8010_playback_copy,
-	pointer:		snd_emu10k1_fx8010_playback_pointer,
+	.open =			snd_emu10k1_fx8010_playback_open,
+	.close =		snd_emu10k1_fx8010_playback_close,
+	.ioctl =		snd_pcm_lib_ioctl,
+	.hw_params =		snd_emu10k1_fx8010_playback_hw_params,
+	.hw_free =		snd_emu10k1_fx8010_playback_hw_free,
+	.prepare =		snd_emu10k1_fx8010_playback_prepare,
+	.trigger =		snd_emu10k1_fx8010_playback_trigger,
+	.copy =			snd_emu10k1_fx8010_playback_copy,
+	.pointer =		snd_emu10k1_fx8010_playback_pointer,
 };
 
 static void snd_emu10k1_fx8010_pcm_free(snd_pcm_t *pcm)
@@ -924,7 +923,7 @@
 			return -ENOENT;
 	}
 	for (i = 0, _gctl = icode->gpr_add_controls;
-	     i < icode->gpr_add_control_count; i++) {
+	     i < icode->gpr_add_control_count; i++, _gctl++) {
 		if (copy_from_user(&gctl, _gctl, sizeof(gctl)))
 			return -EFAULT;
 		if (snd_emu10k1_look_for_ctl(emu, &gctl.id))
@@ -935,6 +934,12 @@
 		    gctl.id.iface != SNDRV_CTL_ELEM_IFACE_PCM)
 			return -EINVAL;
 	}
+	for (i = 0, _gctl = icode->gpr_list_controls;
+	     i < icode->gpr_list_control_count; i++, _gctl++) {
+	     	/* FIXME: we need to check the WRITE access */
+		if (copy_from_user(&gctl, _gctl, sizeof(gctl)))
+			return -EFAULT;
+	}
 	return 0;
 }
 
@@ -969,6 +974,8 @@
 		knew.iface = gctl.id.iface;
 		knew.name = gctl.id.name;
 		knew.index = gctl.id.index;
+		knew.device = gctl.id.device;
+		knew.subdevice = gctl.id.subdevice;
 		knew.info = snd_emu10k1_gpr_ctl_info;
 		knew.get = snd_emu10k1_gpr_ctl_get;
 		knew.put = snd_emu10k1_gpr_ctl_put;
@@ -977,7 +984,7 @@
 		nctl.count = gctl.count;
 		for (j = 0; j < 32; j++) {
 			nctl.gpr[j] = gctl.gpr[j];
-			nctl.value[j] = ~gctl.value[j];
+			nctl.value[j] = ~gctl.value[j];	/* inverted, we want to write new value in gpr_ctl_put() */
 			val.value.integer.value[j] = gctl.value[j];
 		}
 		nctl.min = gctl.min;
@@ -1020,9 +1027,49 @@
 		ctl = snd_emu10k1_look_for_ctl(emu, &id);
 		snd_runtime_check(ctl == NULL, continue);
 		snd_ctl_remove(emu->card, ctl->kcontrol);
+		list_del(&ctl->list);
 	}
 }
 
+static void snd_emu10k1_list_controls(emu10k1_t *emu, emu10k1_fx8010_code_t *icode)
+{
+	int i = 0, j;
+	unsigned int total = 0;
+	emu10k1_fx8010_control_gpr_t *_gctl, gctl;
+	snd_emu10k1_fx8010_ctl_t *ctl;
+	snd_ctl_elem_id_t *id;
+	struct list_head *list;
+
+	_gctl = icode->gpr_list_controls;	
+	list_for_each(list, &emu->fx8010.gpr_ctl) {
+		ctl = emu10k1_gpr_ctl(list);
+		total++;
+		if (i < icode->gpr_list_control_count) {
+			memset(&gctl, 0, sizeof(gctl));
+			id = &ctl->kcontrol->id;
+			gctl.id.iface = id->iface;
+			strncpy(gctl.id.name, id->name, sizeof(gctl.id.name));
+			gctl.id.index = id->index;
+			gctl.id.device = id->device;
+			gctl.id.subdevice = id->subdevice;
+			gctl.vcount = ctl->vcount;
+			gctl.count = ctl->count;
+			for (j = 0; j < 32; j++) {
+				gctl.gpr[j] = ctl->gpr[j];
+				gctl.value[j] = ctl->value[j];
+			}
+			gctl.min = ctl->min;
+			gctl.max = ctl->max;
+			gctl.translation = ctl->translation;
+			snd_runtime_check(copy_to_user(_gctl, &gctl, sizeof(gctl)) == 0, goto __next);
+		}
+	      __next:
+		_gctl++;
+		i++;
+	}
+	icode->gpr_list_control_total = total;
+}
+
 static int snd_emu10k1_icode_poke(emu10k1_t *emu, emu10k1_fx8010_code_t *icode)
 {
 	int err = 0;
@@ -1063,6 +1110,7 @@
 	snd_emu10k1_gpr_peek(emu, icode);
 	snd_emu10k1_tram_peek(emu, icode);
 	snd_emu10k1_code_peek(emu, icode);
+	snd_emu10k1_list_controls(emu, icode);
 	up(&emu->fx8010.lock);
 	return 0;
 }
@@ -1314,6 +1362,17 @@
 	snd_emu10k1_init_stereo_control(&controls[nctl++], "Audigy CD Capture Volume", gpr, 0);
 	gpr += 2;
 
+ 	/* Optical SPDIF Playback Volume */
+	A_ADD_VOLUME_IN(playback, gpr, A_EXTIN_OPT_SPDIF_L);
+	A_ADD_VOLUME_IN(playback+1, gpr+1, A_EXTIN_OPT_SPDIF_R);
+	snd_emu10k1_init_stereo_control(&controls[nctl++], "Optical IEC958 Playback Volume", gpr, 0);
+	gpr += 2;
+	/* Optical SPDIF Capture Volume */
+	A_ADD_VOLUME_IN(capture, gpr, A_EXTIN_OPT_SPDIF_L);
+	A_ADD_VOLUME_IN(capture+1, gpr+1, A_EXTIN_OPT_SPDIF_R);
+	snd_emu10k1_init_stereo_control(&controls[nctl++], "Optical IEC958 Capture Volume", gpr, 0);
+	gpr += 2;
+
 	/* Line2 Playback Volume */
 	A_ADD_VOLUME_IN(playback, gpr, A_EXTIN_LINE2_L);
 	A_ADD_VOLUME_IN(playback+1, gpr+1, A_EXTIN_LINE2_R);
@@ -1323,6 +1382,17 @@
 	A_ADD_VOLUME_IN(capture, gpr, A_EXTIN_LINE2_L);
 	A_ADD_VOLUME_IN(capture+1, gpr+1, A_EXTIN_LINE2_R);
 	snd_emu10k1_init_stereo_control(&controls[nctl++], "Line2 Capture Volume", gpr, 0);
+	gpr += 2;
+        
+	/* RCA SPDIF Playback Volume */
+	A_ADD_VOLUME_IN(playback, gpr, A_EXTIN_RCA_SPDIF_L);
+	A_ADD_VOLUME_IN(playback+1, gpr+1, A_EXTIN_RCA_SPDIF_R);
+	snd_emu10k1_init_stereo_control(&controls[nctl++], "RCA SPDIF Playback Volume", gpr, 0);
+	gpr += 2;
+	/* RCA SPDIF Capture Volume */
+	A_ADD_VOLUME_IN(capture, gpr, A_EXTIN_RCA_SPDIF_L);
+	A_ADD_VOLUME_IN(capture+1, gpr+1, A_EXTIN_RCA_SPDIF_R);
+	snd_emu10k1_init_stereo_control(&controls[nctl++], "RCA SPDIF Capture Volume", gpr, 0);
 	gpr += 2;
 
 	/* Aux2 Playback Volume */
diff -Nru a/sound/pci/emu10k1/emumixer.c b/sound/pci/emu10k1/emumixer.c
--- a/sound/pci/emu10k1/emumixer.c	Mon Oct  7 11:25:37 2002
+++ b/sound/pci/emu10k1/emumixer.c	Mon Oct  7 11:25:37 2002
@@ -26,7 +26,6 @@
  *
  */
 
-#define __NO_VERSION__
 #include <sound/driver.h>
 #include <linux/time.h>
 #include <linux/init.h>
@@ -92,20 +91,20 @@
 
 static snd_kcontrol_new_t snd_emu10k1_spdif_mask_control =
 {
-	access:		SNDRV_CTL_ELEM_ACCESS_READ,
-        iface:          SNDRV_CTL_ELEM_IFACE_MIXER,
-        name:           SNDRV_CTL_NAME_IEC958("",PLAYBACK,MASK),
-        info:           snd_emu10k1_spdif_info,
-        get:            snd_emu10k1_spdif_get_mask
+	.access =	SNDRV_CTL_ELEM_ACCESS_READ,
+        .iface =        SNDRV_CTL_ELEM_IFACE_MIXER,
+        .name =         SNDRV_CTL_NAME_IEC958("",PLAYBACK,MASK),
+        .info =         snd_emu10k1_spdif_info,
+        .get =          snd_emu10k1_spdif_get_mask
 };
 
 static snd_kcontrol_new_t snd_emu10k1_spdif_control =
 {
-        iface:          SNDRV_CTL_ELEM_IFACE_MIXER,
-        name:           SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
-        info:           snd_emu10k1_spdif_info,
-        get:            snd_emu10k1_spdif_get,
-        put:            snd_emu10k1_spdif_put
+        .iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
+        .name =         SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
+        .info =         snd_emu10k1_spdif_info,
+        .get =          snd_emu10k1_spdif_get,
+        .put =          snd_emu10k1_spdif_put
 };
 
 
@@ -202,12 +201,12 @@
 
 static snd_kcontrol_new_t snd_emu10k1_send_routing_control =
 {
-	access:		SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
-        iface:          SNDRV_CTL_ELEM_IFACE_MIXER,
-        name:           "EMU10K1 PCM Send Routing",
-        info:           snd_emu10k1_send_routing_info,
-        get:            snd_emu10k1_send_routing_get,
-        put:            snd_emu10k1_send_routing_put
+	.access =	SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
+        .iface =        SNDRV_CTL_ELEM_IFACE_MIXER,
+        .name =         "EMU10K1 PCM Send Routing",
+        .info =         snd_emu10k1_send_routing_info,
+        .get =          snd_emu10k1_send_routing_get,
+        .put =          snd_emu10k1_send_routing_put
 };
 
 static int snd_emu10k1_send_volume_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
@@ -270,12 +269,12 @@
 
 static snd_kcontrol_new_t snd_emu10k1_send_volume_control =
 {
-	access:		SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
-        iface:          SNDRV_CTL_ELEM_IFACE_MIXER,
-        name:           "EMU10K1 PCM Send Volume",
-        info:           snd_emu10k1_send_volume_info,
-        get:            snd_emu10k1_send_volume_get,
-        put:            snd_emu10k1_send_volume_put
+	.access =	SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
+        .iface =        SNDRV_CTL_ELEM_IFACE_MIXER,
+        .name =         "EMU10K1 PCM Send Volume",
+        .info =         snd_emu10k1_send_volume_info,
+        .get =          snd_emu10k1_send_volume_get,
+        .put =          snd_emu10k1_send_volume_put
 };
 
 static int snd_emu10k1_attn_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
@@ -332,12 +331,12 @@
 
 static snd_kcontrol_new_t snd_emu10k1_attn_control =
 {
-	access:		SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
-        iface:          SNDRV_CTL_ELEM_IFACE_MIXER,
-        name:           "EMU10K1 PCM Volume",
-        info:           snd_emu10k1_attn_info,
-        get:            snd_emu10k1_attn_get,
-        put:            snd_emu10k1_attn_put
+	.access =	SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
+        .iface =        SNDRV_CTL_ELEM_IFACE_MIXER,
+        .name =         "EMU10K1 PCM Volume",
+        .info =         snd_emu10k1_attn_info,
+        .get =          snd_emu10k1_attn_get,
+        .put =          snd_emu10k1_attn_put
 };
 
 static int snd_emu10k1_shared_spdif_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
@@ -394,20 +393,20 @@
 
 static snd_kcontrol_new_t snd_emu10k1_shared_spdif __devinitdata =
 {
-	iface:		SNDRV_CTL_ELEM_IFACE_MIXER,
-	name:		"SB Live Analog/Digital Output Jack",
-	info:		snd_emu10k1_shared_spdif_info,
-	get:		snd_emu10k1_shared_spdif_get,
-	put:		snd_emu10k1_shared_spdif_put
+	.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name =		"SB Live Analog/Digital Output Jack",
+	.info =		snd_emu10k1_shared_spdif_info,
+	.get =		snd_emu10k1_shared_spdif_get,
+	.put =		snd_emu10k1_shared_spdif_put
 };
 
 static snd_kcontrol_new_t snd_audigy_shared_spdif __devinitdata =
 {
-	iface:		SNDRV_CTL_ELEM_IFACE_MIXER,
-	name:		"Audigy Analog/Digital Output Jack",
-	info:		snd_emu10k1_shared_spdif_info,
-	get:		snd_emu10k1_shared_spdif_get,
-	put:		snd_emu10k1_shared_spdif_put
+	.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name =		"Audigy Analog/Digital Output Jack",
+	.info =		snd_emu10k1_shared_spdif_info,
+	.get =		snd_emu10k1_shared_spdif_get,
+	.put =		snd_emu10k1_shared_spdif_put
 };
 
 /*
diff -Nru a/sound/pci/emu10k1/emumpu401.c b/sound/pci/emu10k1/emumpu401.c
--- a/sound/pci/emu10k1/emumpu401.c	Mon Oct  7 11:25:25 2002
+++ b/sound/pci/emu10k1/emumpu401.c	Mon Oct  7 11:25:25 2002
@@ -19,7 +19,6 @@
  *
  */
 
-#define __NO_VERSION__
 #include <sound/driver.h>
 #include <linux/time.h>
 #include <linux/init.h>
@@ -288,16 +287,16 @@
 
 static snd_rawmidi_ops_t snd_emu10k1_midi_output =
 {
-	open:		snd_emu10k1_midi_output_open,
-	close:		snd_emu10k1_midi_output_close,
-	trigger:	snd_emu10k1_midi_output_trigger,
+	.open =		snd_emu10k1_midi_output_open,
+	.close =	snd_emu10k1_midi_output_close,
+	.trigger =	snd_emu10k1_midi_output_trigger,
 };
 
 static snd_rawmidi_ops_t snd_emu10k1_midi_input =
 {
-	open:		snd_emu10k1_midi_input_open,
-	close:		snd_emu10k1_midi_input_close,
-	trigger:	snd_emu10k1_midi_input_trigger,
+	.open =		snd_emu10k1_midi_input_open,
+	.close =	snd_emu10k1_midi_input_close,
+	.trigger =	snd_emu10k1_midi_input_trigger,
 };
 
 static void snd_emu10k1_midi_free(snd_rawmidi_t *rmidi)
diff -Nru a/sound/pci/emu10k1/emupcm.c b/sound/pci/emu10k1/emupcm.c
--- a/sound/pci/emu10k1/emupcm.c	Mon Oct  7 11:25:25 2002
+++ b/sound/pci/emu10k1/emupcm.c	Mon Oct  7 11:25:25 2002
@@ -25,8 +25,8 @@
  *
  */
 
-#define __NO_VERSION__
 #include <sound/driver.h>
+#include <linux/delay.h>
 #include <linux/slab.h>
 #include <linux/time.h>
 #include <linux/init.h>
@@ -141,9 +141,9 @@
 };
 
 static snd_pcm_hw_constraint_list_t hw_constraints_capture_period_sizes = {
-	count: 31,
-	list: capture_period_sizes,
-	mask: 0
+	.count = 31,
+	.list = capture_period_sizes,
+	.mask = 0
 };
 
 static unsigned int capture_rates[8] = {
@@ -151,9 +151,9 @@
 };
 
 static snd_pcm_hw_constraint_list_t hw_constraints_capture_rates = {
-	count: 8,
-	list: capture_rates,
-	mask: 0
+	.count = 8,
+	.list = capture_rates,
+	.mask = 0
 };
 
 static unsigned int snd_emu10k1_capture_rate_reg(unsigned int rate)
@@ -625,6 +625,7 @@
 		}
 		snd_emu10k1_ptr_write(emu, epcm->capture_bs_reg, 0, epcm->capture_bs_val);
 		epcm->running = 1;
+		epcm->first_ptr = 1;
 		break;
 	case SNDRV_PCM_TRIGGER_STOP:
 		epcm->running = 0;
@@ -682,6 +683,10 @@
 
 	if (!epcm->running)
 		return 0;
+	if (epcm->first_ptr) {
+		udelay(50);	// hack, it takes awhile until capture is started
+		epcm->first_ptr = 0;
+	}
 	ptr = snd_emu10k1_ptr_read(emu, epcm->capture_idx_reg, 0) & 0x0000ffff;
 	return bytes_to_frames(runtime, ptr);
 }
@@ -692,21 +697,21 @@
 
 static snd_pcm_hardware_t snd_emu10k1_playback =
 {
-	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
 				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
 				 SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_PAUSE),
-	formats:		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
-	rates:			SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
-	rate_min:		4000,
-	rate_max:		48000,
-	channels_min:		1,
-	channels_max:		2,
-	buffer_bytes_max:	(128*1024),
-	period_bytes_min:	64,
-	period_bytes_max:	(128*1024),
-	periods_min:		1,
-	periods_max:		1024,
-	fifo_size:		0,
+	.formats =		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
+	.rates =		SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+	.rate_min =		4000,
+	.rate_max =		48000,
+	.channels_min =		1,
+	.channels_max =		2,
+	.buffer_bytes_max =	(128*1024),
+	.period_bytes_min =	64,
+	.period_bytes_max =	(128*1024),
+	.periods_min =		1,
+	.periods_max =		1024,
+	.fifo_size =		0,
 };
 
 /*
@@ -715,21 +720,21 @@
 
 static snd_pcm_hardware_t snd_emu10k1_capture =
 {
-	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
 				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
 				 SNDRV_PCM_INFO_MMAP_VALID),
-	formats:		SNDRV_PCM_FMTBIT_S16_LE,
-	rates:			SNDRV_PCM_RATE_8000_48000,
-	rate_min:		8000,
-	rate_max:		48000,
-	channels_min:		1,
-	channels_max:		2,
-	buffer_bytes_max:	(64*1024),
-	period_bytes_min:	384,
-	period_bytes_max:	(64*1024),
-	periods_min:		2,
-	periods_max:		2,
-	fifo_size:		0,
+	.formats =		SNDRV_PCM_FMTBIT_S16_LE,
+	.rates =		SNDRV_PCM_RATE_8000_48000,
+	.rate_min =		8000,
+	.rate_max =		48000,
+	.channels_min =		1,
+	.channels_max =		2,
+	.buffer_bytes_max =	(64*1024),
+	.period_bytes_min =	384,
+	.period_bytes_max =	(64*1024),
+	.periods_min =		2,
+	.periods_max =		2,
+	.fifo_size =		0,
 };
 
 /*
@@ -940,28 +945,28 @@
 }
 
 static snd_pcm_ops_t snd_emu10k1_playback_ops = {
-	open:			snd_emu10k1_playback_open,
-	close:			snd_emu10k1_playback_close,
-	ioctl:			snd_pcm_lib_ioctl,
-	hw_params:		snd_emu10k1_playback_hw_params,
-	hw_free:		snd_emu10k1_playback_hw_free,
-	prepare:		snd_emu10k1_playback_prepare,
-	trigger:		snd_emu10k1_playback_trigger,
-	pointer:		snd_emu10k1_playback_pointer,
-	copy:			snd_pcm_sgbuf_ops_copy_playback,
-	silence:		snd_pcm_sgbuf_ops_silence,
-	page:			snd_pcm_sgbuf_ops_page,
+	.open =			snd_emu10k1_playback_open,
+	.close =		snd_emu10k1_playback_close,
+	.ioctl =		snd_pcm_lib_ioctl,
+	.hw_params =		snd_emu10k1_playback_hw_params,
+	.hw_free =		snd_emu10k1_playback_hw_free,
+	.prepare =		snd_emu10k1_playback_prepare,
+	.trigger =		snd_emu10k1_playback_trigger,
+	.pointer =		snd_emu10k1_playback_pointer,
+	.copy =			snd_pcm_sgbuf_ops_copy_playback,
+	.silence =		snd_pcm_sgbuf_ops_silence,
+	.page =			snd_pcm_sgbuf_ops_page,
 };
 
 static snd_pcm_ops_t snd_emu10k1_capture_ops = {
-	open:			snd_emu10k1_capture_open,
-	close:			snd_emu10k1_capture_close,
-	ioctl:			snd_pcm_lib_ioctl,
-	hw_params:		snd_emu10k1_capture_hw_params,
-	hw_free:		snd_emu10k1_capture_hw_free,
-	prepare:		snd_emu10k1_capture_prepare,
-	trigger:		snd_emu10k1_capture_trigger,
-	pointer:		snd_emu10k1_capture_pointer,
+	.open =			snd_emu10k1_capture_open,
+	.close =		snd_emu10k1_capture_close,
+	.ioctl =		snd_pcm_lib_ioctl,
+	.hw_params =		snd_emu10k1_capture_hw_params,
+	.hw_free =		snd_emu10k1_capture_hw_free,
+	.prepare =		snd_emu10k1_capture_prepare,
+	.trigger =		snd_emu10k1_capture_trigger,
+	.pointer =		snd_emu10k1_capture_pointer,
 };
 
 static void snd_emu10k1_pcm_free(snd_pcm_t *pcm)
@@ -1005,14 +1010,14 @@
 }
 
 static snd_pcm_ops_t snd_emu10k1_capture_mic_ops = {
-	open:			snd_emu10k1_capture_mic_open,
-	close:			snd_emu10k1_capture_mic_close,
-	ioctl:			snd_pcm_lib_ioctl,
-	hw_params:		snd_emu10k1_capture_hw_params,
-	hw_free:		snd_emu10k1_capture_hw_free,
-	prepare:		snd_emu10k1_capture_prepare,
-	trigger:		snd_emu10k1_capture_trigger,
-	pointer:		snd_emu10k1_capture_pointer,
+	.open =			snd_emu10k1_capture_mic_open,
+	.close =		snd_emu10k1_capture_mic_close,
+	.ioctl =		snd_pcm_lib_ioctl,
+	.hw_params =		snd_emu10k1_capture_hw_params,
+	.hw_free =		snd_emu10k1_capture_hw_free,
+	.prepare =		snd_emu10k1_capture_prepare,
+	.trigger =		snd_emu10k1_capture_trigger,
+	.pointer =		snd_emu10k1_capture_pointer,
 };
 
 static void snd_emu10k1_pcm_mic_free(snd_pcm_t *pcm)
@@ -1100,22 +1105,22 @@
 }
 
 static snd_kcontrol_new_t snd_emu10k1_pcm_efx_voices_mask = {
-	iface: SNDRV_CTL_ELEM_IFACE_PCM,
-	name: "EFX voices mask",
-	info: snd_emu10k1_pcm_efx_voices_mask_info,
-	get: snd_emu10k1_pcm_efx_voices_mask_get,
-	put: snd_emu10k1_pcm_efx_voices_mask_put
+	.iface = SNDRV_CTL_ELEM_IFACE_PCM,
+	.name = "EFX voices mask",
+	.info = snd_emu10k1_pcm_efx_voices_mask_info,
+	.get = snd_emu10k1_pcm_efx_voices_mask_get,
+	.put = snd_emu10k1_pcm_efx_voices_mask_put
 };
 
 static snd_pcm_ops_t snd_emu10k1_capture_efx_ops = {
-	open:			snd_emu10k1_capture_efx_open,
-	close:			snd_emu10k1_capture_efx_close,
-	ioctl:			snd_pcm_lib_ioctl,
-	hw_params:		snd_emu10k1_capture_hw_params,
-	hw_free:		snd_emu10k1_capture_hw_free,
-	prepare:		snd_emu10k1_capture_prepare,
-	trigger:		snd_emu10k1_capture_trigger,
-	pointer:		snd_emu10k1_capture_pointer,
+	.open =			snd_emu10k1_capture_efx_open,
+	.close =		snd_emu10k1_capture_efx_close,
+	.ioctl =		snd_pcm_lib_ioctl,
+	.hw_params =		snd_emu10k1_capture_hw_params,
+	.hw_free =		snd_emu10k1_capture_hw_free,
+	.prepare =		snd_emu10k1_capture_prepare,
+	.trigger =		snd_emu10k1_capture_trigger,
+	.pointer =		snd_emu10k1_capture_pointer,
 };
 
 static void snd_emu10k1_pcm_efx_free(snd_pcm_t *pcm)
diff -Nru a/sound/pci/emu10k1/emuproc.c b/sound/pci/emu10k1/emuproc.c
--- a/sound/pci/emu10k1/emuproc.c	Mon Oct  7 11:25:31 2002
+++ b/sound/pci/emu10k1/emuproc.c	Mon Oct  7 11:25:31 2002
@@ -25,7 +25,6 @@
  *
  */
 
-#define __NO_VERSION__
 #include <sound/driver.h>
 #include <linux/slab.h>
 #include <linux/init.h>
diff -Nru a/sound/pci/emu10k1/io.c b/sound/pci/emu10k1/io.c
--- a/sound/pci/emu10k1/io.c	Mon Oct  7 11:25:29 2002
+++ b/sound/pci/emu10k1/io.c	Mon Oct  7 11:25:29 2002
@@ -25,7 +25,6 @@
  *
  */
 
-#define __NO_VERSION__
 #include <sound/driver.h>
 #include <linux/time.h>
 #include <sound/core.h>
diff -Nru a/sound/pci/emu10k1/irq.c b/sound/pci/emu10k1/irq.c
--- a/sound/pci/emu10k1/irq.c	Mon Oct  7 11:25:26 2002
+++ b/sound/pci/emu10k1/irq.c	Mon Oct  7 11:25:26 2002
@@ -25,7 +25,6 @@
  *
  */
 
-#define __NO_VERSION__
 #include <sound/driver.h>
 #include <linux/time.h>
 #include <sound/core.h>
diff -Nru a/sound/pci/emu10k1/memory.c b/sound/pci/emu10k1/memory.c
--- a/sound/pci/emu10k1/memory.c	Mon Oct  7 11:25:38 2002
+++ b/sound/pci/emu10k1/memory.c	Mon Oct  7 11:25:38 2002
@@ -21,7 +21,6 @@
  *
  */
 
-#define __NO_VERSION__
 #include <sound/driver.h>
 #include <linux/time.h>
 #include <sound/core.h>
diff -Nru a/sound/pci/emu10k1/voice.c b/sound/pci/emu10k1/voice.c
--- a/sound/pci/emu10k1/voice.c	Mon Oct  7 11:25:35 2002
+++ b/sound/pci/emu10k1/voice.c	Mon Oct  7 11:25:35 2002
@@ -25,7 +25,6 @@
  *
  */
 
-#define __NO_VERSION__
 #include <sound/driver.h>
 #include <linux/time.h>
 #include <sound/core.h>
diff -Nru a/sound/pci/ens1370.c b/sound/pci/ens1370.c
--- a/sound/pci/ens1370.c	Mon Oct  7 11:25:28 2002
+++ b/sound/pci/ens1370.c	Mon Oct  7 11:25:28 2002
@@ -414,40 +414,40 @@
 static unsigned int snd_es1370_fixed_rates[] =
 	{5512, 11025, 22050, 44100};
 static snd_pcm_hw_constraint_list_t snd_es1370_hw_constraints_rates = {
-	count: 4, 
-	list: snd_es1370_fixed_rates,
-	mask: 0,
+	.count = 4, 
+	.list = snd_es1370_fixed_rates,
+	.mask = 0,
 };
 static ratnum_t es1370_clock = {
-	num: ES_1370_SRCLOCK,
-	den_min: 29, 
-	den_max: 353,
-	den_step: 1,
+	.num = ES_1370_SRCLOCK,
+	.den_min = 29, 
+	.den_max = 353,
+	.den_step = 1,
 };
 static snd_pcm_hw_constraint_ratnums_t snd_es1370_hw_constraints_clock = {
-	nrats: 1,
-	rats: &es1370_clock,
+	.nrats = 1,
+	.rats = &es1370_clock,
 };
 #else
 static ratden_t es1371_dac_clock = {
-	num_min: 3000 * (1 << 15),
-	num_max: 48000 * (1 << 15),
-	num_step: 3000,
-	den: 1 << 15,
+	.num_min = 3000 * (1 << 15),
+	.num_max = 48000 * (1 << 15),
+	.num_step = 3000,
+	.den = 1 << 15,
 };
 static snd_pcm_hw_constraint_ratdens_t snd_es1371_hw_constraints_dac_clock = {
-	nrats: 1,
-	rats: &es1371_dac_clock,
+	.nrats = 1,
+	.rats = &es1371_dac_clock,
 };
 static ratnum_t es1371_adc_clock = {
-	num: 48000 << 15,
-	den_min: 32768, 
-	den_max: 393216,
-	den_step: 1,
+	.num = 48000 << 15,
+	.den_min = 32768, 
+	.den_max = 393216,
+	.den_step = 1,
 };
 static snd_pcm_hw_constraint_ratnums_t snd_es1371_hw_constraints_adc_clock = {
-	nrats: 1,
-	rats: &es1371_adc_clock,
+	.nrats = 1,
+	.rats = &es1371_adc_clock,
 };
 #endif
 static const unsigned int snd_ensoniq_sample_shift[] =
@@ -953,12 +953,12 @@
 
 static snd_pcm_hardware_t snd_ensoniq_playback1 =
 {
-	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
 				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
 				 SNDRV_PCM_INFO_MMAP_VALID |
 				 SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_SYNC_START),
-	formats:		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
-	rates:
+	.formats =		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
+	.rates =
 #ifndef CHIP1370
 				SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
 #else
@@ -966,55 +966,55 @@
 				 SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_22050 | 
 				 SNDRV_PCM_RATE_44100),
 #endif
-	rate_min:		4000,
-	rate_max:		48000,
-	channels_min:		1,
-	channels_max:		2,
-	buffer_bytes_max:	(128*1024),
-	period_bytes_min:	64,
-	period_bytes_max:	(128*1024),
-	periods_min:		1,
-	periods_max:		1024,
-	fifo_size:		0,
+	.rate_min =		4000,
+	.rate_max =		48000,
+	.channels_min =		1,
+	.channels_max =		2,
+	.buffer_bytes_max =	(128*1024),
+	.period_bytes_min =	64,
+	.period_bytes_max =	(128*1024),
+	.periods_min =		1,
+	.periods_max =		1024,
+	.fifo_size =		0,
 };
 
 static snd_pcm_hardware_t snd_ensoniq_playback2 =
 {
-	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
 				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
 				 SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_PAUSE | 
 				 SNDRV_PCM_INFO_SYNC_START),
-	formats:		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
-	rates:			SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
-	rate_min:		4000,
-	rate_max:		48000,
-	channels_min:		1,
-	channels_max:		2,
-	buffer_bytes_max:	(128*1024),
-	period_bytes_min:	64,
-	period_bytes_max:	(128*1024),
-	periods_min:		1,
-	periods_max:		1024,
-	fifo_size:		0,
+	.formats =		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
+	.rates =		SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+	.rate_min =		4000,
+	.rate_max =		48000,
+	.channels_min =		1,
+	.channels_max =		2,
+	.buffer_bytes_max =	(128*1024),
+	.period_bytes_min =	64,
+	.period_bytes_max =	(128*1024),
+	.periods_min =		1,
+	.periods_max =		1024,
+	.fifo_size =		0,
 };
 
 static snd_pcm_hardware_t snd_ensoniq_capture =
 {
-	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
 				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
 				 SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START),
-	formats:		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
-	rates:			SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
-	rate_min:		4000,
-	rate_max:		48000,
-	channels_min:		1,
-	channels_max:		2,
-	buffer_bytes_max:	(128*1024),
-	period_bytes_min:	64,
-	period_bytes_max:	(128*1024),
-	periods_min:		1,
-	periods_max:		1024,
-	fifo_size:		0,
+	.formats =		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
+	.rates =		SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+	.rate_min =		4000,
+	.rate_max =		48000,
+	.channels_min =		1,
+	.channels_max =		2,
+	.buffer_bytes_max =	(128*1024),
+	.period_bytes_min =	64,
+	.period_bytes_max =	(128*1024),
+	.periods_min =		1,
+	.periods_max =		1024,
+	.fifo_size =		0,
 };
 
 static int snd_ensoniq_playback1_open(snd_pcm_substream_t * substream)
@@ -1114,36 +1114,36 @@
 }
 
 static snd_pcm_ops_t snd_ensoniq_playback1_ops = {
-	open:		snd_ensoniq_playback1_open,
-	close:		snd_ensoniq_playback1_close,
-	ioctl:		snd_pcm_lib_ioctl,
-	hw_params:	snd_ensoniq_hw_params,
-	hw_free:	snd_ensoniq_hw_free,
-	prepare:	snd_ensoniq_playback1_prepare,
-	trigger:	snd_ensoniq_trigger,
-	pointer:	snd_ensoniq_playback1_pointer,
+	.open =		snd_ensoniq_playback1_open,
+	.close =	snd_ensoniq_playback1_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	snd_ensoniq_hw_params,
+	.hw_free =	snd_ensoniq_hw_free,
+	.prepare =	snd_ensoniq_playback1_prepare,
+	.trigger =	snd_ensoniq_trigger,
+	.pointer =	snd_ensoniq_playback1_pointer,
 };
 
 static snd_pcm_ops_t snd_ensoniq_playback2_ops = {
-	open:		snd_ensoniq_playback2_open,
-	close:		snd_ensoniq_playback2_close,
-	ioctl:		snd_pcm_lib_ioctl,
-	hw_params:	snd_ensoniq_hw_params,
-	hw_free:	snd_ensoniq_hw_free,
-	prepare:	snd_ensoniq_playback2_prepare,
-	trigger:	snd_ensoniq_trigger,
-	pointer:	snd_ensoniq_playback2_pointer,
+	.open =		snd_ensoniq_playback2_open,
+	.close =	snd_ensoniq_playback2_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	snd_ensoniq_hw_params,
+	.hw_free =	snd_ensoniq_hw_free,
+	.prepare =	snd_ensoniq_playback2_prepare,
+	.trigger =	snd_ensoniq_trigger,
+	.pointer =	snd_ensoniq_playback2_pointer,
 };
 
 static snd_pcm_ops_t snd_ensoniq_capture_ops = {
-	open:		snd_ensoniq_capture_open,
-	close:		snd_ensoniq_capture_close,
-	ioctl:		snd_pcm_lib_ioctl,
-	hw_params:	snd_ensoniq_hw_params,
-	hw_free:	snd_ensoniq_hw_free,
-	prepare:	snd_ensoniq_capture_prepare,
-	trigger:	snd_ensoniq_trigger,
-	pointer:	snd_ensoniq_capture_pointer,
+	.open =		snd_ensoniq_capture_open,
+	.close =	snd_ensoniq_capture_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	snd_ensoniq_hw_params,
+	.hw_free =	snd_ensoniq_hw_free,
+	.prepare =	snd_ensoniq_capture_prepare,
+	.trigger =	snd_ensoniq_trigger,
+	.pointer =	snd_ensoniq_capture_pointer,
 };
 
 static void snd_ensoniq_pcm_free(snd_pcm_t *pcm)
@@ -1236,8 +1236,8 @@
 #ifdef CHIP1371
 
 #define ES1371_SPDIF(xname) \
-{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, info: snd_es1371_spdif_info, \
-  get: snd_es1371_spdif_get, put: snd_es1371_spdif_put }
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .info = snd_es1371_spdif_info, \
+  .get = snd_es1371_spdif_get, .put = snd_es1371_spdif_put }
 
 static int snd_es1371_spdif_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
 {
@@ -1294,11 +1294,12 @@
 	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 }
+	{ .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_VENDOR_ID_ENSONIQ, .did = PCI_DEVICE_ID_ENSONIQ_ES1371, .rev = ES1371REV_ES1373_8 },
+	{ .vid = PCI_ANY_ID, .did = PCI_ANY_ID }
 };
 
 static int snd_ensoniq_1371_mixer(ensoniq_t * ensoniq)
@@ -1318,7 +1319,10 @@
 		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));
+		    	snd_kcontrol_t *kctl = snd_ctl_new1(&snd_es1371_mixer_spdif, ensoniq); 
+		    	if (ensoniq->u.es1371.ac97->ext_id & AC97_EI_SPDIF)
+			    	kctl->id.index = 1;
+			snd_ctl_add(card, kctl);
 			break;
 		}
 	return 0;
@@ -1327,9 +1331,9 @@
 #endif /* CHIP1371 */
 
 #define ENSONIQ_CONTROL(xname, mask) \
-{ iface: SNDRV_CTL_ELEM_IFACE_CARD, name: xname, info: snd_ensoniq_control_info, \
-  get: snd_ensoniq_control_get, put: snd_ensoniq_control_put, \
-  private_value: mask }
+{ .iface = SNDRV_CTL_ELEM_IFACE_CARD, .name = xname, .info = snd_ensoniq_control_info, \
+  .get = snd_ensoniq_control_get, .put = snd_ensoniq_control_put, \
+  .private_value = mask }
 
 static int snd_ensoniq_control_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
 {
@@ -1420,8 +1424,8 @@
 #ifdef CHIP1371
 
 #define ES1371_JOYSTICK_ADDR(xname) \
-{ iface: SNDRV_CTL_ELEM_IFACE_CARD, name: xname, info: snd_es1371_joystick_addr_info, \
-  get: snd_es1371_joystick_addr_get, put: snd_es1371_joystick_addr_put }
+{ .iface = SNDRV_CTL_ELEM_IFACE_CARD, .name = xname, .info = snd_es1371_joystick_addr_info, \
+  .get = snd_es1371_joystick_addr_get, .put = snd_es1371_joystick_addr_put }
 
 static int snd_es1371_joystick_addr_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
 {
@@ -1560,23 +1564,23 @@
 	unsigned short svid;		/* subsystem vendor ID */
 	unsigned short sdid;		/* subsystem device ID */
 } es1371_amplifier_hack[] = {
-	{ svid: 0x107b, sdid: 0x2150 },	/* Gateway Solo 2150 */
-	{ svid: 0x13bd, sdid: 0x100c },	/* EV1938 on Mebius PC-MJ100V */
-	{ svid: 0x1102, sdid: 0x5938 },	/* Targa Xtender300 */
-	{ svid: 0x1102, sdid: 0x8938 }, /* IPC Topnote G notebook */
-	{ svid: PCI_ANY_ID, sdid: PCI_ANY_ID }
+	{ .svid = 0x107b, .sdid = 0x2150 },	/* Gateway Solo 2150 */
+	{ .svid = 0x13bd, .sdid = 0x100c },	/* EV1938 on Mebius PC-MJ100V */
+	{ .svid = 0x1102, .sdid = 0x5938 },	/* Targa Xtender300 */
+	{ .svid = 0x1102, .sdid = 0x8938 },	/* IPC Topnote G notebook */
+	{ .svid = PCI_ANY_ID, .sdid = PCI_ANY_ID }
 };
 static struct {
 	unsigned short vid;		/* vendor ID */
 	unsigned short did;		/* device ID */
 	unsigned char rev;		/* revision */
 } es1371_ac97_reset_hack[] = {
-	{ 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_VENDOR_ID_ENSONIQ, did: PCI_DEVICE_ID_ENSONIQ_ES1371, rev: ES1371REV_ES1373_8 },
-	{ vid: PCI_ANY_ID, did: PCI_ANY_ID, rev: 0 }
+	{ .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_VENDOR_ID_ENSONIQ, .did = PCI_DEVICE_ID_ENSONIQ_ES1371, .rev = ES1371REV_ES1373_8 },
+	{ .vid = PCI_ANY_ID, .did = PCI_ANY_ID }
 };
 #endif
 
@@ -1592,7 +1596,7 @@
 #endif
 	int err;
 	static snd_device_ops_t ops = {
-		dev_free:	snd_ensoniq_dev_free,
+		.dev_free =	snd_ensoniq_dev_free,
 	};
 
 	*rensoniq = NULL;
@@ -1900,16 +1904,16 @@
 
 static snd_rawmidi_ops_t snd_ensoniq_midi_output =
 {
-	open:           snd_ensoniq_midi_output_open,
-	close:          snd_ensoniq_midi_output_close,
-	trigger:        snd_ensoniq_midi_output_trigger,
+	.open =		snd_ensoniq_midi_output_open,
+	.close =	snd_ensoniq_midi_output_close,
+	.trigger =	snd_ensoniq_midi_output_trigger,
 };
 
 static snd_rawmidi_ops_t snd_ensoniq_midi_input =
 {
-	open:           snd_ensoniq_midi_input_open,
-	close:          snd_ensoniq_midi_input_close,
-	trigger:        snd_ensoniq_midi_input_trigger,
+	.open =		snd_ensoniq_midi_input_open,
+	.close =	snd_ensoniq_midi_input_close,
+	.trigger =	snd_ensoniq_midi_input_trigger,
 };
 
 static int __devinit snd_ensoniq_midi(ensoniq_t * ensoniq, int device, snd_rawmidi_t **rrawmidi)
@@ -2053,10 +2057,10 @@
 }
 
 static struct pci_driver driver = {
-	name: "Ensoniq AudioPCI",
-	id_table: snd_audiopci_ids,
-	probe: snd_audiopci_probe,
-	remove: __devexit_p(snd_audiopci_remove),
+	.name = "Ensoniq AudioPCI",
+	.id_table = snd_audiopci_ids,
+	.probe = snd_audiopci_probe,
+	.remove = __devexit_p(snd_audiopci_remove),
 };
 	
 static int __init alsa_card_ens137x_init(void)
diff -Nru a/sound/pci/es1938.c b/sound/pci/es1938.c
--- a/sound/pci/es1938.c	Mon Oct  7 11:25:36 2002
+++ b/sound/pci/es1938.c	Mon Oct  7 11:25:36 2002
@@ -1,5 +1,5 @@
 /*
- *  Driver for ESS Solo-1 (ES1938, ES1946) soundcard
+ *  Driver for ESS Solo-1 (ES1938, ES1946, ES1969) soundcard
  *  Copyright (c) by Jaromir Koutek <miri@punknet.cz>,
  *                   Jaroslav Kysela <perex@suse.cz>,
  *                   Thomas Sailer <sailer@ife.ee.ethz.ch>,
@@ -70,6 +70,8 @@
 MODULE_LICENSE("GPL");
 MODULE_CLASSES("{sound}");
 MODULE_DEVICES("{{ESS,ES1938},"
+                "{ESS,ES1946},"
+                "{ESS,ES1969},"
 		"{TerraTec,128i PCI}}");
 
 #ifndef PCI_VENDOR_ID_ESS
@@ -454,22 +456,22 @@
 
 static ratnum_t clocks[2] = {
 	{
-		num: 793800,
-		den_min: 1,
-		den_max: 128,
-		den_step: 1,
+		.num = 793800,
+		.den_min = 1,
+		.den_max = 128,
+		.den_step = 1,
 	},
 	{
-		num: 768000,
-		den_min: 1,
-		den_max: 128,
-		den_step: 1,
+		.num = 768000,
+		.den_min = 1,
+		.den_max = 128,
+		.den_step = 1,
 	}
 };
 
 static snd_pcm_hw_constraint_ratnums_t hw_constraints_clocks = {
-	nrats: 2,
-	rats: clocks,
+	.nrats = 2,
+	.rats = clocks,
 };
 
 
@@ -843,20 +845,20 @@
  * ----------------------------------------------------------------------*/
 static snd_pcm_hardware_t snd_es1938_capture =
 {
-	info:			(SNDRV_PCM_INFO_INTERLEAVED |
-				 SNDRV_PCM_INFO_BLOCK_TRANSFER),
-	formats:		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U16_LE,
-	rates:			SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
-	rate_min:		6000,
-	rate_max:		48000,
-	channels_min:		1,
-	channels_max:		2,
-	buffer_bytes_max:	65536,
-	period_bytes_min:	64,
-	period_bytes_max:	65536,
-	periods_min:		1,
-	periods_max:		1024,
-	fifo_size:		256,
+	.info =			(SNDRV_PCM_INFO_INTERLEAVED |
+				SNDRV_PCM_INFO_BLOCK_TRANSFER),
+	.formats =		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U16_LE,
+	.rates =		SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+	.rate_min =		6000,
+	.rate_max =		48000,
+	.channels_min =		1,
+	.channels_max =		2,
+	.buffer_bytes_max =	65536,
+	.period_bytes_min =	64,
+	.period_bytes_max =	65536,
+	.periods_min =		1,
+	.periods_max =		1024,
+	.fifo_size =		256,
 };
 
 /* -----------------------------------------------------------------------
@@ -864,21 +866,21 @@
  * -----------------------------------------------------------------------*/
 static snd_pcm_hardware_t snd_es1938_playback =
 {
-	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
 				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
 				 SNDRV_PCM_INFO_MMAP_VALID),
-	formats:		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U16_LE,
-	rates:			SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
-	rate_min:		6000,
-	rate_max:		48000,
-	channels_min:		1,
-	channels_max:		2,
-	buffer_bytes_max:	65536,
-	period_bytes_min:	64,
-	period_bytes_max:	65536,
-	periods_min:		1,
-	periods_max:		1024,
-	fifo_size:		256,
+	.formats =		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U16_LE,
+	.rates =		SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+	.rate_min =		6000,
+	.rate_max =		48000,
+	.channels_min =		1,
+	.channels_max =		2,
+	.buffer_bytes_max =	65536,
+	.period_bytes_min =	64,
+	.period_bytes_max =	65536,
+	.periods_min =		1,
+	.periods_max =		1024,
+	.fifo_size =		256,
 };
 
 static int snd_es1938_capture_open(snd_pcm_substream_t * substream)
@@ -958,22 +960,22 @@
 }
 
 static snd_pcm_ops_t snd_es1938_playback_ops = {
-	open:		snd_es1938_playback_open,
-	close:		snd_es1938_playback_close,
-	ioctl:		snd_pcm_lib_ioctl,
-	prepare:	snd_es1938_playback_prepare,
-	trigger:	snd_es1938_playback_trigger,
-	pointer:	snd_es1938_playback_pointer,
+	.open =		snd_es1938_playback_open,
+	.close =	snd_es1938_playback_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.prepare =	snd_es1938_playback_prepare,
+	.trigger =	snd_es1938_playback_trigger,
+	.pointer =	snd_es1938_playback_pointer,
 };
 
 static snd_pcm_ops_t snd_es1938_capture_ops = {
-	open:		snd_es1938_capture_open,
-	close:		snd_es1938_capture_close,
-	ioctl:		snd_pcm_lib_ioctl,
-	prepare:	snd_es1938_capture_prepare,
-	trigger:	snd_es1938_capture_trigger,
-	pointer:	snd_es1938_capture_pointer,
-	copy:		snd_es1938_capture_copy,
+	.open =		snd_es1938_capture_open,
+	.close =	snd_es1938_capture_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.prepare =	snd_es1938_capture_prepare,
+	.trigger =	snd_es1938_capture_trigger,
+	.pointer =	snd_es1938_capture_pointer,
+	.copy =		snd_es1938_capture_copy,
 };
 
 static void snd_es1938_free_pcm(snd_pcm_t *pcm)
@@ -1136,10 +1138,10 @@
 }
 
 #define ES1938_SINGLE(xname, xindex, reg, shift, mask, invert) \
-{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \
-  info: snd_es1938_info_single, \
-  get: snd_es1938_get_single, put: snd_es1938_put_single, \
-  private_value: reg | (shift << 8) | (mask << 16) | (invert << 24) }
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
+  .info = snd_es1938_info_single, \
+  .get = snd_es1938_get_single, .put = snd_es1938_put_single, \
+  .private_value = reg | (shift << 8) | (mask << 16) | (invert << 24) }
 
 static int snd_es1938_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
 {
@@ -1186,10 +1188,10 @@
 }
 
 #define ES1938_DOUBLE(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \
-{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \
-  info: snd_es1938_info_double, \
-  get: snd_es1938_get_double, put: snd_es1938_put_double, \
-  private_value: left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) }
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
+  .info = snd_es1938_info_double, \
+  .get = snd_es1938_get_double, .put = snd_es1938_put_double, \
+  .private_value = left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) }
 
 static int snd_es1938_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
 {
@@ -1266,18 +1268,18 @@
 ES1938_DOUBLE("Master Playback Volume", 0, 0x60, 0x62, 0, 0, 63, 0),
 ES1938_DOUBLE("Master Playback Switch", 0, 0x60, 0x62, 6, 6, 1, 1),
 {
-	iface: SNDRV_CTL_ELEM_IFACE_MIXER,
-	name: "Hardware Master Playback Volume",
-	access: SNDRV_CTL_ELEM_ACCESS_READ,
-	info: snd_es1938_info_hw_volume,
-	get: snd_es1938_get_hw_volume,
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "Hardware Master Playback Volume",
+	.access = SNDRV_CTL_ELEM_ACCESS_READ,
+	.info = snd_es1938_info_hw_volume,
+	.get = snd_es1938_get_hw_volume,
 },
 {
-	iface: SNDRV_CTL_ELEM_IFACE_MIXER,
-	name: "Hardware Master Playback Switch",
-	access: SNDRV_CTL_ELEM_ACCESS_READ,
-	info: snd_es1938_info_hw_switch,
-	get: snd_es1938_get_hw_switch,
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "Hardware Master Playback Switch",
+	.access = SNDRV_CTL_ELEM_ACCESS_READ,
+	.info = snd_es1938_info_hw_switch,
+	.get = snd_es1938_get_hw_switch,
 },
 ES1938_SINGLE("Hardware Volume Split", 0, 0x64, 7, 1, 0),
 ES1938_DOUBLE("Line Playback Volume", 0, 0x3e, 0x3e, 4, 0, 15, 0),
@@ -1291,11 +1293,11 @@
 ES1938_SINGLE("Record Monitor", 0, 0xa8, 3, 1, 0),
 ES1938_SINGLE("Capture Switch", 0, 0x1c, 4, 1, 1),
 {
-	iface: SNDRV_CTL_ELEM_IFACE_MIXER,
-	name: "Capture Source",
-	info: snd_es1938_info_mux,
-	get: snd_es1938_get_mux,
-	put: snd_es1938_put_mux,
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "Capture Source",
+	.info = snd_es1938_info_mux,
+	.get = snd_es1938_get_mux,
+	.put = snd_es1938_put_mux,
 },
 ES1938_DOUBLE("Mono Input Playback Volume", 0, 0x6d, 0x6d, 4, 0, 15, 0),
 ES1938_DOUBLE("PCM Capture Volume", 0, 0x69, 0x69, 4, 0, 15, 0),
@@ -1309,11 +1311,11 @@
 ES1938_DOUBLE("PCM Playback Volume", 1, 0x14, 0x14, 4, 0, 15, 0),
 ES1938_SINGLE("3D Control - Level", 0, 0x52, 0, 63, 0),
 {
-	iface: SNDRV_CTL_ELEM_IFACE_MIXER,
-	name: "3D Control - Switch",
-	info: snd_es1938_info_spatializer_enable,
-	get: snd_es1938_get_spatializer_enable,
-	put: snd_es1938_put_spatializer_enable,
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "3D Control - Switch",
+	.info = snd_es1938_info_spatializer_enable,
+	.get = snd_es1938_get_spatializer_enable,
+	.put = snd_es1938_put_spatializer_enable,
 },
 ES1938_SINGLE("Mic Boost (+26dB)", 0, 0x7d, 3, 1, 0)
 };
@@ -1369,7 +1371,7 @@
 	es1938_t *chip;
 	int err;
 	static snd_device_ops_t ops = {
-		dev_free:	snd_es1938_dev_free,
+		.dev_free =	snd_es1938_dev_free,
 	};
 
 	*rchip = NULL;
@@ -1670,10 +1672,10 @@
 }
 
 static struct pci_driver driver = {
-	name: "ESS ES1938 (Solo-1)",
-	id_table: snd_es1938_ids,
-	probe: snd_es1938_probe,
-	remove: __devexit_p(snd_es1938_remove),
+	.name = "ESS ES1938 (Solo-1)",
+	.id_table = snd_es1938_ids,
+	.probe = snd_es1938_probe,
+	.remove = __devexit_p(snd_es1938_remove),
 };
 
 static int __init alsa_card_es1938_init(void)
diff -Nru a/sound/pci/es1968.c b/sound/pci/es1968.c
--- a/sound/pci/es1968.c	Mon Oct  7 11:25:36 2002
+++ b/sound/pci/es1968.c	Mon Oct  7 11:25:36 2002
@@ -1363,45 +1363,45 @@
 }
 
 static snd_pcm_hardware_t snd_es1968_playback = {
-	info:			(SNDRV_PCM_INFO_MMAP |
+	.info =			(SNDRV_PCM_INFO_MMAP |
                		         SNDRV_PCM_INFO_MMAP_VALID |
 				 SNDRV_PCM_INFO_INTERLEAVED |
 				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
 				 /*SNDRV_PCM_INFO_PAUSE |*/
 				 SNDRV_PCM_INFO_RESUME),
-	formats:		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
-	rates:			SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
-	rate_min:		4000,
-	rate_max:		48000,
-	channels_min:		1,
-	channels_max:		2,
-	buffer_bytes_max:	65536,
-	period_bytes_min:	256,
-	period_bytes_max:	65536,
-	periods_min:		1,
-	periods_max:		1024,
-	fifo_size:		0,
+	.formats =		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
+	.rates =		SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+	.rate_min =		4000,
+	.rate_max =		48000,
+	.channels_min =		1,
+	.channels_max =		2,
+	.buffer_bytes_max =	65536,
+	.period_bytes_min =	256,
+	.period_bytes_max =	65536,
+	.periods_min =		1,
+	.periods_max =		1024,
+	.fifo_size =		0,
 };
 
 static snd_pcm_hardware_t snd_es1968_capture = {
-	info:			(SNDRV_PCM_INFO_NONINTERLEAVED |
+	.info =			(SNDRV_PCM_INFO_NONINTERLEAVED |
 				 SNDRV_PCM_INFO_MMAP |
 				 SNDRV_PCM_INFO_MMAP_VALID |
 				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
 				 /*SNDRV_PCM_INFO_PAUSE |*/
 				 SNDRV_PCM_INFO_RESUME),
-	formats:		/*SNDRV_PCM_FMTBIT_U8 |*/ SNDRV_PCM_FMTBIT_S16_LE,
-	rates:			SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
-	rate_min:		4000,
-	rate_max:		48000,
-	channels_min:		1,
-	channels_max:		2,
-	buffer_bytes_max:	65536,
-	period_bytes_min:	256,
-	period_bytes_max:	65536,
-	periods_min:		1,
-	periods_max:		1024,
-	fifo_size:		0,
+	.formats =		/*SNDRV_PCM_FMTBIT_U8 |*/ SNDRV_PCM_FMTBIT_S16_LE,
+	.rates =		SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+	.rate_min =		4000,
+	.rate_max =		48000,
+	.channels_min =		1,
+	.channels_max =		2,
+	.buffer_bytes_max =	65536,
+	.period_bytes_min =	256,
+	.period_bytes_max =	65536,
+	.periods_min =		1,
+	.periods_max =		1024,
+	.fifo_size =		0,
 };
 
 /* *************************
@@ -1752,26 +1752,26 @@
 }
 
 static snd_pcm_ops_t snd_es1968_playback_ops = {
-	open:		snd_es1968_playback_open,
-	close:		snd_es1968_playback_close,
-	ioctl:		snd_pcm_lib_ioctl,
-	hw_params:	snd_es1968_hw_params,
-	hw_free:	snd_es1968_hw_free,
-	prepare:	snd_es1968_pcm_prepare,
-	trigger:	snd_es1968_pcm_trigger,
-	pointer:	snd_es1968_pcm_pointer,
+	.open =		snd_es1968_playback_open,
+	.close =	snd_es1968_playback_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	snd_es1968_hw_params,
+	.hw_free =	snd_es1968_hw_free,
+	.prepare =	snd_es1968_pcm_prepare,
+	.trigger =	snd_es1968_pcm_trigger,
+	.pointer =	snd_es1968_pcm_pointer,
 };
 
 static snd_pcm_ops_t snd_es1968_capture_ops = {
-	open:		snd_es1968_capture_open,
-	close:		snd_es1968_capture_close,
-	ioctl:		snd_pcm_lib_ioctl,
-	hw_params:	snd_es1968_hw_params,
-	hw_free:	snd_es1968_hw_free,
-	prepare:	snd_es1968_pcm_prepare,
-	trigger:	snd_es1968_pcm_trigger,
-	pointer:	snd_es1968_pcm_pointer,
-	copy:		snd_es1968_capture_copy,
+	.open =		snd_es1968_capture_open,
+	.close =	snd_es1968_capture_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	snd_es1968_hw_params,
+	.hw_free =	snd_es1968_hw_free,
+	.prepare =	snd_es1968_pcm_prepare,
+	.trigger =	snd_es1968_pcm_trigger,
+	.pointer =	snd_es1968_pcm_pointer,
+	.copy =		snd_es1968_capture_copy,
 };
 
 
@@ -2501,11 +2501,13 @@
 
 static int snd_es1968_free(es1968_t *chip)
 {
+	if (chip->res_io_port)
+		snd_es1968_reset(chip);
+
 	snd_es1968_set_acpi(chip, ACPI_D3);
 	chip->master_switch = NULL;
 	chip->master_volume = NULL;
 	if (chip->res_io_port) {
-		snd_es1968_reset(chip);
 		release_resource(chip->res_io_port);
 		kfree_nocheck(chip->res_io_port);
 	}
@@ -2529,7 +2531,7 @@
 				    es1968_t **chip_ret)
 {
 	static snd_device_ops_t ops = {
-		dev_free:       snd_es1968_dev_free,
+		.dev_free =	snd_es1968_dev_free,
 	};
 	es1968_t *chip;
 	int i, err;
@@ -2654,11 +2656,11 @@
 
 static snd_kcontrol_new_t snd_es1968_control_switches[] __devinitdata = {
 	{
-		name: "Joystick",
-		iface: SNDRV_CTL_ELEM_IFACE_CARD,
-		info: snd_es1968_joystick_info,
-		get: snd_es1968_joystick_get,
-		put: snd_es1968_joystick_put,
+		.name = "Joystick",
+		.iface = SNDRV_CTL_ELEM_IFACE_CARD,
+		.info = snd_es1968_joystick_info,
+		.get = snd_es1968_joystick_get,
+		.put = snd_es1968_joystick_put,
 	}
 };
 
@@ -2761,13 +2763,13 @@
 }
 
 static struct pci_driver driver = {
-	name: "ES1968 (ESS Maestro)",
-	id_table: snd_es1968_ids,
-	probe: snd_es1968_probe,
-	remove: __devexit_p(snd_es1968_remove),
+	.name = "ES1968 (ESS Maestro)",
+	.id_table = snd_es1968_ids,
+	.probe = snd_es1968_probe,
+	.remove = __devexit_p(snd_es1968_remove),
 #ifdef CONFIG_PM
-	suspend: snd_es1968_suspend,
-	resume: snd_es1968_resume,
+	.suspend = snd_es1968_suspend,
+	.resume = snd_es1968_resume,
 #endif
 };
 
diff -Nru a/sound/pci/fm801.c b/sound/pci/fm801.c
--- a/sound/pci/fm801.c	Mon Oct  7 11:25:36 2002
+++ b/sound/pci/fm801.c	Mon Oct  7 11:25:36 2002
@@ -270,9 +270,9 @@
 #define RATES sizeof(rates) / sizeof(rates[0])
 
 static snd_pcm_hw_constraint_list_t hw_constraints_rates = {
-	count: RATES,
-	list: rates,
-	mask: 0,
+	.count = RATES,
+	.list = rates,
+	.mask = 0,
 };
 
 static unsigned int channels[] = {
@@ -282,9 +282,9 @@
 #define CHANNELS sizeof(channels) / sizeof(channels[0])
 
 static snd_pcm_hw_constraint_list_t hw_constraints_channels = {
-	count: CHANNELS,
-	list: channels,
-	mask: 0,
+	.count = CHANNELS,
+	.list = channels,
+	.mask = 0,
 };
 
 /*
@@ -512,7 +512,7 @@
 
 static snd_pcm_hardware_t snd_fm801_playback =
 {
-	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
 				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
 				 SNDRV_PCM_INFO_MMAP_VALID),
 	formats:		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
@@ -531,7 +531,7 @@
 
 static snd_pcm_hardware_t snd_fm801_capture =
 {
-	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
 				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
 				 SNDRV_PCM_INFO_MMAP_VALID),
 	formats:		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
@@ -597,25 +597,25 @@
 }
 
 static snd_pcm_ops_t snd_fm801_playback_ops = {
-	open:		snd_fm801_playback_open,
-	close:		snd_fm801_playback_close,
-	ioctl:		snd_pcm_lib_ioctl,
-	hw_params:	snd_fm801_hw_params,
-	hw_free:	snd_fm801_hw_free,
-	prepare:	snd_fm801_playback_prepare,
-	trigger:	snd_fm801_playback_trigger,
-	pointer:	snd_fm801_playback_pointer,
+	.open =		snd_fm801_playback_open,
+	.close =	snd_fm801_playback_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	snd_fm801_hw_params,
+	.hw_free =	snd_fm801_hw_free,
+	.prepare =	snd_fm801_playback_prepare,
+	.trigger =	snd_fm801_playback_trigger,
+	.pointer =	snd_fm801_playback_pointer,
 };
 
 static snd_pcm_ops_t snd_fm801_capture_ops = {
-	open:		snd_fm801_capture_open,
-	close:		snd_fm801_capture_close,
-	ioctl:		snd_pcm_lib_ioctl,
-	hw_params:	snd_fm801_hw_params,
-	hw_free:	snd_fm801_hw_free,
-	prepare:	snd_fm801_capture_prepare,
-	trigger:	snd_fm801_capture_trigger,
-	pointer:	snd_fm801_capture_pointer,
+	.open =		snd_fm801_capture_open,
+	.close =	snd_fm801_capture_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	snd_fm801_hw_params,
+	.hw_free =	snd_fm801_hw_free,
+	.prepare =	snd_fm801_capture_prepare,
+	.trigger =	snd_fm801_capture_trigger,
+	.pointer =	snd_fm801_capture_pointer,
 };
 
 static void snd_fm801_pcm_free(snd_pcm_t *pcm)
@@ -656,9 +656,9 @@
  */
 
 #define FM801_SINGLE(xname, reg, shift, mask, invert) \
-{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, info: snd_fm801_info_single, \
-  get: snd_fm801_get_single, put: snd_fm801_put_single, \
-  private_value: reg | (shift << 8) | (mask << 16) | (invert << 24) }
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .info = snd_fm801_info_single, \
+  .get = snd_fm801_get_single, .put = snd_fm801_put_single, \
+  .private_value = reg | (shift << 8) | (mask << 16) | (invert << 24) }
 
 static int snd_fm801_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
 {
@@ -701,9 +701,9 @@
 }
 
 #define FM801_DOUBLE(xname, reg, shift_left, shift_right, mask, invert) \
-{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, info: snd_fm801_info_double, \
-  get: snd_fm801_get_double, put: snd_fm801_put_double, \
-  private_value: reg | (shift_left << 8) | (shift_right << 12) | (mask << 16) | (invert << 24) }
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .info = snd_fm801_info_double, \
+  .get = snd_fm801_get_double, .put = snd_fm801_put_double, \
+  .private_value = reg | (shift_left << 8) | (shift_right << 12) | (mask << 16) | (invert << 24) }
 
 static int snd_fm801_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
 {
@@ -804,11 +804,11 @@
 FM801_DOUBLE("FM Playback Volume", FM801_FM_VOL, 0, 8, 31, 1),
 FM801_SINGLE("FM Playback Switch", FM801_FM_VOL, 15, 1, 1),
 {
-	iface: SNDRV_CTL_ELEM_IFACE_MIXER,
-	name: "Digital Capture Source",
-	info: snd_fm801_info_mux,
-	get: snd_fm801_get_mux,
-	put: snd_fm801_put_mux,
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "Digital Capture Source",
+	.info = snd_fm801_info_mux,
+	.get = snd_fm801_get_mux,
+	.put = snd_fm801_put_mux,
 }
 };
 
@@ -903,7 +903,7 @@
 	signed long timeout;
 	int err;
 	static snd_device_ops_t ops = {
-		dev_free:	snd_fm801_dev_free,
+		.dev_free =	snd_fm801_dev_free,
 	};
 	
 	*rchip = NULL;
@@ -1091,10 +1091,10 @@
 }
 
 static struct pci_driver driver = {
-	name: "FM801",
-	id_table: snd_fm801_ids,
-	probe: snd_card_fm801_probe,
-	remove: __devexit_p(snd_card_fm801_remove),
+	.name = "FM801",
+	.id_table = snd_fm801_ids,
+	.probe = snd_card_fm801_probe,
+	.remove = __devexit_p(snd_card_fm801_remove),
 };
                                                                 
 static int __init alsa_card_fm801_init(void)
diff -Nru a/sound/pci/ice1712/Makefile b/sound/pci/ice1712/Makefile
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/sound/pci/ice1712/Makefile	Mon Oct  7 11:26:06 2002
@@ -0,0 +1,13 @@
+#
+# Makefile for ALSA
+# Copyright (c) 2001 by Jaroslav Kysela <perex@suse.cz>
+#
+
+export-objs  := ice1712.o
+
+snd-ice1712-objs := ice1712.o ak4524.o delta.o hoontech.o ews.o
+
+# Toplevel Module Dependency
+obj-$(CONFIG_SND_ICE1712) += snd-ice1712.o
+
+include $(TOPDIR)/Rules.make
diff -Nru a/sound/pci/ice1712/ak4524.c b/sound/pci/ice1712/ak4524.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/sound/pci/ice1712/ak4524.c	Mon Oct  7 11:26:06 2002
@@ -0,0 +1,324 @@
+/*
+ *   ALSA driver for ICEnsemble ICE1712 (Envy24)
+ *
+ *   AK4524 / AK4528 interface
+ *
+ *	Copyright (c) 2000 Jaroslav Kysela <perex@suse.cz>
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */      
+
+#include <sound/driver.h>
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <sound/core.h>
+#include "ice1712.h"
+
+
+/*
+ * write AK4524 register
+ */
+void snd_ice1712_ak4524_write(ice1712_t *ice, int chip,
+			      unsigned char addr, unsigned char data)
+{
+	unsigned char tmp, saved[2];
+	int idx;
+	unsigned int addrdata;
+	ak4524_t *ak = &ice->ak4524;
+
+	snd_assert(chip >= 0 && chip < 4, return);
+
+	if (ak->ops.start) {
+		if (ak->ops.start(ice, saved, chip) < 0)
+			return;
+	} else
+		snd_ice1712_save_gpio_status(ice, saved);
+
+	tmp = snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA);
+	tmp |= ak->add_flags;
+	if (ak->cif) {
+		tmp |= ak->codecs_mask; /* start without chip select */
+	}  else {
+		tmp &= ~ak->codecs_mask; /* chip select low */
+		snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp);
+		udelay(1);
+	}
+
+	addr &= 0x07;
+	/* build I2C address + data byte */
+	addrdata = 0xa000 | (addr << 8) | data;
+	for (idx = 15; idx >= 0; idx--) {
+		tmp &= ~(ak->data_mask | ak->clk_mask);
+		if (addrdata & (1 << idx))
+			tmp |= ak->data_mask;
+		snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp);
+		//udelay(200);
+		udelay(1);
+		tmp |= ak->clk_mask;
+		snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp);
+		udelay(1);
+	}
+
+	if ((addr != 0x04 && addr != 0x05) || (data & 0x80) == 0)
+		ak->images[chip][addr] = data;
+	else
+		ak->ipga_gain[chip][addr-4] = data;
+
+	if (ak->cif) {
+		/* assert a cs pulse to trigger */
+		tmp &= ~ak->codecs_mask;
+		snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp);
+		udelay(1);
+	}
+	tmp |= ak->codecs_mask; /* chip select high to trigger */
+	snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp);
+	udelay(1);
+
+	if (ak->ops.stop)
+		ak->ops.stop(ice, saved);
+	else
+		snd_ice1712_restore_gpio_status(ice, saved);
+}
+
+void snd_ice1712_ak4524_reset(ice1712_t *ice, int state)
+{
+	int chip;
+	unsigned char reg;
+	ak4524_t *ak = &ice->ak4524;
+	
+	for (chip = 0; chip < ak->num_dacs/2; chip++) {
+		snd_ice1712_ak4524_write(ice, chip, 0x01, state ? 0x00 : 0x03);
+		if (state)
+			continue;
+		for (reg = 0x04; reg < (ak->is_ak4528 ? 0x06 : 0x08); reg++)
+			snd_ice1712_ak4524_write(ice, chip, reg, ak->images[chip][reg]);
+		if (ak->is_ak4528)
+			continue;
+		for (reg = 0x04; reg < 0x06; reg++)
+			snd_ice1712_ak4524_write(ice, chip, reg, ak->ipga_gain[chip][reg-4]);
+	}
+}
+
+/*
+ * initialize all the ak4524/4528 chips
+ */
+void __devinit snd_ice1712_ak4524_init(ice1712_t *ice)
+{
+	static unsigned char inits[] = {
+		0x00, 0x07, /* 0: all power up */
+		0x01, 0x00, /* 1: ADC/DAC reset */
+		0x02, 0x60, /* 2: 24bit I2S */
+		0x03, 0x19, /* 3: deemphasis off */
+		0x01, 0x03, /* 1: ADC/DAC enable */
+		0x04, 0x00, /* 4: ADC left muted */
+		0x05, 0x00, /* 5: ADC right muted */
+		0x04, 0x80, /* 4: ADC IPGA gain 0dB */
+		0x05, 0x80, /* 5: ADC IPGA gain 0dB */
+		0x06, 0x00, /* 6: DAC left muted */
+		0x07, 0x00, /* 7: DAC right muted */
+		0xff, 0xff
+	};
+	int chip, idx;
+	unsigned char *ptr, reg, data;
+	ak4524_t *ak = &ice->ak4524;
+
+	for (chip = idx = 0; chip < ak->num_dacs/2; chip++) {
+		ptr = inits;
+		while (*ptr != 0xff) {
+			reg = *ptr++;
+			data = *ptr++;
+			if (ak->is_ak4528) {
+				if (reg > 5)
+					continue;
+				if (reg >= 4 && (data & 0x80))
+					continue;
+			}
+			if (reg == 0x03 && ak->is_ak4528)
+				data = 0x0d;	/* deemphasis off, turn LR highpass filters on */
+			snd_ice1712_ak4524_write(ice, chip, reg, data);
+		}
+	}
+}
+
+static int snd_ice1712_ak4524_volume_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 127;
+	return 0;
+}
+
+static int snd_ice1712_ak4524_volume_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	int chip = kcontrol->private_value / 8;
+	int addr = kcontrol->private_value % 8;
+	ucontrol->value.integer.value[0] = ice->ak4524.images[chip][addr];
+	return 0;
+}
+
+static int snd_ice1712_ak4524_volume_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	int chip = kcontrol->private_value / 8;
+	int addr = kcontrol->private_value % 8;
+	unsigned char nval = ucontrol->value.integer.value[0];
+	int change = ice->ak4524.images[chip][addr] != nval;
+	if (change)
+		snd_ice1712_ak4524_write(ice, chip, addr, nval);
+	return change;
+}
+
+static int snd_ice1712_ak4524_ipga_gain_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 36;
+	return 0;
+}
+
+static int snd_ice1712_ak4524_ipga_gain_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	int chip = kcontrol->private_value / 8;
+	int addr = kcontrol->private_value % 8;
+	ucontrol->value.integer.value[0] = ice->ak4524.ipga_gain[chip][addr-4] & 0x7f;
+	return 0;
+}
+
+static int snd_ice1712_ak4524_ipga_gain_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	int chip = kcontrol->private_value / 8;
+	int addr = kcontrol->private_value % 8;
+	unsigned char nval = (ucontrol->value.integer.value[0] % 37) | 0x80;
+	int change = ice->ak4524.ipga_gain[chip][addr] != nval;
+	if (change)
+		snd_ice1712_ak4524_write(ice, chip, addr, nval);
+	return change;
+}
+
+static int snd_ice1712_ak4524_deemphasis_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+	static char *texts[4] = {
+		"44.1kHz", "Off", "48kHz", "32kHz",
+	};
+	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 = 3;
+	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+	return 0;
+}
+
+static int snd_ice1712_ak4524_deemphasis_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	int chip = kcontrol->id.index;
+	ucontrol->value.enumerated.item[0] = ice->ak4524.images[chip][3] & 3;
+	return 0;
+}
+
+static int snd_ice1712_ak4524_deemphasis_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	int chip = kcontrol->id.index;
+	unsigned char nval = ucontrol->value.enumerated.item[0];
+	int change;
+	nval |= (nval & 3) | (ice->ak4524.images[chip][3] & ~3);
+	change = ice->ak4524.images[chip][3] != nval;
+	if (change)
+		snd_ice1712_ak4524_write(ice, chip, 3, nval);
+	return change;
+}
+
+/*
+ * build AK4524 controls
+ */
+
+int __devinit snd_ice1712_ak4524_build_controls(ice1712_t *ice)
+{
+	int err, idx;
+	ak4524_t *ak = &ice->ak4524;
+
+	for (idx = 0; idx < ak->num_dacs; ++idx) {
+		snd_kcontrol_t ctl;
+		memset(&ctl, 0, sizeof(ctl));
+		strcpy(ctl.id.name, "DAC Volume");
+		ctl.id.index = idx;
+		ctl.id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+		ctl.info = snd_ice1712_ak4524_volume_info;
+		ctl.access = SNDRV_CTL_ELEM_ACCESS_READ|SNDRV_CTL_ELEM_ACCESS_WRITE;
+		ctl.get = snd_ice1712_ak4524_volume_get;
+		ctl.put = snd_ice1712_ak4524_volume_put;
+		if (ak->is_ak4528)
+			ctl.private_value = (idx / 2) * 8 + (idx % 2) + 4; /* register 4 & 5 */
+		else
+			ctl.private_value = (idx / 2) * 8 + (idx % 2) + 6; /* register 6 & 7 */
+		ctl.private_data = ice;
+		if ((err = snd_ctl_add(ice->card, snd_ctl_new(&ctl))) < 0)
+			return err;
+	}
+	for (idx = 0; idx < ak->num_adcs && !ak->is_ak4528; ++idx) {
+		snd_kcontrol_t ctl;
+		memset(&ctl, 0, sizeof(ctl));
+		strcpy(ctl.id.name, "ADC Volume");
+		ctl.id.index = idx;
+		ctl.id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+		ctl.info = snd_ice1712_ak4524_volume_info;
+		ctl.access = SNDRV_CTL_ELEM_ACCESS_READ|SNDRV_CTL_ELEM_ACCESS_WRITE;
+		ctl.get = snd_ice1712_ak4524_volume_get;
+		ctl.put = snd_ice1712_ak4524_volume_put;
+		ctl.private_value = (idx / 2) * 8 + (idx % 2) + 4; /* register 4 & 5 */
+		ctl.private_data = ice;
+		if ((err = snd_ctl_add(ice->card, snd_ctl_new(&ctl))) < 0)
+			return err;
+		memset(&ctl, 0, sizeof(ctl));
+		strcpy(ctl.id.name, "IPGA Analog Capture Volume");
+		ctl.id.index = idx;
+		ctl.id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+		ctl.info = snd_ice1712_ak4524_ipga_gain_info;
+		ctl.access = SNDRV_CTL_ELEM_ACCESS_READ|SNDRV_CTL_ELEM_ACCESS_WRITE;
+		ctl.get = snd_ice1712_ak4524_ipga_gain_get;
+		ctl.put = snd_ice1712_ak4524_ipga_gain_put;
+		ctl.private_value = (idx / 2) * 8 + (idx % 2) + 4; /* register 4 & 5 */
+		ctl.private_data = ice;
+		if ((err = snd_ctl_add(ice->card, snd_ctl_new(&ctl))) < 0)
+			return err;
+	}
+	for (idx = 0; idx < ak->num_dacs/2; idx++) {
+		snd_kcontrol_t ctl;
+		memset(&ctl, 0, sizeof(ctl));
+		strcpy(ctl.id.name, "Deemphasis");
+		ctl.id.index = idx;
+		ctl.id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+		ctl.info = snd_ice1712_ak4524_deemphasis_info;
+		ctl.access = SNDRV_CTL_ELEM_ACCESS_READ|SNDRV_CTL_ELEM_ACCESS_WRITE;
+		ctl.get = snd_ice1712_ak4524_deemphasis_get;
+		ctl.put = snd_ice1712_ak4524_deemphasis_put;
+		ctl.private_data = ice;
+		if ((err = snd_ctl_add(ice->card, snd_ctl_new(&ctl))) < 0)
+			return err;
+	}
+	return 0;
+}
+
+
diff -Nru a/sound/pci/ice1712/delta.c b/sound/pci/ice1712/delta.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/sound/pci/ice1712/delta.c	Mon Oct  7 11:25:40 2002
@@ -0,0 +1,498 @@
+/*
+ *   ALSA driver for ICEnsemble ICE1712 (Envy24)
+ *
+ *   Lowlevel functions for M-Audio Delta 1010, 44, 66, Dio2496, Audiophile
+ *
+ *	Copyright (c) 2000 Jaroslav Kysela <perex@suse.cz>
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */      
+
+#include <sound/driver.h>
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/cs8427.h>
+#include <sound/asoundef.h>
+
+#include "ice1712.h"
+#include "delta.h"
+
+#define SND_CS8403
+#include <sound/cs8403.h>
+
+
+/*
+ * CS8427 via SPI mode (for Audiophile), emulated I2C
+ */
+
+/* send 8 bits */
+static void ap_cs8427_write_byte(ice1712_t *ice, unsigned char data, unsigned char tmp)
+{
+	int idx;
+
+	for (idx = 7; idx >= 0; idx--) {
+		tmp &= ~(ICE1712_DELTA_AP_DOUT|ICE1712_DELTA_AP_CCLK);
+		if (data & (1 << idx))
+			tmp |= ICE1712_DELTA_AP_DOUT;
+		snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp);
+		udelay(5);
+		tmp |= ICE1712_DELTA_AP_CCLK;
+		snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp);
+		udelay(5);
+	}
+}
+
+/* read 8 bits */
+static unsigned char ap_cs8427_read_byte(ice1712_t *ice, unsigned char tmp)
+{
+	unsigned char data = 0;
+	int idx;
+	
+	for (idx = 7; idx >= 0; idx--) {
+		tmp &= ~ICE1712_DELTA_AP_CCLK;
+		snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp);
+		udelay(5);
+		if (snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA) & ICE1712_DELTA_AP_DIN)
+			data |= 1 << idx;
+		tmp |= ICE1712_DELTA_AP_CCLK;
+		snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp);
+		udelay(5);
+	}
+	return data;
+}
+
+/* assert chip select */
+static unsigned char ap_cs8427_codec_select(ice1712_t *ice)
+{
+	unsigned char tmp;
+	tmp = snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA);
+	tmp |= ICE1712_DELTA_AP_CCLK | ICE1712_DELTA_AP_CS_CODEC;
+	tmp &= ~ICE1712_DELTA_AP_CS_DIGITAL;
+	snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp);
+	udelay(5);
+	return tmp;
+}
+
+/* deassert chip select */
+static void ap_cs8427_codec_deassert(ice1712_t *ice, unsigned char tmp)
+{
+	tmp |= ICE1712_DELTA_AP_CS_DIGITAL;
+	snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp);
+}
+
+/* sequential write */
+static int ap_cs8427_sendbytes(snd_i2c_device_t *device, unsigned char *bytes, int count)
+{
+	ice1712_t *ice = snd_magic_cast(ice1712_t, device->bus->private_data, return -EIO);
+	int res = count;
+	unsigned char tmp;
+
+	down(&ice->gpio_mutex);
+	tmp = ap_cs8427_codec_select(ice);
+	ap_cs8427_write_byte(ice, (device->addr << 1) | 0, tmp); /* address + write mode */
+	while (count-- > 0)
+		ap_cs8427_write_byte(ice, *bytes++, tmp);
+	ap_cs8427_codec_deassert(ice, tmp);
+	up(&ice->gpio_mutex);
+	return res;
+}
+
+/* sequential read */
+static int ap_cs8427_readbytes(snd_i2c_device_t *device, unsigned char *bytes, int count)
+{
+	ice1712_t *ice = snd_magic_cast(ice1712_t, device->bus->private_data, return -EIO);
+	int res = count;
+	unsigned char tmp;
+	
+	down(&ice->gpio_mutex);
+	tmp = ap_cs8427_codec_select(ice);
+	ap_cs8427_write_byte(ice, (device->addr << 1) | 1, tmp); /* address + read mode */
+	while (count-- > 0)
+		*bytes++ = ap_cs8427_read_byte(ice, tmp);
+	ap_cs8427_codec_deassert(ice, tmp);
+	up(&ice->gpio_mutex);
+	return res;
+}
+
+static int ap_cs8427_probeaddr(snd_i2c_bus_t *bus, unsigned short addr)
+{
+	if (addr == 0x10)
+		return 1;
+	return -ENOENT;
+}
+
+static snd_i2c_ops_t ap_cs8427_i2c_ops = {
+	.sendbytes = ap_cs8427_sendbytes,
+	.readbytes = ap_cs8427_readbytes,
+	.probeaddr = ap_cs8427_probeaddr,
+};
+
+/*
+ */
+
+static void snd_ice1712_delta_cs8403_spdif_write(ice1712_t *ice, unsigned char bits)
+{
+	unsigned char tmp, mask1, mask2;
+	int idx;
+	/* send byte to transmitter */
+	mask1 = ICE1712_DELTA_SPDIF_OUT_STAT_CLOCK;
+	mask2 = ICE1712_DELTA_SPDIF_OUT_STAT_DATA;
+	down(&ice->gpio_mutex);
+	tmp = snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA);
+	for (idx = 7; idx >= 0; idx--) {
+		tmp &= ~(mask1 | mask2);
+		if (bits & (1 << idx))
+			tmp |= mask2;
+		snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp);
+		udelay(100);
+		tmp |= mask1;
+		snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp);
+		udelay(100);
+	}
+	tmp &= ~mask1;
+	snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp);
+	up(&ice->gpio_mutex);
+}
+
+
+static void delta_spdif_default_get(ice1712_t *ice, snd_ctl_elem_value_t * ucontrol)
+{
+	snd_cs8403_decode_spdif_bits(&ucontrol->value.iec958, ice->spdif.cs8403_bits);
+}
+
+static int delta_spdif_default_put(ice1712_t *ice, snd_ctl_elem_value_t * ucontrol)
+{
+	unsigned long flags;
+	unsigned int val;
+	int change;
+
+	val = snd_cs8403_encode_spdif_bits(&ucontrol->value.iec958);
+	spin_lock_irqsave(&ice->reg_lock, flags);
+	change = ice->spdif.cs8403_bits != val;
+	ice->spdif.cs8403_bits = val;
+	if (change && ice->playback_pro_substream == NULL) {
+		spin_unlock_irqrestore(&ice->reg_lock, flags);
+		snd_ice1712_delta_cs8403_spdif_write(ice, val);
+	} else {
+		spin_unlock_irqrestore(&ice->reg_lock, flags);
+	}
+	return change;
+}
+
+static void delta_spdif_stream_get(ice1712_t *ice, snd_ctl_elem_value_t * ucontrol)
+{
+	snd_cs8403_decode_spdif_bits(&ucontrol->value.iec958, ice->spdif.cs8403_stream_bits);
+}
+
+static int delta_spdif_stream_put(ice1712_t *ice, snd_ctl_elem_value_t * ucontrol)
+{
+	unsigned long flags;
+	unsigned int val;
+	int change;
+
+	val = snd_cs8403_encode_spdif_bits(&ucontrol->value.iec958);
+	spin_lock_irqsave(&ice->reg_lock, flags);
+	change = ice->spdif.cs8403_stream_bits != val;
+	ice->spdif.cs8403_stream_bits = val;
+	if (change && ice->playback_pro_substream != NULL) {
+		spin_unlock_irqrestore(&ice->reg_lock, flags);
+		snd_ice1712_delta_cs8403_spdif_write(ice, val);
+	} else {
+		spin_unlock_irqrestore(&ice->reg_lock, flags);
+	}
+	return change;
+}
+
+
+/*
+ * AK4524 on Delta 44 and 66 to choose the chip mask
+ */
+static int delta_ak4524_start(ice1712_t *ice, unsigned char *saved, int chip)
+{
+	snd_ice1712_save_gpio_status(ice, saved);
+	ice->ak4524.codecs_mask = chip == 0 ? ICE1712_DELTA_CODEC_CHIP_A : ICE1712_DELTA_CODEC_CHIP_B;
+	return 0;
+}
+
+/*
+ * change the rate of AK4524 on Delta 44/66 and AP
+ */
+static void delta_ak4524_set_rate_val(ice1712_t *ice, unsigned char val)
+{
+	unsigned char tmp, tmp2;
+
+	/* check before reset ak4524 to avoid unnecessary clicks */
+	down(&ice->gpio_mutex);
+	tmp = snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA);
+	up(&ice->gpio_mutex);
+	tmp2 = tmp;
+	tmp2 &= ~ICE1712_DELTA_DFS; 
+	if (val == 15 || val == 11 || val == 7)
+		tmp2 |= ICE1712_DELTA_DFS;
+	if (tmp == tmp2)
+		return;
+
+	/* do it again */
+	snd_ice1712_ak4524_reset(ice, 1);
+	down(&ice->gpio_mutex);
+	tmp = snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA);
+	if (val == 15 || val == 11 || val == 7) {
+		tmp |= ICE1712_DELTA_DFS;
+	} else {
+		tmp &= ~ICE1712_DELTA_DFS;
+	}
+	snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp);
+	up(&ice->gpio_mutex);
+	snd_ice1712_ak4524_reset(ice, 0);
+}
+
+
+/*
+ * SPDIF ops for Delta 1010, Dio, 66
+ */
+
+/* open callback */
+static void delta_open_spdif(ice1712_t *ice, snd_pcm_substream_t * substream)
+{
+	ice->spdif.cs8403_stream_bits = ice->spdif.cs8403_bits;
+}
+
+/* set up */
+static void delta_setup_spdif(ice1712_t *ice, snd_pcm_substream_t * substream)
+{
+	unsigned long flags;
+	unsigned int tmp;
+	int change;
+
+	spin_lock_irqsave(&ice->reg_lock, flags);
+	tmp = ice->spdif.cs8403_stream_bits;
+	if (tmp & 0x01)		/* consumer */
+		tmp &= (tmp & 0x01) ? ~0x06 : ~0x18;
+	switch (substream->runtime->rate) {
+	case 32000: tmp |= (tmp & 0x01) ? 0x04 : 0x00; break;
+	case 44100: tmp |= (tmp & 0x01) ? 0x00 : 0x10; break;
+	case 48000: tmp |= (tmp & 0x01) ? 0x02 : 0x08; break;
+	default: tmp |= (tmp & 0x01) ? 0x00 : 0x18; break;
+	}
+	change = ice->spdif.cs8403_stream_bits != tmp;
+	ice->spdif.cs8403_stream_bits = tmp;
+	spin_unlock_irqrestore(&ice->reg_lock, flags);
+	if (change)
+		snd_ctl_notify(ice->card, SNDRV_CTL_EVENT_MASK_VALUE, &ice->spdif.stream_ctl->id);
+	snd_ice1712_delta_cs8403_spdif_write(ice, tmp);
+}
+
+
+/*
+ * initialize the chips on M-Audio cards
+ */
+
+static int __devinit snd_ice1712_delta_init(ice1712_t *ice)
+{
+	int err;
+	ak4524_t *ak;
+
+	/* determine I2C, DACs and ADCs */
+	switch (ice->eeprom.subvendor) {
+	case ICE1712_SUBDEVICE_AUDIOPHILE:
+		ice->num_total_dacs = 2;
+		break;
+	case ICE1712_SUBDEVICE_DELTA44:
+	case ICE1712_SUBDEVICE_DELTA66:
+		ice->num_total_dacs = ice->omni ? 8 : 4;
+		break;
+	case ICE1712_SUBDEVICE_DELTA1010:
+		ice->num_total_dacs = 8;
+		break;
+	}
+
+	/* initialize spdif */
+	switch (ice->eeprom.subvendor) {
+	case ICE1712_SUBDEVICE_AUDIOPHILE:
+		if ((err = snd_i2c_bus_create(ice->card, "ICE1712 GPIO 1", NULL, &ice->i2c)) < 0) {
+			snd_printk("unable to create I2C bus\n");
+			return err;
+		}
+		ice->i2c->private_data = ice;
+		ice->i2c->ops = &ap_cs8427_i2c_ops;
+		if ((err = snd_ice1712_init_cs8427(ice, CS8427_BASE_ADDR)) < 0)
+			return err;
+		break;
+	case ICE1712_SUBDEVICE_DELTA1010:
+	case ICE1712_SUBDEVICE_DELTADIO2496:
+	case ICE1712_SUBDEVICE_DELTA66:
+		ice->spdif.ops.open = delta_open_spdif;
+		ice->spdif.ops.setup = delta_setup_spdif;
+		ice->spdif.ops.default_get = delta_spdif_default_get;
+		ice->spdif.ops.default_put = delta_spdif_default_put;
+		ice->spdif.ops.stream_get = delta_spdif_stream_get;
+		ice->spdif.ops.stream_put = delta_spdif_stream_put;
+		/* Set spdif defaults */
+		snd_ice1712_delta_cs8403_spdif_write(ice, ice->spdif.cs8403_bits);
+		break;
+	}
+
+	/* second stage of initialization, analog parts and others */
+	ak = &ice->ak4524;
+	switch (ice->eeprom.subvendor) {
+	case ICE1712_SUBDEVICE_AUDIOPHILE:
+		ak->num_adcs = ak->num_dacs = 2;
+		ak->is_ak4528 = 1;
+		ak->cif = 0; /* the default level of the CIF pin from AK4524 */
+		ak->data_mask = ICE1712_DELTA_AP_DOUT;
+		ak->clk_mask = ICE1712_DELTA_AP_CCLK;
+		ak->codecs_mask = ICE1712_DELTA_AP_CS_CODEC; /* select AK4528 codec */
+		ak->add_flags = ICE1712_DELTA_AP_CS_DIGITAL; /* assert digital high */
+		ak->ops.set_rate_val = delta_ak4524_set_rate_val;
+		snd_ice1712_ak4524_init(ice);
+		break;
+	case ICE1712_SUBDEVICE_DELTA66:
+	case ICE1712_SUBDEVICE_DELTA44:
+		ak->num_adcs = ak->num_dacs = 4;
+		ak->cif = 0; /* the default level of the CIF pin from AK4524 */
+		ak->data_mask = ICE1712_DELTA_CODEC_SERIAL_DATA;
+		ak->clk_mask = ICE1712_DELTA_CODEC_SERIAL_CLOCK;
+		ak->codecs_mask = 0; /* set later */
+		ak->add_flags = 0;
+		ak->ops.start = delta_ak4524_start;
+		ak->ops.set_rate_val = delta_ak4524_set_rate_val;
+		snd_ice1712_ak4524_init(ice);
+		break;
+	}
+
+	return 0;
+}
+
+
+/*
+ * additional controls for M-Audio cards
+ */
+
+static snd_kcontrol_new_t snd_ice1712_delta1010_wordclock_select __devinitdata =
+ICE1712_GPIO(SNDRV_CTL_ELEM_IFACE_PCM, "Word Clock Sync", 0, ICE1712_DELTA_WORD_CLOCK_SELECT, 1, 0);
+static snd_kcontrol_new_t snd_ice1712_delta1010_wordclock_status __devinitdata =
+ICE1712_GPIO(SNDRV_CTL_ELEM_IFACE_PCM, "Word Clock Status", 0, ICE1712_DELTA_WORD_CLOCK_STATUS, 1, SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE);
+static snd_kcontrol_new_t snd_ice1712_deltadio2496_spdif_in_select __devinitdata =
+ICE1712_GPIO(SNDRV_CTL_ELEM_IFACE_PCM, "IEC958 Input Optical", 0, ICE1712_DELTA_SPDIF_INPUT_SELECT, 0, 0);
+static snd_kcontrol_new_t snd_ice1712_delta_spdif_in_status __devinitdata =
+ICE1712_GPIO(SNDRV_CTL_ELEM_IFACE_PCM, "Delta IEC958 Input Status", 0, ICE1712_DELTA_SPDIF_IN_STAT, 1, SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE);
+
+
+static int __devinit snd_ice1712_delta_add_controls(ice1712_t *ice)
+{
+	int err;
+
+	/* 1010 and dio specific controls */
+	switch (ice->eeprom.subvendor) {
+	case ICE1712_SUBDEVICE_DELTA1010:
+		err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_delta1010_wordclock_select, ice));
+		if (err < 0)
+			return err;
+		err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_delta1010_wordclock_status, ice));
+		if (err < 0)
+			return err;
+		break;
+	case ICE1712_SUBDEVICE_DELTADIO2496:
+		err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_deltadio2496_spdif_in_select, ice));
+		if (err < 0)
+			return err;
+		break;
+	}
+
+	/* normal spdif controls */
+	switch (ice->eeprom.subvendor) {
+	case ICE1712_SUBDEVICE_DELTA1010:
+	case ICE1712_SUBDEVICE_DELTADIO2496:
+	case ICE1712_SUBDEVICE_DELTA66:
+	case ICE1712_SUBDEVICE_AUDIOPHILE:
+		err = snd_ice1712_spdif_build_controls(ice);
+		if (err < 0)
+			return err;
+		break;
+	}
+
+	/* spdif status in */
+	switch (ice->eeprom.subvendor) {
+	case ICE1712_SUBDEVICE_DELTA1010:
+	case ICE1712_SUBDEVICE_DELTADIO2496:
+	case ICE1712_SUBDEVICE_DELTA66:
+		err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_delta_spdif_in_status, ice));
+		if (err < 0)
+			return err;
+		break;
+	}
+
+	/* ak4524 controls */
+	switch (ice->eeprom.subvendor) {
+	case ICE1712_SUBDEVICE_AUDIOPHILE:
+	case ICE1712_SUBDEVICE_DELTA44:
+	case ICE1712_SUBDEVICE_DELTA66:
+		err = snd_ice1712_ak4524_build_controls(ice);
+		if (err < 0)
+			return err;
+		break;
+	}
+	return 0;
+}
+
+
+/* entry point */
+struct snd_ice1712_card_info snd_ice1712_delta_cards[] __devinitdata = {
+	{
+		ICE1712_SUBDEVICE_DELTA1010,
+		"M Audio Delta 1010",
+		snd_ice1712_delta_init,
+		snd_ice1712_delta_add_controls,
+	},
+	{
+		ICE1712_SUBDEVICE_DELTADIO2496,
+		"M Audio Delta DiO 2496",
+		snd_ice1712_delta_init,
+		snd_ice1712_delta_add_controls,
+		1, /* NO MPU */
+	},
+	{
+		ICE1712_SUBDEVICE_DELTA66,
+		"M Audio Delta 66",
+		snd_ice1712_delta_init,
+		snd_ice1712_delta_add_controls,
+		1, /* NO MPU */
+	},
+	{
+		ICE1712_SUBDEVICE_DELTA44,
+		"M Audio Delta 44",
+		snd_ice1712_delta_init,
+		snd_ice1712_delta_add_controls,
+		1, /* NO MPU */
+	},
+	{
+		ICE1712_SUBDEVICE_AUDIOPHILE,
+		"M Audio Audiophile 24/96",
+		snd_ice1712_delta_init,
+		snd_ice1712_delta_add_controls,
+	},
+	{
+		ICE1712_SUBDEVICE_DELTA1010LT,
+		"M Audio Delta 1010LT",
+		snd_ice1712_delta_init,
+		snd_ice1712_delta_add_controls,
+	},
+	{ } /* terminator */
+};
diff -Nru a/sound/pci/ice1712/delta.h b/sound/pci/ice1712/delta.h
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/sound/pci/ice1712/delta.h	Mon Oct  7 11:26:06 2002
@@ -0,0 +1,120 @@
+#ifndef __SOUND_DELTA_H
+#define __SOUND_DELTA_H
+
+/*
+ *   ALSA driver for ICEnsemble ICE1712 (Envy24)
+ *
+ *   Lowlevel functions for M-Audio Delta 1010, 44, 66, Dio2496, Audiophile
+ *
+ *	Copyright (c) 2000 Jaroslav Kysela <perex@suse.cz>
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */      
+
+#define DELTA_DEVICE_DESC \
+		"{MidiMan M Audio,Delta 1010},"\
+		"{MidiMan M Audio,Delta DiO 2496},"\
+		"{MidiMan M Audio,Delta 66},"\
+		"{MidiMan M Audio,Delta 44},"\
+		"{MidiMan M Audio,Audiophile 24/96},"
+
+#define ICE1712_SUBDEVICE_DELTA1010	0x121430d6
+#define ICE1712_SUBDEVICE_DELTADIO2496	0x121431d6
+#define ICE1712_SUBDEVICE_DELTA66	0x121432d6
+#define ICE1712_SUBDEVICE_DELTA44	0x121433d6
+#define ICE1712_SUBDEVICE_AUDIOPHILE	0x121434d6
+#define ICE1712_SUBDEVICE_DELTA1010LT	0x12143bd6
+
+/* entry point */
+extern struct snd_ice1712_card_info snd_ice1712_delta_cards[];
+
+
+/*
+ *  MidiMan M-Audio Delta GPIO definitions
+ */
+
+/* MidiMan M-Audio Delta1010 */
+#define ICE1712_DELTA_DFS 0x01		/* fast/slow sample rate mode */
+					/* (>48kHz must be 1) */
+#define ICE1712_DELTA_SPDIF_IN_STAT 0x02
+					/* S/PDIF input status */
+					/* 0 = valid signal is present */
+					/* all except Delta44 */
+					/* look to CS8414 datasheet */
+#define ICE1712_DELTA_SPDIF_OUT_STAT_CLOCK 0x04
+					/* S/PDIF output status clock */
+					/* (writting on rising edge - 0->1) */
+					/* all except Delta44 */
+					/* look to CS8404A datasheet */
+#define ICE1712_DELTA_SPDIF_OUT_STAT_DATA 0x08
+					/* S/PDIF output status data */
+					/* all except Delta44 */
+					/* look to CS8404A datasheet */
+/* MidiMan M-Audio DeltaDiO */
+/* 0x01 = DFS */
+/* 0x02 = SPDIF_IN_STAT */
+/* 0x04 = SPDIF_OUT_STAT_CLOCK */
+/* 0x08 = SPDIF_OUT_STAT_DATA */
+#define ICE1712_DELTA_SPDIF_INPUT_SELECT 0x10
+					/* coaxial (0), optical (1) */
+					/* S/PDIF input select*/
+
+/* MidiMan M-Audio Delta1010 */
+/* 0x01 = DFS */
+/* 0x02 = SPDIF_IN_STAT */
+/* 0x04 = SPDIF_OUT_STAT_CLOCK */
+/* 0x08 = SPDIF_OUT_STAT_DATA */
+#define ICE1712_DELTA_WORD_CLOCK_SELECT 0x10
+					/* 1 - clock are taken from S/PDIF input */
+					/* 0 - clock are taken from Word Clock input */
+					/* affected SPMCLKIN pin of Envy24 */
+#define ICE1712_DELTA_WORD_CLOCK_STATUS	0x20
+					/* 0 = valid word clock signal is present */
+
+/* MidiMan M-Audio Delta66 */
+/* 0x01 = DFS */
+/* 0x02 = SPDIF_IN_STAT */
+/* 0x04 = SPDIF_OUT_STAT_CLOCK */
+/* 0x08 = SPDIF_OUT_STAT_DATA */
+#define ICE1712_DELTA_CODEC_SERIAL_DATA 0x10
+					/* AKM4524 serial data */
+#define ICE1712_DELTA_CODEC_SERIAL_CLOCK 0x20
+					/* AKM4524 serial clock */
+					/* (writting on rising edge - 0->1 */
+#define ICE1712_DELTA_CODEC_CHIP_A	0x40
+#define ICE1712_DELTA_CODEC_CHIP_B	0x80
+					/* 1 - select chip A or B */
+
+/* MidiMan M-Audio Delta44 */
+/* 0x01 = DFS */
+/* 0x10 = CODEC_SERIAL_DATA */
+/* 0x20 = CODEC_SERIAL_CLOCK */
+/* 0x40 = CODEC_CHIP_A */
+/* 0x80 = CODEC_CHIP_B */
+
+/* MidiMan M-Audio Audiophile definitions */
+/* 0x01 = DFS */
+#define ICE1712_DELTA_AP_CCLK	0x02	/* SPI clock */
+					/* (clocking on rising edge - 0->1) */
+#define ICE1712_DELTA_AP_DIN	0x04	/* data input */
+#define ICE1712_DELTA_AP_DOUT	0x08	/* data output */
+#define ICE1712_DELTA_AP_CS_DIGITAL 0x10 /* CS8427 chip select */
+					/* low signal = select */
+#define ICE1712_DELTA_AP_CS_CODEC 0x20	/* AK4528 chip select */
+					/* low signal = select */
+
+
+#endif /* __SOUND_DELTA_H */
diff -Nru a/sound/pci/ice1712/ews.c b/sound/pci/ice1712/ews.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/sound/pci/ice1712/ews.c	Mon Oct  7 11:25:40 2002
@@ -0,0 +1,929 @@
+/*
+ *   ALSA driver for ICEnsemble ICE1712 (Envy24)
+ *
+ *   Lowlevel functions for Terratec EWS88MT/D, EWX24/96, DMX 6Fire
+ *
+ *	Copyright (c) 2000 Jaroslav Kysela <perex@suse.cz>
+ *                    2002 Takashi Iwai <tiwai@suse.de>
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */      
+
+#include <sound/driver.h>
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/cs8427.h>
+#include <sound/asoundef.h>
+
+#include "ice1712.h"
+#include "ews.h"
+
+#define SND_CS8404
+#include <sound/cs8403.h>
+
+/*
+ * access via i2c mode (for EWX 24/96, EWS 88MT&D)
+ */
+
+/* send SDA and SCL */
+static void ewx_i2c_setlines(snd_i2c_bus_t *bus, int clk, int data)
+{
+	ice1712_t *ice = snd_magic_cast(ice1712_t, bus->private_data, return);
+	unsigned char tmp = 0;
+	if (clk)
+		tmp |= ICE1712_EWX2496_SERIAL_CLOCK;
+	if (data)
+		tmp |= ICE1712_EWX2496_SERIAL_DATA;
+	snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp);
+	udelay(5);
+}
+
+static int ewx_i2c_getclock(snd_i2c_bus_t *bus)
+{
+	ice1712_t *ice = snd_magic_cast(ice1712_t, bus->private_data, return -EIO);
+	return snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA) & ICE1712_EWX2496_SERIAL_CLOCK ? 1 : 0;
+}
+
+static int ewx_i2c_getdata(snd_i2c_bus_t *bus, int ack)
+{
+	ice1712_t *ice = snd_magic_cast(ice1712_t, bus->private_data, return -EIO);
+	int bit;
+	/* set RW pin to low */
+	snd_ice1712_write(ice, ICE1712_IREG_GPIO_WRITE_MASK, ~ICE1712_EWX2496_RW);
+	snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, 0);
+	if (ack)
+		udelay(5);
+	bit = snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA) & ICE1712_EWX2496_SERIAL_DATA ? 1 : 0;
+	/* set RW pin to high */
+	snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, ICE1712_EWX2496_RW);
+	/* reset write mask */
+	snd_ice1712_write(ice, ICE1712_IREG_GPIO_WRITE_MASK, ~ICE1712_EWX2496_SERIAL_CLOCK);
+	return bit;
+}
+
+static void ewx_i2c_start(snd_i2c_bus_t *bus)
+{
+	ice1712_t *ice = snd_magic_cast(ice1712_t, bus->private_data, return);
+	unsigned char mask;
+
+	snd_ice1712_save_gpio_status(ice, (unsigned char *)&bus->private_value);
+	/* set RW high */
+	mask = ICE1712_EWX2496_RW;
+	switch (ice->eeprom.subvendor) {
+	case ICE1712_SUBDEVICE_EWX2496:
+		mask |= ICE1712_EWX2496_AK4524_CS; /* CS high also */
+		break;
+	case ICE1712_SUBDEVICE_DMX6FIRE:
+		mask |= ICE1712_6FIRE_AK4524_CS_MASK; /* CS high also */
+		break;
+	}
+	snd_ice1712_gpio_write_bits(ice, mask, mask);
+}
+
+static void ewx_i2c_stop(snd_i2c_bus_t *bus)
+{
+	ice1712_t *ice = snd_magic_cast(ice1712_t, bus->private_data, return);
+	snd_ice1712_restore_gpio_status(ice, (unsigned char *)&bus->private_value);
+}
+
+static void ewx_i2c_direction(snd_i2c_bus_t *bus, int clock, int data)
+{
+	ice1712_t *ice = snd_magic_cast(ice1712_t, bus->private_data, return);
+	unsigned char mask = 0;
+
+	if (clock)
+		mask |= ICE1712_EWX2496_SERIAL_CLOCK; /* write SCL */
+	if (data)
+		mask |= ICE1712_EWX2496_SERIAL_DATA; /* write SDA */
+	ice->gpio_direction &= ~(ICE1712_EWX2496_SERIAL_CLOCK|ICE1712_EWX2496_SERIAL_DATA);
+	ice->gpio_direction |= mask;
+	snd_ice1712_write(ice, ICE1712_IREG_GPIO_DIRECTION, ice->gpio_direction);
+	snd_ice1712_write(ice, ICE1712_IREG_GPIO_WRITE_MASK, ~mask);
+}
+
+static snd_i2c_bit_ops_t snd_ice1712_ewx_cs8427_bit_ops = {
+	.start = ewx_i2c_start,
+	.stop = ewx_i2c_stop,
+	.direction = ewx_i2c_direction,
+	.setlines = ewx_i2c_setlines,
+	.getclock = ewx_i2c_getclock,
+	.getdata = ewx_i2c_getdata,
+};
+
+
+/*
+ * AK4524 access
+ */
+
+/* AK4524 chip select; address 0x48 bit 0-3 */
+static int snd_ice1712_ews88mt_chip_select(ice1712_t *ice, int chip_mask)
+{
+	unsigned char data, ndata;
+
+	snd_assert(chip_mask >= 0 && chip_mask <= 0x0f, return -EINVAL);
+	snd_i2c_lock(ice->i2c);
+	if (snd_i2c_readbytes(ice->i2cdevs[1], &data, 1) != 1)
+		goto __error;
+	ndata = (data & 0xf0) | chip_mask;
+	if (ndata != data)
+		if (snd_i2c_sendbytes(ice->i2cdevs[1], &ndata, 1) != 1)
+			goto __error;
+	snd_i2c_unlock(ice->i2c);
+	return 0;
+
+     __error:
+	snd_i2c_unlock(ice->i2c);
+	snd_printk(KERN_ERR "AK4524 chip select failed, check cable to the front module\n");
+	return -EIO;
+}
+
+/* start callback for EWS88MT, needs to select a certain chip mask */
+static int ews88mt_ak4524_start(ice1712_t *ice, unsigned char *saved, int chip)
+{
+	unsigned char tmp;
+	/* assert AK4524 CS */
+	if (snd_ice1712_ews88mt_chip_select(ice, ~(1 << chip) & 0x0f) < 0)
+		return -EINVAL;
+	snd_ice1712_save_gpio_status(ice, saved);
+	tmp = ICE1712_EWS88_SERIAL_DATA |
+		ICE1712_EWS88_SERIAL_CLOCK |
+		ICE1712_EWS88_RW;
+	snd_ice1712_write(ice, ICE1712_IREG_GPIO_DIRECTION,
+			  ice->gpio_direction | tmp);
+	snd_ice1712_write(ice, ICE1712_IREG_GPIO_WRITE_MASK, ~tmp);
+	return 0;
+}
+
+/* stop callback for EWS88MT, needs to deselect chip mask */
+static void ews88mt_ak4524_stop(ice1712_t *ice, unsigned char *saved)
+{
+	snd_ice1712_restore_gpio_status(ice, saved);
+	udelay(1);
+	snd_ice1712_ews88mt_chip_select(ice, 0x0f);
+}
+
+/* start callback for EWX24/96 */
+static int ewx2496_ak4524_start(ice1712_t *ice, unsigned char *saved, int chip)
+{
+	unsigned char tmp;
+	snd_ice1712_save_gpio_status(ice, saved);
+	tmp =  ICE1712_EWX2496_SERIAL_DATA |
+		ICE1712_EWX2496_SERIAL_CLOCK |
+		ICE1712_EWX2496_AK4524_CS |
+		ICE1712_EWX2496_RW;
+	snd_ice1712_write(ice, ICE1712_IREG_GPIO_DIRECTION,
+			  ice->gpio_direction | tmp);
+	snd_ice1712_write(ice, ICE1712_IREG_GPIO_WRITE_MASK, ~tmp);
+	return 0;
+}
+
+/* start callback for DMX 6fire */
+static int dmx6fire_ak4524_start(ice1712_t *ice, unsigned char *saved, int chip)
+{
+	unsigned char tmp;
+	snd_ice1712_save_gpio_status(ice, saved);
+	tmp = ice->ak4524.codecs_mask = (1 << chip) & ICE1712_6FIRE_AK4524_CS_MASK;
+	tmp |= ICE1712_6FIRE_SERIAL_DATA |
+		ICE1712_6FIRE_SERIAL_CLOCK |
+		ICE1712_6FIRE_RW;
+	snd_ice1712_write(ice, ICE1712_IREG_GPIO_DIRECTION,
+			  ice->gpio_direction | tmp);
+	snd_ice1712_write(ice, ICE1712_IREG_GPIO_WRITE_MASK, ~tmp);
+	return 0;
+}
+
+
+/*
+ * CS8404 interface on EWS88MT/D
+ */
+
+static void snd_ice1712_ews_cs8404_spdif_write(ice1712_t *ice, unsigned char bits)
+{
+	unsigned char bytes[2];
+
+	snd_i2c_lock(ice->i2c);
+	switch (ice->eeprom.subvendor) {
+	case ICE1712_SUBDEVICE_EWS88MT:
+		snd_runtime_check(snd_i2c_sendbytes(ice->cs8404, &bits, 1) == 1, snd_i2c_unlock(ice->i2c); return);
+		break;
+	case ICE1712_SUBDEVICE_EWS88D:
+		snd_runtime_check(snd_i2c_readbytes(ice->i2cdevs[0], bytes, 2) == 2, snd_i2c_unlock(ice->i2c); return);
+		if (bits != bytes[1]) {
+			bytes[1] = bits;
+			snd_runtime_check(snd_i2c_readbytes(ice->i2cdevs[0], bytes, 2) == 2, snd_i2c_unlock(ice->i2c); return);
+		}
+		break;
+	}
+	snd_i2c_unlock(ice->i2c);
+}
+
+/*
+ */
+
+static void ews88_spdif_default_get(ice1712_t *ice, snd_ctl_elem_value_t * ucontrol)
+{
+	snd_cs8404_decode_spdif_bits(&ucontrol->value.iec958, ice->spdif.cs8403_bits);
+}
+
+static int ews88_spdif_default_put(ice1712_t *ice, snd_ctl_elem_value_t * ucontrol)
+{
+	unsigned long flags;
+	unsigned int val;
+	int change;
+
+	val = snd_cs8404_encode_spdif_bits(&ucontrol->value.iec958);
+	spin_lock_irqsave(&ice->reg_lock, flags);
+	change = ice->spdif.cs8403_bits != val;
+	ice->spdif.cs8403_bits = val;
+	if (change && ice->playback_pro_substream == NULL) {
+		spin_unlock_irqrestore(&ice->reg_lock, flags);
+		snd_ice1712_ews_cs8404_spdif_write(ice, val);
+	} else {
+		spin_unlock_irqrestore(&ice->reg_lock, flags);
+	}
+	return change;
+}
+
+static void ews88_spdif_stream_get(ice1712_t *ice, snd_ctl_elem_value_t * ucontrol)
+{
+	snd_cs8404_decode_spdif_bits(&ucontrol->value.iec958, ice->spdif.cs8403_stream_bits);
+}
+
+static int ews88_spdif_stream_put(ice1712_t *ice, snd_ctl_elem_value_t * ucontrol)
+{
+	unsigned long flags;
+	unsigned int val;
+	int change;
+
+	val = snd_cs8404_encode_spdif_bits(&ucontrol->value.iec958);
+	spin_lock_irqsave(&ice->reg_lock, flags);
+	change = ice->spdif.cs8403_stream_bits != val;
+	ice->spdif.cs8403_stream_bits = val;
+	if (change && ice->playback_pro_substream != NULL) {
+		spin_unlock_irqrestore(&ice->reg_lock, flags);
+		snd_ice1712_ews_cs8404_spdif_write(ice, val);
+	} else {
+		spin_unlock_irqrestore(&ice->reg_lock, flags);
+	}
+	return change;
+}
+
+
+/* open callback */
+static void ews88_open_spdif(ice1712_t *ice, snd_pcm_substream_t * substream)
+{
+	ice->spdif.cs8403_stream_bits = ice->spdif.cs8403_bits;
+}
+
+/* set up SPDIF for EWS88MT / EWS88D */
+static void ews88_setup_spdif(ice1712_t *ice, snd_pcm_substream_t * substream)
+{
+	unsigned long flags;
+	unsigned char tmp;
+	int change;
+
+	spin_lock_irqsave(&ice->reg_lock, flags);
+	tmp = ice->spdif.cs8403_stream_bits;
+	if (tmp & 0x10)		/* consumer */
+		tmp &= (tmp & 0x01) ? ~0x06 : ~0x60;
+	switch (substream->runtime->rate) {
+	case 32000: tmp |= (tmp & 0x01) ? 0x02 : 0x00; break;
+	case 44100: tmp |= (tmp & 0x01) ? 0x06 : 0x40; break;
+	case 48000: tmp |= (tmp & 0x01) ? 0x04 : 0x20; break;
+	default: tmp |= (tmp & 0x01) ? 0x06 : 0x40; break;
+	}
+	change = ice->spdif.cs8403_stream_bits != tmp;
+	ice->spdif.cs8403_stream_bits = tmp;
+	spin_unlock_irqrestore(&ice->reg_lock, flags);
+	if (change)
+		snd_ctl_notify(ice->card, SNDRV_CTL_EVENT_MASK_VALUE, &ice->spdif.stream_ctl->id);
+	snd_ice1712_ews_cs8404_spdif_write(ice, tmp);
+}
+
+
+/*
+ * initialize the chip
+ */
+
+static int __devinit snd_ice1712_ews_init(ice1712_t *ice)
+{
+	int err;
+	ak4524_t *ak;
+
+	/* set the analog DACs */
+	switch (ice->eeprom.subvendor) {
+	case ICE1712_SUBDEVICE_EWX2496:
+		ice->num_total_dacs = 2;
+		break;	
+	case ICE1712_SUBDEVICE_EWS88MT:
+		ice->num_total_dacs = 8;
+		break;
+	case ICE1712_SUBDEVICE_EWS88D:
+		break; /* no analog */
+	case ICE1712_SUBDEVICE_DMX6FIRE:
+		ice->num_total_dacs = 6;
+		break;
+	}
+
+	/* create i2c */
+	if ((err = snd_i2c_bus_create(ice->card, "ICE1712 GPIO 1", NULL, &ice->i2c)) < 0) {
+		snd_printk("unable to create I2C bus\n");
+		return err;
+	}
+	ice->i2c->private_data = ice;
+	ice->i2c->hw_ops.bit = &snd_ice1712_ewx_cs8427_bit_ops;
+
+	/* create i2c devices */
+	switch (ice->eeprom.subvendor) {
+	case ICE1712_SUBDEVICE_DMX6FIRE:
+		if ((err = snd_i2c_device_create(ice->i2c, "PCF9554", ICE1712_6FIRE_PCF9554_ADDR, &ice->i2cdevs[0])) < 0) {
+			snd_printk("PCF9554 initialization failed\n");
+			return err;
+		}
+		break;
+	case ICE1712_SUBDEVICE_EWS88MT:
+		if ((err = snd_i2c_device_create(ice->i2c, "CS8404", ICE1712_EWS88MT_CS8404_ADDR, &ice->cs8404)) < 0)
+			return err;
+		if ((err = snd_i2c_device_create(ice->i2c, "PCF8574 (1st)", ICE1712_EWS88MT_INPUT_ADDR, &ice->i2cdevs[0])) < 0)
+			return err;
+		if ((err = snd_i2c_device_create(ice->i2c, "PCF8574 (2nd)", ICE1712_EWS88MT_OUTPUT_ADDR, &ice->i2cdevs[1])) < 0)
+			return err;
+		/* Check if the front module is connected */
+		if ((err = snd_ice1712_ews88mt_chip_select(ice, 0x0f)) < 0)
+			return err;
+		break;
+	case ICE1712_SUBDEVICE_EWS88D:
+		if ((err = snd_i2c_device_create(ice->i2c, "PCF8575", ICE1712_EWS88D_PCF_ADDR, &ice->i2cdevs[0])) < 0)
+			return err;
+		break;
+	}
+
+	/* set up SPDIF interface */
+	switch (ice->eeprom.subvendor) {
+	case ICE1712_SUBDEVICE_EWX2496:
+		if ((err = snd_ice1712_init_cs8427(ice, CS8427_BASE_ADDR)) < 0)
+			return err;
+		break;
+#if 0 // XXX not working...
+	case ICE1712_SUBDEVICE_DMX6FIRE:
+		if ((err = snd_ice1712_init_cs8427(ice, ICE1712_6FIRE_CS8427_ADDR)) < 0)
+			return err;
+#endif
+	case ICE1712_SUBDEVICE_EWS88MT:
+	case ICE1712_SUBDEVICE_EWS88D:
+		/* set up CS8404 */
+		ice->spdif.ops.open = ews88_open_spdif;
+		ice->spdif.ops.setup = ews88_setup_spdif;
+		ice->spdif.ops.default_get = ews88_spdif_default_get;
+		ice->spdif.ops.default_put = ews88_spdif_default_put;
+		ice->spdif.ops.stream_get = ews88_spdif_stream_get;
+		ice->spdif.ops.stream_put = ews88_spdif_stream_put;
+		/* Set spdif defaults */
+		snd_ice1712_ews_cs8404_spdif_write(ice, ice->spdif.cs8403_bits);
+		break;
+	}
+
+	/* analog section */
+	ak = &ice->ak4524;
+	switch (ice->eeprom.subvendor) {
+	case ICE1712_SUBDEVICE_EWS88MT:
+		ak->num_adcs = ak->num_dacs = 8;
+		ak->cif = 1; /* CIF high */
+		ak->data_mask = ICE1712_EWS88_SERIAL_DATA;
+		ak->clk_mask = ICE1712_EWS88_SERIAL_CLOCK;
+		ak->codecs_mask = 0; /* no chip select on gpio */
+		ak->add_flags = ICE1712_EWS88_RW; /* set rw bit high */
+		ak->ops.start = ews88mt_ak4524_start;
+		ak->ops.stop = ews88mt_ak4524_stop;
+		snd_ice1712_ak4524_init(ice);
+		break;
+	case ICE1712_SUBDEVICE_EWX2496:
+		ak->num_adcs = ak->num_dacs = 2;
+		ak->cif = 1; /* CIF high */
+		ak->data_mask = ICE1712_EWS88_SERIAL_DATA;
+		ak->clk_mask = ICE1712_EWS88_SERIAL_CLOCK;
+		ak->codecs_mask = ICE1712_EWX2496_AK4524_CS;
+		ak->add_flags = ICE1712_EWS88_RW; /* set rw bit high */
+		ak->ops.start = ewx2496_ak4524_start;
+		snd_ice1712_ak4524_init(ice);
+		break;
+	case ICE1712_SUBDEVICE_DMX6FIRE:
+		ak->num_adcs = ak->num_dacs = 6;
+		ak->cif = 1; /* CIF high */
+		ak->data_mask = ICE1712_6FIRE_SERIAL_DATA;
+		ak->clk_mask = ICE1712_6FIRE_SERIAL_CLOCK;
+		ak->codecs_mask = 0; /* set later */
+		ak->add_flags = ICE1712_6FIRE_RW; /* set rw bit high */
+		ak->ops.start = dmx6fire_ak4524_start;
+		snd_ice1712_ak4524_init(ice);
+		break;
+	}
+
+	return 0;
+}
+
+/*
+ * EWX 24/96 specific controls
+ */
+
+/* i/o sensitivity - this callback is shared among other devices, too */
+static int snd_ice1712_ewx_io_sense_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo){
+
+	static char *texts[2] = {
+		"+4dBu", "-10dBV",
+	};
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 1;
+	uinfo->value.enumerated.items = 2;
+	if (uinfo->value.enumerated.item >= 2)
+		uinfo->value.enumerated.item = 1;
+	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+	return 0;
+}
+
+static int snd_ice1712_ewx_io_sense_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	unsigned char mask = kcontrol->private_value & 0xff;
+	unsigned char saved[2];
+	
+	snd_ice1712_save_gpio_status(ice, saved);
+	ucontrol->value.enumerated.item[0] = snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA) & mask ? 1 : 0;
+	snd_ice1712_restore_gpio_status(ice, saved);
+	return 0;
+}
+
+static int snd_ice1712_ewx_io_sense_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	unsigned char mask = kcontrol->private_value & 0xff;
+	unsigned char saved[2];
+	int val, nval;
+
+	if (kcontrol->private_value & (1 << 31))
+		return -EPERM;
+	nval = ucontrol->value.enumerated.item[0] ? mask : 0;
+	snd_ice1712_save_gpio_status(ice, saved);
+	val = snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA);
+	nval |= val & ~mask;
+	snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, nval);
+	snd_ice1712_restore_gpio_status(ice, saved);
+	return val != nval;
+}
+
+static snd_kcontrol_new_t snd_ice1712_ewx2496_controls[] __devinitdata = {
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Input Sensitivity Switch",
+		.info = snd_ice1712_ewx_io_sense_info,
+		.get = snd_ice1712_ewx_io_sense_get,
+		.put = snd_ice1712_ewx_io_sense_put,
+		.private_value = ICE1712_EWX2496_AIN_SEL,
+	},
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Output Sensitivity Switch",
+		.info = snd_ice1712_ewx_io_sense_info,
+		.get = snd_ice1712_ewx_io_sense_get,
+		.put = snd_ice1712_ewx_io_sense_put,
+		.private_value = ICE1712_EWX2496_AOUT_SEL,
+	},
+};
+
+
+/*
+ * EWS88MT specific controls
+ */
+/* analog output sensitivity;; address 0x48 bit 6 */
+static int snd_ice1712_ews88mt_output_sense_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	unsigned char data;
+
+	snd_i2c_lock(ice->i2c);
+	if (snd_i2c_readbytes(ice->i2cdevs[1], &data, 1) != 1) {
+		snd_i2c_unlock(ice->i2c);
+		return -EIO;
+	}
+	snd_i2c_unlock(ice->i2c);
+	ucontrol->value.enumerated.item[0] = data & ICE1712_EWS88MT_OUTPUT_SENSE ? 1 : 0; /* high = -10dBV, low = +4dBu */
+	return 0;
+}
+
+/* analog output sensitivity;; address 0x48 bit 6 */
+static int snd_ice1712_ews88mt_output_sense_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	unsigned char data, ndata;
+
+	snd_i2c_lock(ice->i2c);
+	if (snd_i2c_readbytes(ice->i2cdevs[1], &data, 1) != 1) {
+		snd_i2c_unlock(ice->i2c);
+		return -EIO;
+	}
+	ndata = (data & ~ICE1712_EWS88MT_OUTPUT_SENSE) | (ucontrol->value.enumerated.item[0] ? ICE1712_EWS88MT_OUTPUT_SENSE : 0);
+	if (ndata != data && snd_i2c_sendbytes(ice->i2cdevs[1], &ndata, 1) != 1) {
+		snd_i2c_unlock(ice->i2c);
+		return -EIO;
+	}
+	snd_i2c_unlock(ice->i2c);
+	return ndata != data;
+}
+
+/* analog input sensitivity; address 0x46 */
+static int snd_ice1712_ews88mt_input_sense_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	int channel = kcontrol->id.index;
+	unsigned char data;
+
+	snd_assert(channel >= 0 && channel <= 7, return 0);
+	snd_i2c_lock(ice->i2c);
+	if (snd_i2c_readbytes(ice->i2cdevs[0], &data, 1) != 1) {
+		snd_i2c_unlock(ice->i2c);
+		return -EIO;
+	}
+	/* reversed; high = +4dBu, low = -10dBV */
+	ucontrol->value.enumerated.item[0] = data & (1 << channel) ? 0 : 1;
+	return 0;
+}
+
+/* analog output sensitivity; address 0x46 */
+static int snd_ice1712_ews88mt_input_sense_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	int channel = kcontrol->id.index;
+	unsigned char data, ndata;
+
+	snd_assert(channel >= 0 && channel <= 7, return 0);
+	snd_i2c_lock(ice->i2c);
+	if (snd_i2c_readbytes(ice->i2cdevs[0], &data, 1) != 1) {
+		snd_i2c_unlock(ice->i2c);
+		return -EIO;
+	}
+	ndata = (data & ~(1 << channel)) | (ucontrol->value.enumerated.item[0] ? 0 : (1 << channel));
+	if (ndata != data && snd_i2c_sendbytes(ice->i2cdevs[0], &ndata, 1) != 1) {
+		snd_i2c_unlock(ice->i2c);
+		return -EIO;
+	}
+	snd_i2c_unlock(ice->i2c);
+	return ndata != data;
+}
+
+static snd_kcontrol_new_t snd_ice1712_ews88mt_input_sense __devinitdata = {
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "Input Sensitivity Switch",
+	.info = snd_ice1712_ewx_io_sense_info,
+	.get = snd_ice1712_ews88mt_input_sense_get,
+	.put = snd_ice1712_ews88mt_input_sense_put,
+};
+
+static snd_kcontrol_new_t snd_ice1712_ews88mt_output_sense __devinitdata = {
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "Output Sensitivity Switch",
+	.info = snd_ice1712_ewx_io_sense_info,
+	.get = snd_ice1712_ews88mt_output_sense_get,
+	.put = snd_ice1712_ews88mt_output_sense_put,
+};
+
+
+/*
+ * EWS88D specific controls
+ */
+
+static int snd_ice1712_ews88d_control_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_ice1712_ews88d_control_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	int shift = kcontrol->private_value & 0xff;
+	int invert = (kcontrol->private_value >> 8) & 1;
+	unsigned char data[2];
+	
+	snd_i2c_lock(ice->i2c);
+	if (snd_i2c_readbytes(ice->i2cdevs[0], data, 2) != 2) {
+		snd_i2c_unlock(ice->i2c);
+		return -EIO;
+	}
+	snd_i2c_unlock(ice->i2c);
+	data[0] = (data[shift >> 3] >> (shift & 7)) & 0x01;
+	if (invert)
+		data[0] ^= 0x01;
+	ucontrol->value.integer.value[0] = data[0];
+	return 0;
+}
+
+static int snd_ice1712_ews88d_control_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	int shift = kcontrol->private_value & 0xff;
+	int invert = (kcontrol->private_value >> 8) & 1;
+	unsigned char data[2], ndata[2];
+	int change;
+
+	snd_i2c_lock(ice->i2c);
+	if (snd_i2c_readbytes(ice->i2cdevs[0], data, 2) != 2) {
+		snd_i2c_unlock(ice->i2c);
+		return -EIO;
+	}
+	ndata[shift >> 3] = data[shift >> 3] & ~(1 << (shift & 7));
+	if (invert) {
+		if (! ucontrol->value.integer.value[0])
+			ndata[shift >> 3] |= (1 << (shift & 7));
+	} else {
+		if (ucontrol->value.integer.value[0])
+			ndata[shift >> 3] |= (1 << (shift & 7));
+	}
+	change = (data[shift >> 3] != ndata[shift >> 3]);
+	if (change && snd_i2c_sendbytes(ice->i2cdevs[0], data, 2) != 2) {
+		snd_i2c_unlock(ice->i2c);
+		return -EIO;
+	}
+	snd_i2c_unlock(ice->i2c);
+	return change;
+}
+
+#define EWS88D_CONTROL(xiface, xname, xshift, xinvert, xaccess) \
+{ .iface = xiface,\
+  .name = xname,\
+  .access = xaccess,\
+  .info = snd_ice1712_ews88d_control_info,\
+  .get = snd_ice1712_ews88d_control_get,\
+  .put = snd_ice1712_ews88d_control_put,\
+  .private_value = xshift | (xinvert << 8),\
+}
+
+static snd_kcontrol_new_t snd_ice1712_ews88d_controls[] __devinitdata = {
+	EWS88D_CONTROL(SNDRV_CTL_ELEM_IFACE_MIXER, "IEC958 Input Optical", 0, 1, 0), /* inverted */
+	EWS88D_CONTROL(SNDRV_CTL_ELEM_IFACE_MIXER, "ADAT Output Optical", 1, 0, 0),
+	EWS88D_CONTROL(SNDRV_CTL_ELEM_IFACE_MIXER, "ADAT External Master Clock", 2, 0, 0),
+	EWS88D_CONTROL(SNDRV_CTL_ELEM_IFACE_MIXER, "Enable ADAT", 3, 0, 0),
+	EWS88D_CONTROL(SNDRV_CTL_ELEM_IFACE_MIXER, "ADAT Through", 4, 1, 0),
+};
+
+
+/*
+ * DMX 6Fire specific controls
+ */
+
+#define PCF9554_REG_INPUT	0
+#define PCF9554_REG_OUTPUT	1
+#define PCF9554_REG_POLARITY	2
+#define PCF9554_REG_CONFIG	3
+
+static int snd_ice1712_6fire_read_pca(ice1712_t *ice, unsigned char reg)
+{
+	unsigned char byte;
+	snd_i2c_lock(ice->i2c);
+	byte = reg;
+	snd_i2c_sendbytes(ice->i2cdevs[0], &byte, 1);
+	byte = 0;
+	if (snd_i2c_readbytes(ice->i2cdevs[0], &byte, 1) != 1) {
+		snd_i2c_unlock(ice->i2c);
+		printk("cannot read pca\n");
+		return -EIO;
+	}
+	snd_i2c_unlock(ice->i2c);
+	return byte;
+}
+
+static int snd_ice1712_6fire_write_pca(ice1712_t *ice, unsigned char reg, unsigned char data)
+{
+	unsigned char bytes[2];
+	snd_i2c_lock(ice->i2c);
+	bytes[0] = reg;
+	bytes[1] = data;
+	if (snd_i2c_sendbytes(ice->i2cdevs[0], bytes, 2) != 2) {
+		snd_i2c_unlock(ice->i2c);
+		return -EIO;
+	}
+	snd_i2c_unlock(ice->i2c);
+	return 0;
+}
+
+static int snd_ice1712_6fire_control_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_ice1712_6fire_control_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	int shift = kcontrol->private_value & 0xff;
+	int invert = (kcontrol->private_value >> 8) & 1;
+	int data;
+	
+	if ((data = snd_ice1712_6fire_read_pca(ice, PCF9554_REG_OUTPUT)) < 0)
+		return data;
+	data = (data >> shift) & 1;
+	if (invert)
+		data ^= 1;
+	ucontrol->value.integer.value[0] = data;
+	return 0;
+}
+
+static int snd_ice1712_6fire_control_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	int shift = kcontrol->private_value & 0xff;
+	int invert = (kcontrol->private_value >> 8) & 1;
+	int data, ndata;
+	
+	if ((data = snd_ice1712_6fire_read_pca(ice, PCF9554_REG_OUTPUT)) < 0)
+		return data;
+	ndata = data & ~(1 << shift);
+	if (ucontrol->value.integer.value[0])
+		ndata |= (1 << shift);
+	if (invert)
+		ndata ^= (1 << shift);
+	if (data != ndata) {
+		snd_ice1712_6fire_write_pca(ice, PCF9554_REG_OUTPUT, (unsigned char)ndata);
+		return 1;
+	}
+	return 0;
+}
+
+static int snd_ice1712_6fire_select_input_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+	static char *texts[4] = {
+		"Internal", "Front Input", "Rear Input", "Wave Table"
+	};
+	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 = 1;
+	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+	return 0;
+}
+     
+static int snd_ice1712_6fire_select_input_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	int data;
+	
+	if ((data = snd_ice1712_6fire_read_pca(ice, PCF9554_REG_OUTPUT)) < 0)
+		return data;
+	ucontrol->value.integer.value[0] = data & 3;
+	return 0;
+}
+
+static int snd_ice1712_6fire_select_input_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	int data, ndata;
+	
+	if ((data = snd_ice1712_6fire_read_pca(ice, PCF9554_REG_OUTPUT)) < 0)
+		return data;
+	ndata = data & ~3;
+	ndata |= (ucontrol->value.integer.value[0] & 3);
+	if (data != ndata) {
+		snd_ice1712_6fire_write_pca(ice, PCF9554_REG_OUTPUT, (unsigned char)ndata);
+		return 1;
+	}
+	return 0;
+}
+
+
+#define DMX6FIRE_CONTROL(xname, xshift, xinvert) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,\
+  .name = xname,\
+  .info = snd_ice1712_6fire_control_info,\
+  .get = snd_ice1712_6fire_control_get,\
+  .put = snd_ice1712_6fire_control_put,\
+  .private_value = xshift | (xinvert << 8),\
+}
+
+static snd_kcontrol_new_t snd_ice1712_6fire_controls[] __devinitdata = {
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "Analog Input Select",
+		.info = snd_ice1712_6fire_select_input_info,
+		.get = snd_ice1712_6fire_select_input_get,
+		.put = snd_ice1712_6fire_select_input_put,
+	},
+	DMX6FIRE_CONTROL("Front Digital Input Switch", 2, 0),
+	// DMX6FIRE_CONTROL("Master Clock Select", 3, 0),
+	DMX6FIRE_CONTROL("Optical Digital Input Switch", 4, 0),
+	DMX6FIRE_CONTROL("Phono Analog Input Switch", 5, 0),
+	DMX6FIRE_CONTROL("Breakbox LED", 6, 0),
+};
+
+
+static int __devinit snd_ice1712_ews_add_controls(ice1712_t *ice)
+{
+	int err, idx;
+	snd_kcontrol_t *kctl;
+	
+	/* all terratec cards have spdif */
+	err = snd_ice1712_spdif_build_controls(ice);
+	if (err < 0)
+		return err;
+
+	/* ak4524 controls */
+	switch (ice->eeprom.subvendor) {
+	case ICE1712_SUBDEVICE_EWX2496:
+	case ICE1712_SUBDEVICE_EWS88MT:
+	case ICE1712_SUBDEVICE_DMX6FIRE:
+		err = snd_ice1712_ak4524_build_controls(ice);
+		if (err < 0)
+			return err;
+		break;
+	}
+
+	/* card specific controls */
+	switch (ice->eeprom.subvendor) {
+	case ICE1712_SUBDEVICE_EWX2496:
+		for (idx = 0; idx < sizeof(snd_ice1712_ewx2496_controls)/sizeof(snd_ice1712_ewx2496_controls[0]); idx++) {
+			err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_ewx2496_controls[idx], ice));
+			if (err < 0)
+				return err;
+		}
+		break;
+	case ICE1712_SUBDEVICE_EWS88MT:
+		for (idx = 0; idx < 8; idx++) {
+			kctl = snd_ctl_new1(&snd_ice1712_ews88mt_input_sense, ice);
+			kctl->id.index = idx;
+			err = snd_ctl_add(ice->card, kctl);
+			if (err < 0)
+				return err;
+		}
+		err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_ews88mt_output_sense, ice));
+		if (err < 0)
+			return err;
+		break;
+	case ICE1712_SUBDEVICE_EWS88D:
+		for (idx = 0; idx < sizeof(snd_ice1712_ews88d_controls)/sizeof(snd_ice1712_ews88d_controls[0]); idx++) {
+			err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_ews88d_controls[idx], ice));
+			if (err < 0)
+				return err;
+		}
+		break;
+	case ICE1712_SUBDEVICE_DMX6FIRE:
+		for (idx = 0; idx < sizeof(snd_ice1712_6fire_controls)/sizeof(snd_ice1712_6fire_controls[0]); idx++) {
+			err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_6fire_controls[idx], ice));
+			if (err < 0)
+				return err;
+		}
+		break;
+	}
+	return 0;
+}
+
+
+/* entry point */
+struct snd_ice1712_card_info snd_ice1712_ews_cards[] __devinitdata = {
+	{
+		ICE1712_SUBDEVICE_EWX2496,
+		"TerraTec EWX 24/96",
+		snd_ice1712_ews_init,
+		snd_ice1712_ews_add_controls,
+	},
+	{
+		ICE1712_SUBDEVICE_EWS88MT,
+		"TerraTec EWS 88MT",
+		snd_ice1712_ews_init,
+		snd_ice1712_ews_add_controls,
+	},
+	{
+		ICE1712_SUBDEVICE_EWS88D,
+		"TerraTec EWS 88D",
+		snd_ice1712_ews_init,
+		snd_ice1712_ews_add_controls,
+	},
+	{
+		ICE1712_SUBDEVICE_DMX6FIRE,
+		"TerraTec DMX 6Fire",
+		snd_ice1712_ews_init,
+		snd_ice1712_ews_add_controls,
+	},
+	{ } /* terminator */
+};
diff -Nru a/sound/pci/ice1712/ews.h b/sound/pci/ice1712/ews.h
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/sound/pci/ice1712/ews.h	Mon Oct  7 11:26:06 2002
@@ -0,0 +1,81 @@
+#ifndef __SOUND_EWS_H
+#define __SOUND_EWS_H
+
+/*
+ *   ALSA driver for ICEnsemble ICE1712 (Envy24)
+ *
+ *   Lowlevel functions for Terratec EWS88MT/D, EWX24/96, DMX 6Fire
+ *
+ *	Copyright (c) 2000 Jaroslav Kysela <perex@suse.cz>
+ *                    2002 Takashi Iwai <tiwai@suse.de>
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */      
+
+#define EWS_DEVICE_DESC \
+		"{TerraTec,EWX 24/96},"\
+		"{TerraTec,EWS 88MT},"\
+		"{TerraTec,EWS 88D},"\
+		"{TerraTec,DMX 6Fire},"
+
+#define ICE1712_SUBDEVICE_EWX2496	0x3b153011
+#define ICE1712_SUBDEVICE_EWS88MT	0x3b151511
+#define ICE1712_SUBDEVICE_EWS88D	0x3b152b11
+#define ICE1712_SUBDEVICE_DMX6FIRE	0x3b153811
+
+/* entry point */
+extern struct snd_ice1712_card_info snd_ice1712_ews_cards[];
+
+
+/* TerraTec EWX 24/96 configuration definitions */
+
+#define ICE1712_EWX2496_AK4524_CS	0x01	/* AK4524 chip select; low = active */
+#define ICE1712_EWX2496_AIN_SEL		0x02	/* input sensitivity switch; high = louder */
+#define ICE1712_EWX2496_AOUT_SEL	0x04	/* output sensitivity switch; high = louder */
+#define ICE1712_EWX2496_RW		0x08	/* read/write switch for i2c; high = write  */
+#define ICE1712_EWX2496_SERIAL_DATA	0x10	/* i2c & ak4524 data */
+#define ICE1712_EWX2496_SERIAL_CLOCK	0x20	/* i2c & ak4524 clock */
+#define ICE1712_EWX2496_TX2		0x40	/* MIDI2 (not used) */
+#define ICE1712_EWX2496_RX2		0x80	/* MIDI2 (not used) */
+
+/* TerraTec EWS 88MT/D configuration definitions */
+/* RW, SDA snd SCLK are identical with EWX24/96 */
+#define ICE1712_EWS88_CS8414_RATE	0x07	/* CS8414 sample rate: gpio 0-2 */
+#define ICE1712_EWS88_RW		0x08	/* read/write switch for i2c; high = write  */
+#define ICE1712_EWS88_SERIAL_DATA	0x10	/* i2c & ak4524 data */
+#define ICE1712_EWS88_SERIAL_CLOCK	0x20	/* i2c & ak4524 clock */
+#define ICE1712_EWS88_TX2		0x40	/* MIDI2 (only on 88D) */
+#define ICE1712_EWS88_RX2		0x80	/* MIDI2 (only on 88D) */
+
+/* i2c address */
+#define ICE1712_EWS88MT_CS8404_ADDR	(0x40>>1)
+#define ICE1712_EWS88MT_INPUT_ADDR	(0x46>>1)
+#define ICE1712_EWS88MT_OUTPUT_ADDR	(0x48>>1)
+#define ICE1712_EWS88MT_OUTPUT_SENSE	0x40	/* mask */
+#define ICE1712_EWS88D_PCF_ADDR		(0x40>>1)
+
+/* TerraTec DMX 6Fire configuration definitions */
+#define ICE1712_6FIRE_AK4524_CS_MASK	0x07	/* AK4524 chip select #1-#3 */
+#define ICE1712_6FIRE_RW		0x08	/* read/write switch for i2c; high = write  */
+#define ICE1712_6FIRE_SERIAL_DATA	0x10	/* i2c & ak4524 data */
+#define ICE1712_6FIRE_SERIAL_CLOCK	0x20	/* i2c & ak4524 clock */
+#define ICE1712_6FIRE_TX2		0x40	/* MIDI2 */
+#define ICE1712_6FIRE_RX2		0x80	/* MIDI2 */
+
+#define ICE1712_6FIRE_PCF9554_ADDR	(0x40>>1)
+#define ICE1712_6FIRE_CS8427_ADDR	(0x22>>1)
+
+#endif /* __SOUND_EWS_H */
diff -Nru a/sound/pci/ice1712/hoontech.c b/sound/pci/ice1712/hoontech.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/sound/pci/ice1712/hoontech.c	Mon Oct  7 11:25:40 2002
@@ -0,0 +1,223 @@
+/*
+ *   ALSA driver for ICEnsemble ICE1712 (Envy24)
+ *
+ *   Lowlevel functions for Hoontech STDSP24
+ *
+ *	Copyright (c) 2000 Jaroslav Kysela <perex@suse.cz>
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */      
+
+#include <sound/driver.h>
+#include <asm/io.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+
+#include "ice1712.h"
+#include "hoontech.h"
+
+
+static void __devinit snd_ice1712_stdsp24_gpio_write(ice1712_t *ice, unsigned char byte)
+{
+	byte |= ICE1712_STDSP24_CLOCK_BIT;
+	udelay(100);
+	snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, byte);
+	byte &= ~ICE1712_STDSP24_CLOCK_BIT;
+	udelay(100);
+	snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, byte);
+	byte |= ICE1712_STDSP24_CLOCK_BIT;
+	udelay(100);
+	snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, byte);
+}
+
+static void __devinit snd_ice1712_stdsp24_darear(ice1712_t *ice, int activate)
+{
+	down(&ice->gpio_mutex);
+	ICE1712_STDSP24_0_DAREAR(ice->hoontech_boxbits, activate);
+	snd_ice1712_stdsp24_gpio_write(ice, ice->hoontech_boxbits[0]);
+	up(&ice->gpio_mutex);
+}
+
+static void __devinit snd_ice1712_stdsp24_mute(ice1712_t *ice, int activate)
+{
+	down(&ice->gpio_mutex);
+	ICE1712_STDSP24_3_MUTE(ice->hoontech_boxbits, activate);
+	snd_ice1712_stdsp24_gpio_write(ice, ice->hoontech_boxbits[3]);
+	up(&ice->gpio_mutex);
+}
+
+static void __devinit snd_ice1712_stdsp24_insel(ice1712_t *ice, int activate)
+{
+	down(&ice->gpio_mutex);
+	ICE1712_STDSP24_3_INSEL(ice->hoontech_boxbits, activate);
+	snd_ice1712_stdsp24_gpio_write(ice, ice->hoontech_boxbits[3]);
+	up(&ice->gpio_mutex);
+}
+
+static void __devinit snd_ice1712_stdsp24_box_channel(ice1712_t *ice, int box, int chn, int activate)
+{
+	down(&ice->gpio_mutex);
+
+	/* select box */
+	ICE1712_STDSP24_0_BOX(ice->hoontech_boxbits, box);
+	snd_ice1712_stdsp24_gpio_write(ice, ice->hoontech_boxbits[0]);
+
+	/* prepare for write */
+	if (chn == 3)
+		ICE1712_STDSP24_2_CHN4(ice->hoontech_boxbits, 0);
+	ICE1712_STDSP24_2_MIDI1(ice->hoontech_boxbits, activate);
+	snd_ice1712_stdsp24_gpio_write(ice, ice->hoontech_boxbits[2]);
+
+	ICE1712_STDSP24_1_CHN1(ice->hoontech_boxbits, 1);
+	ICE1712_STDSP24_1_CHN2(ice->hoontech_boxbits, 1);
+	ICE1712_STDSP24_1_CHN3(ice->hoontech_boxbits, 1);
+	ICE1712_STDSP24_2_CHN4(ice->hoontech_boxbits, 1);
+	snd_ice1712_stdsp24_gpio_write(ice, ice->hoontech_boxbits[1]);
+	snd_ice1712_stdsp24_gpio_write(ice, ice->hoontech_boxbits[2]);
+	udelay(100);
+	if (chn == 3) {
+		ICE1712_STDSP24_2_CHN4(ice->hoontech_boxbits, 0);
+		snd_ice1712_stdsp24_gpio_write(ice, ice->hoontech_boxbits[2]);
+	} else {
+		switch (chn) {
+		case 0:	ICE1712_STDSP24_1_CHN1(ice->hoontech_boxbits, 0); break;
+		case 1:	ICE1712_STDSP24_1_CHN2(ice->hoontech_boxbits, 0); break;
+		case 2:	ICE1712_STDSP24_1_CHN3(ice->hoontech_boxbits, 0); break;
+		}
+		snd_ice1712_stdsp24_gpio_write(ice, ice->hoontech_boxbits[1]);
+	}
+	udelay(100);
+	ICE1712_STDSP24_1_CHN1(ice->hoontech_boxbits, 1);
+	ICE1712_STDSP24_1_CHN2(ice->hoontech_boxbits, 1);
+	ICE1712_STDSP24_1_CHN3(ice->hoontech_boxbits, 1);
+	ICE1712_STDSP24_2_CHN4(ice->hoontech_boxbits, 1);
+	snd_ice1712_stdsp24_gpio_write(ice, ice->hoontech_boxbits[1]);
+	snd_ice1712_stdsp24_gpio_write(ice, ice->hoontech_boxbits[2]);
+	udelay(100);
+
+	ICE1712_STDSP24_2_MIDI1(ice->hoontech_boxbits, 0);
+	snd_ice1712_stdsp24_gpio_write(ice, ice->hoontech_boxbits[2]);
+
+	up(&ice->gpio_mutex);
+}
+
+static void __devinit snd_ice1712_stdsp24_box_midi(ice1712_t *ice, int box, int master, int slave)
+{
+	down(&ice->gpio_mutex);
+
+	/* select box */
+	ICE1712_STDSP24_0_BOX(ice->hoontech_boxbits, box);
+	snd_ice1712_stdsp24_gpio_write(ice, ice->hoontech_boxbits[0]);
+
+	ICE1712_STDSP24_2_MIDIIN(ice->hoontech_boxbits, 1);
+	ICE1712_STDSP24_2_MIDI1(ice->hoontech_boxbits, master);
+	snd_ice1712_stdsp24_gpio_write(ice, ice->hoontech_boxbits[2]);
+
+	udelay(100);
+	
+	ICE1712_STDSP24_2_MIDIIN(ice->hoontech_boxbits, 0);
+	snd_ice1712_stdsp24_gpio_write(ice, ice->hoontech_boxbits[2]);
+	
+	udelay(100);
+	
+	ICE1712_STDSP24_2_MIDIIN(ice->hoontech_boxbits, 1);
+	snd_ice1712_stdsp24_gpio_write(ice, ice->hoontech_boxbits[2]);
+
+	udelay(100);
+
+	/* MIDI2 is direct */
+	ICE1712_STDSP24_3_MIDI2(ice->hoontech_boxbits, slave);
+	snd_ice1712_stdsp24_gpio_write(ice, ice->hoontech_boxbits[3]);
+
+	up(&ice->gpio_mutex);
+}
+
+static int __devinit snd_ice1712_hoontech_init(ice1712_t *ice)
+{
+	int box, chn;
+
+	ice->num_total_dacs = 8;
+
+	ice->hoontech_boxbits[0] = 
+	ice->hoontech_boxbits[1] = 
+	ice->hoontech_boxbits[2] = 
+	ice->hoontech_boxbits[3] = 0;	/* should be already */
+
+	ICE1712_STDSP24_SET_ADDR(ice->hoontech_boxbits, 0);
+	ICE1712_STDSP24_CLOCK(ice->hoontech_boxbits, 0, 1);
+	ICE1712_STDSP24_0_BOX(ice->hoontech_boxbits, 0);
+	ICE1712_STDSP24_0_DAREAR(ice->hoontech_boxbits, 0);
+
+	ICE1712_STDSP24_SET_ADDR(ice->hoontech_boxbits, 1);
+	ICE1712_STDSP24_CLOCK(ice->hoontech_boxbits, 1, 1);
+	ICE1712_STDSP24_1_CHN1(ice->hoontech_boxbits, 1);
+	ICE1712_STDSP24_1_CHN2(ice->hoontech_boxbits, 1);
+	ICE1712_STDSP24_1_CHN3(ice->hoontech_boxbits, 1);
+	
+	ICE1712_STDSP24_SET_ADDR(ice->hoontech_boxbits, 2);
+	ICE1712_STDSP24_CLOCK(ice->hoontech_boxbits, 2, 1);
+	ICE1712_STDSP24_2_CHN4(ice->hoontech_boxbits, 1);
+	ICE1712_STDSP24_2_MIDIIN(ice->hoontech_boxbits, 1);
+	ICE1712_STDSP24_2_MIDI1(ice->hoontech_boxbits, 0);
+
+	ICE1712_STDSP24_SET_ADDR(ice->hoontech_boxbits, 3);
+	ICE1712_STDSP24_CLOCK(ice->hoontech_boxbits, 3, 1);
+	ICE1712_STDSP24_3_MIDI2(ice->hoontech_boxbits, 0);
+	ICE1712_STDSP24_3_MUTE(ice->hoontech_boxbits, 1);
+	ICE1712_STDSP24_3_INSEL(ice->hoontech_boxbits, 0);
+
+	/* let's go - activate only functions in first box */
+	ice->hoontech_config = 0;
+			    /* ICE1712_STDSP24_MUTE |
+			       ICE1712_STDSP24_INSEL |
+			       ICE1712_STDSP24_DAREAR; */
+	ice->hoontech_boxconfig[0] = ICE1712_STDSP24_BOX_CHN1 |
+				     ICE1712_STDSP24_BOX_CHN2 |
+				     ICE1712_STDSP24_BOX_CHN3 |
+				     ICE1712_STDSP24_BOX_CHN4 |
+				     ICE1712_STDSP24_BOX_MIDI1 |
+				     ICE1712_STDSP24_BOX_MIDI2;
+	ice->hoontech_boxconfig[1] = 
+	ice->hoontech_boxconfig[2] = 
+	ice->hoontech_boxconfig[3] = 0;
+	snd_ice1712_stdsp24_darear(ice, (ice->hoontech_config & ICE1712_STDSP24_DAREAR) ? 1 : 0);
+	snd_ice1712_stdsp24_mute(ice, (ice->hoontech_config & ICE1712_STDSP24_MUTE) ? 1 : 0);
+	snd_ice1712_stdsp24_insel(ice, (ice->hoontech_config & ICE1712_STDSP24_INSEL) ? 1 : 0);
+	for (box = 0; box < 4; box++) {
+		for (chn = 0; chn < 4; chn++)
+			snd_ice1712_stdsp24_box_channel(ice, box, chn, (ice->hoontech_boxconfig[box] & (1 << chn)) ? 1 : 0);
+		snd_ice1712_stdsp24_box_midi(ice, box,
+				(ice->hoontech_boxconfig[box] & ICE1712_STDSP24_BOX_MIDI1) ? 1 : 0,
+				(ice->hoontech_boxconfig[box] & ICE1712_STDSP24_BOX_MIDI2) ? 1 : 0);
+	}
+
+	return 0;
+}
+
+
+/* entry point */
+struct snd_ice1712_card_info snd_ice1712_hoontech_cards[] __devinitdata = {
+	{
+		ICE1712_SUBDEVICE_STDSP24,
+		"Hoontech SoundTrack Audio DSP24",
+		snd_ice1712_hoontech_init,
+	},
+	{ } /* terminator */
+};
+
diff -Nru a/sound/pci/ice1712/hoontech.h b/sound/pci/ice1712/hoontech.h
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/sound/pci/ice1712/hoontech.h	Mon Oct  7 11:26:06 2002
@@ -0,0 +1,64 @@
+#ifndef __SOUND_HOONTECH_H
+#define __SOUND_HOONTECH_H
+
+/*
+ *   ALSA driver for ICEnsemble ICE1712 (Envy24)
+ *
+ *   Lowlevel functions for Hoontech STDSP24
+ *
+ *	Copyright (c) 2000 Jaroslav Kysela <perex@suse.cz>
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */      
+
+#define  HOONTECH_DEVICE_DESC 	       "{Hoontech SoundTrack DSP 24},"
+
+#define ICE1712_SUBDEVICE_STDSP24	0x12141217	/* Hoontech SoundTrack Audio DSP 24 */
+
+extern struct snd_ice1712_card_info snd_ice1712_hoontech_cards[];
+
+
+/* Hoontech SoundTrack Audio DSP 24 GPIO definitions */
+
+#define ICE1712_STDSP24_0_BOX(r, x)	r[0] = ((r[0] & ~3) | ((x)&3))
+#define ICE1712_STDSP24_0_DAREAR(r, x)	r[0] = ((r[0] & ~4) | (((x)&1)<<2))
+#define ICE1712_STDSP24_1_CHN1(r, x)	r[1] = ((r[1] & ~1) | ((x)&1))
+#define ICE1712_STDSP24_1_CHN2(r, x)	r[1] = ((r[1] & ~2) | (((x)&1)<<1))
+#define ICE1712_STDSP24_1_CHN3(r, x)	r[1] = ((r[1] & ~4) | (((x)&1)<<2))
+#define ICE1712_STDSP24_2_CHN4(r, x)	r[2] = ((r[2] & ~1) | ((x)&1))
+#define ICE1712_STDSP24_2_MIDIIN(r, x)	r[2] = ((r[2] & ~2) | (((x)&1)<<1))
+#define ICE1712_STDSP24_2_MIDI1(r, x)	r[2] = ((r[2] & ~4) | (((x)&1)<<2))
+#define ICE1712_STDSP24_3_MIDI2(r, x)	r[3] = ((r[3] & ~1) | ((x)&1))
+#define ICE1712_STDSP24_3_MUTE(r, x)	r[3] = ((r[3] & ~2) | (((x)&1)<<1))
+#define ICE1712_STDSP24_3_INSEL(r, x)	r[3] = ((r[3] & ~4) | (((x)&1)<<2))
+#define ICE1712_STDSP24_SET_ADDR(r, a)	r[a&3] = ((r[a&3] & ~0x18) | (((a)&3)<<3))
+#define ICE1712_STDSP24_CLOCK(r, a, c)	r[a&3] = ((r[a&3] & ~0x20) | (((c)&1)<<5))
+#define ICE1712_STDSP24_CLOCK_BIT	(1<<5)
+
+/* Hoontech SoundTrack Audio DSP 24 box configuration definitions */
+
+#define ICE1712_STDSP24_DAREAR		(1<<0)
+#define ICE1712_STDSP24_MUTE		(1<<1)
+#define ICE1712_STDSP24_INSEL		(1<<2)
+
+#define ICE1712_STDSP24_BOX_CHN1	(1<<0)	/* input channel 1 */
+#define ICE1712_STDSP24_BOX_CHN2	(1<<1)	/* input channel 2 */
+#define ICE1712_STDSP24_BOX_CHN3	(1<<2)	/* input channel 3 */
+#define ICE1712_STDSP24_BOX_CHN4	(1<<3)	/* input channel 4 */
+#define ICE1712_STDSP24_BOX_MIDI1	(1<<8)
+#define ICE1712_STDSP24_BOX_MIDI2	(1<<9)
+
+#endif /* __SOUND_HOONTECH_H */
diff -Nru a/sound/pci/ice1712/ice1712.c b/sound/pci/ice1712/ice1712.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/sound/pci/ice1712/ice1712.c	Mon Oct  7 11:25:41 2002
@@ -0,0 +1,2477 @@
+/*
+ *   ALSA driver for ICEnsemble ICE1712 (Envy24)
+ *
+ *	Copyright (c) 2000 Jaroslav Kysela <perex@suse.cz>
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */      
+
+/*
+  NOTES:
+  - spdif nonaudio consumer mode does not work (at least with my
+    Sony STR-DB830)
+*/
+
+/*
+ * Changes:
+ *
+ *  2002.09.09	Takashi Iwai <tiwai@suse.de>
+ *	split the code to several files.  each low-level routine
+ *	is stored in the local file and called from registration
+ *	function from card_info struct.
+ */
+
+
+#include <sound/driver.h>
+#include <asm/io.h>
+#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/cs8427.h>
+#include <sound/info.h>
+#include <sound/mpu401.h>
+#define SNDRV_GET_ID
+#include <sound/initval.h>
+
+#include <sound/asoundef.h>
+
+#include "ice1712.h"
+
+/* lowlevel routines */
+#include "delta.h"
+#include "ews.h"
+#include "hoontech.h"
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
+MODULE_DESCRIPTION("ICEnsemble ICE1712 (Envy24)");
+MODULE_LICENSE("GPL");
+MODULE_CLASSES("{sound}");
+MODULE_DEVICES("{"
+	       HOONTECH_DEVICE_DESC
+	       DELTA_DEVICE_DESC
+	       EWS_DEVICE_DESC
+		"{ICEnsemble,Generic ICE1712},"
+		"{ICEnsemble,Generic Envy24}}");
+
+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 */
+static int snd_omni[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = 0};	/* Delta44 & 66 Omni I/O support */
+
+MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_index, "Index value for ICE1712 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 ICE1712 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 ICE1712 soundcard.");
+MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC);
+MODULE_PARM(snd_omni, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_omni, "Enable Midiman M-Audio Delta Omni I/O support.");
+MODULE_PARM_SYNTAX(snd_omni, SNDRV_ENABLED "," SNDRV_ENABLE_DESC);
+
+#ifndef PCI_VENDOR_ID_ICE
+#define PCI_VENDOR_ID_ICE		0x1412
+#endif
+#ifndef PCI_DEVICE_ID_ICE_1712
+#define PCI_DEVICE_ID_ICE_1712		0x1712
+#endif
+
+static struct pci_device_id snd_ice1712_ids[] __devinitdata = {
+	{ PCI_VENDOR_ID_ICE, PCI_DEVICE_ID_ICE_1712, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, },   /* ICE1712 */
+	{ 0, }
+};
+
+MODULE_DEVICE_TABLE(pci, snd_ice1712_ids);
+
+static int snd_ice1712_build_pro_mixer(ice1712_t *ice);
+static int snd_ice1712_build_controls(ice1712_t *ice);
+
+/*
+ *  Basic I/O
+ */
+ 
+static inline void snd_ice1712_ds_write(ice1712_t * ice, u8 channel, u8 addr, u32 data)
+{
+	outb((channel << 4) | addr, ICEDS(ice, INDEX));
+	outl(data, ICEDS(ice, DATA));
+}
+
+static inline u32 snd_ice1712_ds_read(ice1712_t * ice, u8 channel, u8 addr)
+{
+	outb((channel << 4) | addr, ICEDS(ice, INDEX));
+	return inl(ICEDS(ice, DATA));
+}
+
+static void snd_ice1712_ac97_write(ac97_t *ac97,
+				   unsigned short reg,
+				   unsigned short val)
+{
+	ice1712_t *ice = (ice1712_t *)ac97->private_data;
+	int tm;
+	unsigned char old_cmd = 0;
+
+	for (tm = 0; tm < 0x10000; tm++) {
+		old_cmd = inb(ICEREG(ice, AC97_CMD));
+		if (old_cmd & (ICE1712_AC97_WRITE | ICE1712_AC97_READ))
+			continue;
+		if (!(old_cmd & ICE1712_AC97_READY))
+			continue;
+		break;
+	}
+	outb(reg, ICEREG(ice, AC97_INDEX));
+	outw(val, ICEREG(ice, AC97_DATA));
+	old_cmd &= ~(ICE1712_AC97_PBK_VSR | ICE1712_AC97_CAP_VSR);
+	outb(old_cmd | ICE1712_AC97_WRITE, ICEREG(ice, AC97_CMD));
+	for (tm = 0; tm < 0x10000; tm++)
+		if ((inb(ICEREG(ice, AC97_CMD)) & ICE1712_AC97_WRITE) == 0)
+			break;
+}
+
+static unsigned short snd_ice1712_ac97_read(ac97_t *ac97,
+					    unsigned short reg)
+{
+	ice1712_t *ice = (ice1712_t *)ac97->private_data;
+	int tm;
+	unsigned char old_cmd = 0;
+
+	for (tm = 0; tm < 0x10000; tm++) {
+		old_cmd = inb(ICEREG(ice, AC97_CMD));
+		if (old_cmd & (ICE1712_AC97_WRITE | ICE1712_AC97_READ))
+			continue;
+		if (!(old_cmd & ICE1712_AC97_READY))
+			continue;
+		break;
+	}
+	outb(reg, ICEREG(ice, AC97_INDEX));
+	outb(old_cmd | ICE1712_AC97_READ, ICEREG(ice, AC97_CMD));
+	for (tm = 0; tm < 0x10000; tm++)
+		if ((inb(ICEREG(ice, AC97_CMD)) & ICE1712_AC97_READ) == 0)
+			break;
+	if (tm >= 0x10000)		/* timeout */
+		return ~0;
+	return inw(ICEREG(ice, AC97_DATA));
+}
+
+/*
+ * pro ac97 section
+ */
+
+static void snd_ice1712_pro_ac97_write(ac97_t *ac97,
+				       unsigned short reg,
+				       unsigned short val)
+{
+	ice1712_t *ice = (ice1712_t *)ac97->private_data;
+	int tm;
+	unsigned char old_cmd = 0;
+
+	for (tm = 0; tm < 0x10000; tm++) {
+		old_cmd = inb(ICEMT(ice, AC97_CMD));
+		if (old_cmd & (ICE1712_AC97_WRITE | ICE1712_AC97_READ))
+			continue;
+		if (!(old_cmd & ICE1712_AC97_READY))
+			continue;
+		break;
+	}
+	outb(reg, ICEMT(ice, AC97_INDEX));
+	outw(val, ICEMT(ice, AC97_DATA));
+	old_cmd &= ~(ICE1712_AC97_PBK_VSR | ICE1712_AC97_CAP_VSR);
+	outb(old_cmd | ICE1712_AC97_WRITE, ICEMT(ice, AC97_CMD));
+	for (tm = 0; tm < 0x10000; tm++)
+		if ((inb(ICEMT(ice, AC97_CMD)) & ICE1712_AC97_WRITE) == 0)
+			break;
+}
+
+
+static unsigned short snd_ice1712_pro_ac97_read(ac97_t *ac97,
+						unsigned short reg)
+{
+	ice1712_t *ice = (ice1712_t *)ac97->private_data;
+	int tm;
+	unsigned char old_cmd = 0;
+
+	for (tm = 0; tm < 0x10000; tm++) {
+		old_cmd = inb(ICEMT(ice, AC97_CMD));
+		if (old_cmd & (ICE1712_AC97_WRITE | ICE1712_AC97_READ))
+			continue;
+		if (!(old_cmd & ICE1712_AC97_READY))
+			continue;
+		break;
+	}
+	outb(reg, ICEMT(ice, AC97_INDEX));
+	outb(old_cmd | ICE1712_AC97_READ, ICEMT(ice, AC97_CMD));
+	for (tm = 0; tm < 0x10000; tm++)
+		if ((inb(ICEMT(ice, AC97_CMD)) & ICE1712_AC97_READ) == 0)
+			break;
+	if (tm >= 0x10000)		/* timeout */
+		return ~0;
+	return inw(ICEMT(ice, AC97_DATA));
+}
+
+static int snd_ice1712_digmix_route_ac97_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_ice1712_digmix_route_ac97_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	
+	spin_lock_irqsave(&ice->reg_lock, flags);
+	ucontrol->value.integer.value[0] = inb(ICEMT(ice, MONITOR_ROUTECTRL)) & ICE1712_ROUTE_AC97 ? 1 : 0;
+	spin_unlock_irqrestore(&ice->reg_lock, flags);
+	return 0;
+}
+
+static int snd_ice1712_digmix_route_ac97_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	unsigned char val, nval;
+	unsigned long flags;
+	
+	spin_lock_irqsave(&ice->reg_lock, flags);
+	val = inb(ICEMT(ice, MONITOR_ROUTECTRL));
+	nval = val & ~ICE1712_ROUTE_AC97;
+	if (ucontrol->value.integer.value[0]) nval |= ICE1712_ROUTE_AC97;
+	outb(nval, ICEMT(ice, MONITOR_ROUTECTRL));
+	spin_unlock_irqrestore(&ice->reg_lock, flags);
+	return val != nval;
+}
+
+static snd_kcontrol_new_t snd_ice1712_mixer_digmix_route_ac97 __devinitdata = {
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "Digital Mixer To AC97",
+	.info = snd_ice1712_digmix_route_ac97_info,
+	.get = snd_ice1712_digmix_route_ac97_get,
+	.put = snd_ice1712_digmix_route_ac97_put,
+};
+
+
+
+/*
+ * set gpio direction, write mask and data
+ */
+void snd_ice1712_gpio_write_bits(ice1712_t *ice, int mask, int bits)
+{
+	ice->gpio_direction |= mask;
+	snd_ice1712_write(ice, ICE1712_IREG_GPIO_DIRECTION, ice->gpio_direction);
+	snd_ice1712_write(ice, ICE1712_IREG_GPIO_WRITE_MASK, ~mask);
+	snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, mask & bits);
+}
+
+/*
+ */
+void snd_ice1712_save_gpio_status(ice1712_t *ice, unsigned char *tmp)
+{
+	down(&ice->gpio_mutex);
+	tmp[0] = ice->gpio_direction;
+	tmp[1] = ice->gpio_write_mask;
+}
+
+void snd_ice1712_restore_gpio_status(ice1712_t *ice, unsigned char *tmp)
+{
+	snd_ice1712_write(ice, ICE1712_IREG_GPIO_DIRECTION, tmp[0]);
+	snd_ice1712_write(ice, ICE1712_IREG_GPIO_WRITE_MASK, tmp[1]);
+	ice->gpio_direction = tmp[0];
+	ice->gpio_write_mask = tmp[1];
+	up(&ice->gpio_mutex);
+}
+
+
+/*
+ *
+ * CS8427 interface
+ *
+ */
+
+/*
+ * change the input clock selection
+ * spdif_clock = 1 - IEC958 input, 0 - Envy24
+ */
+static int snd_ice1712_cs8427_set_input_clock(ice1712_t *ice, int spdif_clock)
+{
+	unsigned char reg[2] = { 0x80 | 4, 0 };   /* CS8427 auto increment | register number 4 + data */
+	unsigned char val, nval;
+	int res = 0;
+	
+	snd_i2c_lock(ice->i2c);
+	if (snd_i2c_sendbytes(ice->cs8427, reg, 1) != 1) {
+		snd_i2c_unlock(ice->i2c);
+		return -EIO;
+	}
+	if (snd_i2c_readbytes(ice->cs8427, &val, 1) != 1) {
+		snd_i2c_unlock(ice->i2c);
+		return -EIO;
+	}
+	nval = val & 0xf0;
+	if (spdif_clock)
+		nval |= 0x01;
+	else
+		nval |= 0x04;
+	if (val != nval) {
+		reg[1] = nval;
+		if (snd_i2c_sendbytes(ice->cs8427, reg, 2) != 2) {
+			res = -EIO;
+		} else {
+			res++;
+		}
+	}
+	snd_i2c_unlock(ice->i2c);
+	return res;
+}
+
+/*
+ * spdif callbacks
+ */
+static void open_cs8427(ice1712_t *ice, snd_pcm_substream_t * substream)
+{
+	snd_cs8427_iec958_active(ice->cs8427, 1);
+}
+
+static void close_cs8427(ice1712_t *ice, snd_pcm_substream_t * substream)
+{
+	snd_cs8427_iec958_active(ice->cs8427, 0);
+}
+
+static void setup_cs8427(ice1712_t *ice, snd_pcm_substream_t * substream)
+{
+	snd_cs8427_iec958_pcm(ice->cs8427, substream->runtime->rate);
+}
+
+/*
+ * create and initialize callbacks for cs8427 interface
+ */
+int __devinit snd_ice1712_init_cs8427(ice1712_t *ice, int addr)
+{
+	int err;
+
+	if ((err = snd_cs8427_create(ice->i2c, addr, &ice->cs8427)) < 0) {
+		snd_printk("CS8427 initialization failed\n");
+		return err;
+	}
+	ice->spdif.ops.open = open_cs8427;
+	ice->spdif.ops.close = close_cs8427;
+	ice->spdif.ops.setup = setup_cs8427;
+	return 0;
+}
+
+
+/*
+ *  Interrupt handler
+ */
+
+static void snd_ice1712_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	ice1712_t *ice = snd_magic_cast(ice1712_t, dev_id, return);
+	unsigned char status;
+
+	while (1) {
+		status = inb(ICEREG(ice, IRQSTAT));
+		if (status == 0)
+			break;
+		if (status & ICE1712_IRQ_MPU1) {
+			if (ice->rmidi[0])
+				snd_mpu401_uart_interrupt(irq, ice->rmidi[0]->private_data, regs);
+			outb(ICE1712_IRQ_MPU1, ICEREG(ice, IRQSTAT));
+			status &= ~ICE1712_IRQ_MPU1;
+		}
+		if (status & ICE1712_IRQ_TIMER)
+			outb(ICE1712_IRQ_TIMER, ICEREG(ice, IRQSTAT));
+		if (status & ICE1712_IRQ_MPU2) {
+			if (ice->rmidi[1])
+				snd_mpu401_uart_interrupt(irq, ice->rmidi[1]->private_data, regs);
+			outb(ICE1712_IRQ_MPU2, ICEREG(ice, IRQSTAT));
+			status &= ~ICE1712_IRQ_MPU2;
+		}
+		if (status & ICE1712_IRQ_PROPCM) {
+			unsigned char mtstat = inb(ICEMT(ice, IRQ));
+			if (mtstat & ICE1712_MULTI_PBKSTATUS) {
+				if (ice->playback_pro_substream)
+					snd_pcm_period_elapsed(ice->playback_pro_substream);
+				outb(ICE1712_MULTI_PBKSTATUS, ICEMT(ice, IRQ));
+			}
+			if (mtstat & ICE1712_MULTI_CAPSTATUS) {
+				if (ice->capture_pro_substream)
+					snd_pcm_period_elapsed(ice->capture_pro_substream);
+				outb(ICE1712_MULTI_CAPSTATUS, ICEMT(ice, IRQ));
+			}
+		}
+		if (status & ICE1712_IRQ_FM)
+			outb(ICE1712_IRQ_FM, ICEREG(ice, IRQSTAT));
+		if (status & ICE1712_IRQ_PBKDS) {
+			u32 idx;
+			u16 pbkstatus;
+			snd_pcm_substream_t *substream;
+			pbkstatus = inw(ICEDS(ice, INTSTAT));
+			//printk("pbkstatus = 0x%x\n", pbkstatus);
+			for (idx = 0; idx < 6; idx++) {
+				if ((pbkstatus & (3 << (idx * 2))) == 0)
+					continue;
+				if ((substream = ice->playback_con_substream_ds[idx]) != NULL)
+					snd_pcm_period_elapsed(substream);
+				outw(3 << (idx * 2), ICEDS(ice, INTSTAT));
+			}
+			outb(ICE1712_IRQ_PBKDS, ICEREG(ice, IRQSTAT));
+		}
+		if (status & ICE1712_IRQ_CONCAP) {
+			if (ice->capture_con_substream)
+				snd_pcm_period_elapsed(ice->capture_con_substream);
+			outb(ICE1712_IRQ_CONCAP, ICEREG(ice, IRQSTAT));
+		}
+		if (status & ICE1712_IRQ_CONPBK) {
+			if (ice->playback_con_substream)
+				snd_pcm_period_elapsed(ice->playback_con_substream);
+			outb(ICE1712_IRQ_CONPBK, ICEREG(ice, IRQSTAT));
+		}
+	}
+}
+
+
+/*
+ *  PCM part - misc
+ */
+
+static int snd_ice1712_hw_params(snd_pcm_substream_t * substream,
+				 snd_pcm_hw_params_t * hw_params)
+{
+	return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
+}
+
+static int snd_ice1712_hw_free(snd_pcm_substream_t * substream)
+{
+	return snd_pcm_lib_free_pages(substream);
+}
+
+/*
+ *  PCM part - consumer I/O
+ */
+
+static int snd_ice1712_playback_trigger(snd_pcm_substream_t * substream,
+					int cmd)
+{
+	ice1712_t *ice = snd_pcm_substream_chip(substream);
+	int result = 0;
+	u32 tmp;
+	
+	spin_lock(&ice->reg_lock);
+	tmp = snd_ice1712_read(ice, ICE1712_IREG_PBK_CTRL);
+	if (cmd == SNDRV_PCM_TRIGGER_START) {
+		tmp |= 1;
+	} else if (cmd == SNDRV_PCM_TRIGGER_STOP) {
+		tmp &= ~1;
+	} else if (cmd == SNDRV_PCM_TRIGGER_PAUSE_PUSH) {
+		tmp |= 2;
+	} else if (cmd == SNDRV_PCM_TRIGGER_PAUSE_RELEASE) {
+		tmp &= ~2;
+	} else {
+		result = -EINVAL;
+	}
+	snd_ice1712_write(ice, ICE1712_IREG_PBK_CTRL, tmp);
+	spin_unlock(&ice->reg_lock);
+	return result;
+}
+
+static int snd_ice1712_playback_ds_trigger(snd_pcm_substream_t * substream,
+					   int cmd)
+{
+	ice1712_t *ice = snd_pcm_substream_chip(substream);
+	int result = 0;
+	u32 tmp;
+	
+	spin_lock(&ice->reg_lock);
+	tmp = snd_ice1712_ds_read(ice, substream->number * 2, ICE1712_DSC_CONTROL);
+	if (cmd == SNDRV_PCM_TRIGGER_START) {
+		tmp |= 1;
+	} else if (cmd == SNDRV_PCM_TRIGGER_STOP) {
+		tmp &= ~1;
+	} else if (cmd == SNDRV_PCM_TRIGGER_PAUSE_PUSH) {
+		tmp |= 2;
+	} else if (cmd == SNDRV_PCM_TRIGGER_PAUSE_RELEASE) {
+		tmp &= ~2;
+	} else {
+		result = -EINVAL;
+	}
+	snd_ice1712_ds_write(ice, substream->number * 2, ICE1712_DSC_CONTROL, tmp);
+	spin_unlock(&ice->reg_lock);
+	return result;
+}
+
+static int snd_ice1712_capture_trigger(snd_pcm_substream_t * substream,
+				       int cmd)
+{
+	ice1712_t *ice = snd_pcm_substream_chip(substream);
+	int result = 0;
+	u8 tmp;
+	
+	spin_lock(&ice->reg_lock);
+	tmp = snd_ice1712_read(ice, ICE1712_IREG_CAP_CTRL);
+	if (cmd == SNDRV_PCM_TRIGGER_START) {
+		tmp |= 1;
+	} else if (cmd == SNDRV_PCM_TRIGGER_STOP) {
+		tmp &= ~1;
+	} else {
+		result = -EINVAL;
+	}
+	snd_ice1712_write(ice, ICE1712_IREG_CAP_CTRL, tmp);
+	spin_unlock(&ice->reg_lock);
+	return result;
+}
+
+static int snd_ice1712_playback_prepare(snd_pcm_substream_t * substream)
+{
+	unsigned long flags;
+	ice1712_t *ice = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	u32 period_size, buf_size, rate, tmp;
+
+	period_size = (snd_pcm_lib_period_bytes(substream) >> 2) - 1;
+	buf_size = snd_pcm_lib_buffer_bytes(substream) - 1;
+	tmp = 0x0000;
+	if (snd_pcm_format_width(runtime->format) == 16)
+		tmp |= 0x10;
+	if (runtime->channels == 2)
+		tmp |= 0x08;
+	rate = (runtime->rate * 8192) / 375;
+	if (rate > 0x000fffff)
+		rate = 0x000fffff;
+	spin_lock_irqsave(&ice->reg_lock, flags);
+	outb(0, ice->ddma_port + 15);
+	outb(ICE1712_DMA_MODE_WRITE | ICE1712_DMA_AUTOINIT, ice->ddma_port + 0x0b);
+	outl(runtime->dma_addr, ice->ddma_port + 0);
+	outw(buf_size, ice->ddma_port + 4);
+	snd_ice1712_write(ice, ICE1712_IREG_PBK_RATE_LO, rate & 0xff);
+	snd_ice1712_write(ice, ICE1712_IREG_PBK_RATE_MID, (rate >> 8) & 0xff);
+	snd_ice1712_write(ice, ICE1712_IREG_PBK_RATE_HI, (rate >> 16) & 0xff);
+	snd_ice1712_write(ice, ICE1712_IREG_PBK_CTRL, tmp);
+	snd_ice1712_write(ice, ICE1712_IREG_PBK_COUNT_LO, period_size & 0xff);
+	snd_ice1712_write(ice, ICE1712_IREG_PBK_COUNT_HI, period_size >> 8);
+	snd_ice1712_write(ice, ICE1712_IREG_PBK_LEFT, 0);
+	snd_ice1712_write(ice, ICE1712_IREG_PBK_RIGHT, 0);
+	spin_unlock_irqrestore(&ice->reg_lock, flags);
+	return 0;
+}
+
+static int snd_ice1712_playback_ds_prepare(snd_pcm_substream_t * substream)
+{
+	unsigned long flags;
+	ice1712_t *ice = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	u32 period_size, buf_size, rate, tmp, chn;
+
+	period_size = snd_pcm_lib_period_bytes(substream) - 1;
+	buf_size = snd_pcm_lib_buffer_bytes(substream) - 1;
+	tmp = 0x0064;
+	if (snd_pcm_format_width(runtime->format) == 16)
+		tmp &= ~0x04;
+	if (runtime->channels == 2)
+		tmp |= 0x08;
+	rate = (runtime->rate * 8192) / 375;
+	if (rate > 0x000fffff)
+		rate = 0x000fffff;
+	ice->playback_con_active_buf[substream->number] = 0;
+	ice->playback_con_virt_addr[substream->number] = runtime->dma_addr;
+	chn = substream->number * 2;
+	spin_lock_irqsave(&ice->reg_lock, flags);
+	snd_ice1712_ds_write(ice, chn, ICE1712_DSC_ADDR0, runtime->dma_addr);
+	snd_ice1712_ds_write(ice, chn, ICE1712_DSC_COUNT0, period_size);
+	snd_ice1712_ds_write(ice, chn, ICE1712_DSC_ADDR1, runtime->dma_addr + (runtime->periods > 1 ? period_size + 1 : 0));
+	snd_ice1712_ds_write(ice, chn, ICE1712_DSC_COUNT1, period_size);
+	snd_ice1712_ds_write(ice, chn, ICE1712_DSC_RATE, rate);
+	snd_ice1712_ds_write(ice, chn, ICE1712_DSC_VOLUME, 0);
+	snd_ice1712_ds_write(ice, chn, ICE1712_DSC_CONTROL, tmp);
+	if (runtime->channels == 2) {
+		snd_ice1712_ds_write(ice, chn + 1, ICE1712_DSC_RATE, rate);
+		snd_ice1712_ds_write(ice, chn + 1, ICE1712_DSC_VOLUME, 0);
+	}
+	spin_unlock_irqrestore(&ice->reg_lock, flags);
+	return 0;
+}
+
+static int snd_ice1712_capture_prepare(snd_pcm_substream_t * substream)
+{
+	unsigned long flags;
+	ice1712_t *ice = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	u32 period_size, buf_size;
+	u8 tmp;
+
+	period_size = (snd_pcm_lib_period_bytes(substream) >> 2) - 1;
+	buf_size = snd_pcm_lib_buffer_bytes(substream) - 1;
+	tmp = 0x06;
+	if (snd_pcm_format_width(runtime->format) == 16)
+		tmp &= ~0x04;
+	if (runtime->channels == 2)
+		tmp &= ~0x02;
+	spin_lock_irqsave(&ice->reg_lock, flags);
+	outl(ice->capture_con_virt_addr = runtime->dma_addr, ICEREG(ice, CONCAP_ADDR));
+	outw(buf_size, ICEREG(ice, CONCAP_COUNT));
+	snd_ice1712_write(ice, ICE1712_IREG_CAP_COUNT_HI, period_size >> 8);
+	snd_ice1712_write(ice, ICE1712_IREG_CAP_COUNT_LO, period_size & 0xff);
+	snd_ice1712_write(ice, ICE1712_IREG_CAP_CTRL, tmp);
+	spin_unlock_irqrestore(&ice->reg_lock, flags);
+	snd_ac97_set_rate(ice->ac97, AC97_PCM_LR_ADC_RATE, runtime->rate);
+	return 0;
+}
+
+static snd_pcm_uframes_t snd_ice1712_playback_pointer(snd_pcm_substream_t * substream)
+{
+	ice1712_t *ice = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	size_t ptr;
+
+	if (!(snd_ice1712_read(ice, ICE1712_IREG_PBK_CTRL) & 1))
+		return 0;
+	ptr = runtime->buffer_size - inw(ice->ddma_port + 4);
+	return bytes_to_frames(substream->runtime, ptr);
+}
+
+static snd_pcm_uframes_t snd_ice1712_playback_ds_pointer(snd_pcm_substream_t * substream)
+{
+	ice1712_t *ice = snd_pcm_substream_chip(substream);
+	u8 addr;
+	size_t ptr;
+
+	if (!(snd_ice1712_ds_read(ice, substream->number * 2, ICE1712_DSC_CONTROL) & 1))
+		return 0;
+	if (ice->playback_con_active_buf[substream->number])
+		addr = ICE1712_DSC_ADDR1;
+	else
+		addr = ICE1712_DSC_ADDR0;
+	ptr = snd_ice1712_ds_read(ice, substream->number * 2, addr) -
+		ice->playback_con_virt_addr[substream->number];
+	return bytes_to_frames(substream->runtime, ptr);
+}
+
+static snd_pcm_uframes_t snd_ice1712_capture_pointer(snd_pcm_substream_t * substream)
+{
+	ice1712_t *ice = snd_pcm_substream_chip(substream);
+	size_t ptr;
+
+	if (!(snd_ice1712_read(ice, ICE1712_IREG_CAP_CTRL) & 1))
+		return 0;
+	ptr = inl(ICEREG(ice, CONCAP_ADDR)) - ice->capture_con_virt_addr;
+	return bytes_to_frames(substream->runtime, ptr);
+}
+
+static snd_pcm_hardware_t snd_ice1712_playback =
+{
+	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
+				 SNDRV_PCM_INFO_MMAP_VALID |
+				 SNDRV_PCM_INFO_PAUSE),
+	.formats =		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
+	.rates =		SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+	.rate_min =		4000,
+	.rate_max =		48000,
+	.channels_min =		1,
+	.channels_max =		2,
+	.buffer_bytes_max =	(64*1024),
+	.period_bytes_min =	64,
+	.period_bytes_max =	(64*1024),
+	.periods_min =		1,
+	.periods_max =		1024,
+	.fifo_size =		0,
+};
+
+static snd_pcm_hardware_t snd_ice1712_playback_ds =
+{
+	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
+				 SNDRV_PCM_INFO_MMAP_VALID |
+				 SNDRV_PCM_INFO_PAUSE),
+	.formats =		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
+	.rates =		SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+	.rate_min =		4000,
+	.rate_max =		48000,
+	.channels_min =		1,
+	.channels_max =		2,
+	.buffer_bytes_max =	(128*1024),
+	.period_bytes_min =	64,
+	.period_bytes_max =	(128*1024),
+	.periods_min =		2,
+	.periods_max =		2,
+	.fifo_size =		0,
+};
+
+static snd_pcm_hardware_t snd_ice1712_capture =
+{
+	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
+				 SNDRV_PCM_INFO_MMAP_VALID),
+	.formats =		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
+	.rates =		SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+	.rate_min =		4000,
+	.rate_max =		48000,
+	.channels_min =		1,
+	.channels_max =		2,
+	.buffer_bytes_max =	(64*1024),
+	.period_bytes_min =	64,
+	.period_bytes_max =	(64*1024),
+	.periods_min =		1,
+	.periods_max =		1024,
+	.fifo_size =		0,
+};
+
+static int snd_ice1712_playback_open(snd_pcm_substream_t * substream)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	ice1712_t *ice = snd_pcm_substream_chip(substream);
+
+	ice->playback_con_substream = substream;
+	runtime->hw = snd_ice1712_playback;
+	return 0;
+}
+
+static int snd_ice1712_playback_ds_open(snd_pcm_substream_t * substream)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	ice1712_t *ice = snd_pcm_substream_chip(substream);
+	unsigned long flags;
+	u32 tmp;
+
+	ice->playback_con_substream_ds[substream->number] = substream;
+	runtime->hw = snd_ice1712_playback_ds;
+	spin_lock_irqsave(&ice->reg_lock, flags); 
+	tmp = inw(ICEDS(ice, INTMASK)) & ~(1 << (substream->number * 2));
+	outw(tmp, ICEDS(ice, INTMASK));
+	spin_unlock_irqrestore(&ice->reg_lock, flags);
+	return 0;
+}
+
+static int snd_ice1712_capture_open(snd_pcm_substream_t * substream)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	ice1712_t *ice = snd_pcm_substream_chip(substream);
+
+	ice->capture_con_substream = substream;
+	runtime->hw = snd_ice1712_capture;
+	runtime->hw.rates = ice->ac97->rates[AC97_RATES_ADC];
+	if (!(runtime->hw.rates & SNDRV_PCM_RATE_8000))
+		runtime->hw.rate_min = 48000;
+	return 0;
+}
+
+static int snd_ice1712_playback_close(snd_pcm_substream_t * substream)
+{
+	ice1712_t *ice = snd_pcm_substream_chip(substream);
+
+	ice->playback_con_substream = NULL;
+	return 0;
+}
+
+static int snd_ice1712_playback_ds_close(snd_pcm_substream_t * substream)
+{
+	ice1712_t *ice = snd_pcm_substream_chip(substream);
+	unsigned long flags;
+	u32 tmp;
+
+	spin_lock_irqsave(&ice->reg_lock, flags); 
+	tmp = inw(ICEDS(ice, INTMASK)) | (3 << (substream->number * 2));
+	outw(tmp, ICEDS(ice, INTMASK));
+	spin_unlock_irqrestore(&ice->reg_lock, flags);
+	ice->playback_con_substream_ds[substream->number] = NULL;
+	return 0;
+}
+
+static int snd_ice1712_capture_close(snd_pcm_substream_t * substream)
+{
+	ice1712_t *ice = snd_pcm_substream_chip(substream);
+
+	ice->capture_con_substream = NULL;
+	return 0;
+}
+
+static snd_pcm_ops_t snd_ice1712_playback_ops = {
+	.open =		snd_ice1712_playback_open,
+	.close =	snd_ice1712_playback_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	snd_ice1712_hw_params,
+	.hw_free =	snd_ice1712_hw_free,
+	.prepare =	snd_ice1712_playback_prepare,
+	.trigger =	snd_ice1712_playback_trigger,
+	.pointer =	snd_ice1712_playback_pointer,
+};
+
+static snd_pcm_ops_t snd_ice1712_playback_ds_ops = {
+	.open =		snd_ice1712_playback_ds_open,
+	.close =	snd_ice1712_playback_ds_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	snd_ice1712_hw_params,
+	.hw_free =	snd_ice1712_hw_free,
+	.prepare =	snd_ice1712_playback_ds_prepare,
+	.trigger =	snd_ice1712_playback_ds_trigger,
+	.pointer =	snd_ice1712_playback_ds_pointer,
+};
+
+static snd_pcm_ops_t snd_ice1712_capture_ops = {
+	.open =		snd_ice1712_capture_open,
+	.close =	snd_ice1712_capture_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	snd_ice1712_hw_params,
+	.hw_free =	snd_ice1712_hw_free,
+	.prepare =	snd_ice1712_capture_prepare,
+	.trigger =	snd_ice1712_capture_trigger,
+	.pointer =	snd_ice1712_capture_pointer,
+};
+
+static void snd_ice1712_pcm_free(snd_pcm_t *pcm)
+{
+	ice1712_t *ice = snd_magic_cast(ice1712_t, pcm->private_data, return);
+	ice->pcm = NULL;
+	snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+static int __devinit snd_ice1712_pcm(ice1712_t * ice, int device, snd_pcm_t ** rpcm)
+{
+	snd_pcm_t *pcm;
+	int err;
+
+	if (rpcm)
+		*rpcm = NULL;
+	err = snd_pcm_new(ice->card, "ICE1712 consumer", device, 1, 1, &pcm);
+	if (err < 0)
+		return err;
+
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ice1712_playback_ops);
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ice1712_capture_ops);
+
+	pcm->private_data = ice;
+	pcm->private_free = snd_ice1712_pcm_free;
+	pcm->info_flags = 0;
+	strcpy(pcm->name, "ICE1712 consumer");
+	ice->pcm = pcm;
+
+	snd_pcm_lib_preallocate_pci_pages_for_all(ice->pci, pcm, 64*1024, 64*1024);
+
+	if (rpcm)
+		*rpcm = pcm;
+
+	printk(KERN_WARNING "Consumer PCM code does not work well at the moment --jk\n");
+
+	return 0;
+}
+
+static void snd_ice1712_pcm_free_ds(snd_pcm_t *pcm)
+{
+	ice1712_t *ice = snd_magic_cast(ice1712_t, pcm->private_data, return);
+	ice->pcm_ds = NULL;
+	snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+static int __devinit snd_ice1712_pcm_ds(ice1712_t * ice, int device, snd_pcm_t ** rpcm)
+{
+	snd_pcm_t *pcm;
+	int err;
+
+	if (rpcm)
+		*rpcm = NULL;
+	err = snd_pcm_new(ice->card, "ICE1712 consumer (DS)", device, 6, 0, &pcm);
+	if (err < 0)
+		return err;
+
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ice1712_playback_ds_ops);
+
+	pcm->private_data = ice;
+	pcm->private_free = snd_ice1712_pcm_free_ds;
+	pcm->info_flags = 0;
+	strcpy(pcm->name, "ICE1712 consumer (DS)");
+	ice->pcm_ds = pcm;
+
+	snd_pcm_lib_preallocate_pci_pages_for_all(ice->pci, pcm, 64*1024, 128*1024);
+
+	if (rpcm)
+		*rpcm = pcm;
+
+	return 0;
+}
+
+/*
+ *  PCM code - professional part (multitrack)
+ */
+
+static unsigned int rates[] = { 8000, 9600, 11025, 12000, 16000, 22050, 24000,
+				32000, 44100, 48000, 64000, 88200, 96000 };
+
+#define RATES sizeof(rates) / sizeof(rates[0])
+
+static snd_pcm_hw_constraint_list_t hw_constraints_rates = {
+	.count = RATES,
+	.list = rates,
+	.mask = 0,
+};
+
+static int snd_ice1712_pro_trigger(snd_pcm_substream_t *substream,
+				   int cmd)
+{
+	ice1712_t *ice = snd_pcm_substream_chip(substream);
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+	{
+		unsigned int what;
+		unsigned int old;
+		if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
+			return -EINVAL;
+		what = ICE1712_PLAYBACK_PAUSE;
+		snd_pcm_trigger_done(substream, substream);
+		spin_lock(&ice->reg_lock);
+		old = inl(ICEMT(ice, PLAYBACK_CONTROL));
+		if (cmd == SNDRV_PCM_TRIGGER_PAUSE_PUSH)
+			old |= what;
+		else
+			old &= ~what;
+		outl(old, ICEMT(ice, PLAYBACK_CONTROL));
+		spin_unlock(&ice->reg_lock);
+		break;
+	}
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_STOP:
+	{
+		unsigned int what = 0;
+		unsigned int old;
+		snd_pcm_substream_t *s = substream;
+		do {
+			if (s == ice->playback_pro_substream) {
+				what |= ICE1712_PLAYBACK_START;
+				snd_pcm_trigger_done(s, substream);
+			} else if (s == ice->capture_pro_substream) {
+				what |= ICE1712_CAPTURE_START_SHADOW;
+				snd_pcm_trigger_done(s, substream);
+			}
+			s = s->link_next;
+		} while (s != substream);
+		spin_lock(&ice->reg_lock);
+		old = inl(ICEMT(ice, PLAYBACK_CONTROL));
+		if (cmd == SNDRV_PCM_TRIGGER_START)
+			old |= what;
+		else
+			old &= ~what;
+		outl(old, ICEMT(ice, PLAYBACK_CONTROL));
+		spin_unlock(&ice->reg_lock);
+		break;
+	}
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+/*
+ */
+static void snd_ice1712_set_pro_rate(ice1712_t *ice, snd_pcm_substream_t *substream)
+{
+	unsigned long flags;
+	unsigned int rate;
+	unsigned char val;
+
+	spin_lock_irqsave(&ice->reg_lock, flags);
+	if ((inb(ICEMT(ice, PLAYBACK_CONTROL)) & (ICE1712_CAPTURE_START_SHADOW|
+						  ICE1712_PLAYBACK_PAUSE|
+						  ICE1712_PLAYBACK_START)) ||
+	    (inb(ICEMT(ice, RATE)) & ICE1712_SPDIF_MASTER)) {
+		spin_unlock_irqrestore(&ice->reg_lock, flags);
+		return;
+	}
+	rate = substream->runtime->rate;
+	switch (rate) {
+	case 8000: val = 6; break;
+	case 9600: val = 3; break;
+	case 11025: val = 10; break;
+	case 12000: val = 2; break;
+	case 16000: val = 5; break;
+	case 22050: val = 9; break;
+	case 24000: val = 1; break;
+	case 32000: val = 4; break;
+	case 44100: val = 8; break;
+	case 48000: val = 0; break;
+	case 64000: val = 15; break;
+	case 88200: val = 11; break;
+	case 96000: val = 7; break;
+	default:
+		snd_BUG();
+		val = 0;
+		break;
+	}
+	outb(val, ICEMT(ice, RATE));
+	spin_unlock_irqrestore(&ice->reg_lock, flags);
+
+	if (ice->ak4524.ops.set_rate_val)
+		ice->ak4524.ops.set_rate_val(ice, val);
+}
+
+static int snd_ice1712_playback_pro_prepare(snd_pcm_substream_t * substream)
+{
+	unsigned long flags;
+	ice1712_t *ice = snd_pcm_substream_chip(substream);
+
+	ice->playback_pro_size = snd_pcm_lib_buffer_bytes(substream);
+	snd_ice1712_set_pro_rate(ice, substream);
+	spin_lock_irqsave(&ice->reg_lock, flags);
+	outl(substream->runtime->dma_addr, ICEMT(ice, PLAYBACK_ADDR));
+	outw((ice->playback_pro_size >> 2) - 1, ICEMT(ice, PLAYBACK_SIZE));
+	outw((snd_pcm_lib_period_bytes(substream) >> 2) - 1, ICEMT(ice, PLAYBACK_COUNT));
+	spin_unlock_irqrestore(&ice->reg_lock, flags);
+
+	if (ice->spdif.ops.setup)
+		ice->spdif.ops.setup(ice, substream);
+
+	return 0;
+}
+
+static int snd_ice1712_capture_pro_prepare(snd_pcm_substream_t * substream)
+{
+	unsigned long flags;
+	ice1712_t *ice = snd_pcm_substream_chip(substream);
+
+	ice->capture_pro_size = snd_pcm_lib_buffer_bytes(substream);
+	snd_ice1712_set_pro_rate(ice, substream);
+	spin_lock_irqsave(&ice->reg_lock, flags);
+	outl(substream->runtime->dma_addr, ICEMT(ice, CAPTURE_ADDR));
+	outw((ice->capture_pro_size >> 2) - 1, ICEMT(ice, CAPTURE_SIZE));
+	outw((snd_pcm_lib_period_bytes(substream) >> 2) - 1, ICEMT(ice, CAPTURE_COUNT));
+	spin_unlock_irqrestore(&ice->reg_lock, flags);
+	return 0;
+}
+
+static snd_pcm_uframes_t snd_ice1712_playback_pro_pointer(snd_pcm_substream_t * substream)
+{
+	ice1712_t *ice = snd_pcm_substream_chip(substream);
+	size_t ptr;
+
+	if (!(inl(ICEMT(ice, PLAYBACK_CONTROL)) & ICE1712_PLAYBACK_START))
+		return 0;
+	ptr = ice->playback_pro_size - (inw(ICEMT(ice, PLAYBACK_SIZE)) << 2);
+	return bytes_to_frames(substream->runtime, ptr);
+}
+
+static snd_pcm_uframes_t snd_ice1712_capture_pro_pointer(snd_pcm_substream_t * substream)
+{
+	ice1712_t *ice = snd_pcm_substream_chip(substream);
+	size_t ptr;
+
+	if (!(inl(ICEMT(ice, PLAYBACK_CONTROL)) & ICE1712_CAPTURE_START_SHADOW))
+		return 0;
+	ptr = ice->capture_pro_size - (inw(ICEMT(ice, CAPTURE_SIZE)) << 2);
+	return bytes_to_frames(substream->runtime, ptr);
+}
+
+static snd_pcm_hardware_t snd_ice1712_playback_pro =
+{
+	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
+				 SNDRV_PCM_INFO_MMAP_VALID |
+				 SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_SYNC_START),
+	.formats =		SNDRV_PCM_FMTBIT_S32_LE,
+	.rates =		SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_96000,
+	.rate_min =		4000,
+	.rate_max =		96000,
+	.channels_min =		10,
+	.channels_max =		10,
+	.buffer_bytes_max =	(256*1024),
+	.period_bytes_min =	10 * 4 * 2,
+	.period_bytes_max =	131040,
+	.periods_min =		1,
+	.periods_max =		1024,
+	.fifo_size =		0,
+};
+
+static snd_pcm_hardware_t snd_ice1712_capture_pro =
+{
+	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
+				 SNDRV_PCM_INFO_MMAP_VALID |
+				 SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_SYNC_START),
+	.formats =		SNDRV_PCM_FMTBIT_S32_LE,
+	.rates =		SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_96000,
+	.rate_min =		4000,
+	.rate_max =		96000,
+	.channels_min =		12,
+	.channels_max =		12,
+	.buffer_bytes_max =	(256*1024),
+	.period_bytes_min =	12 * 4 * 2,
+	.period_bytes_max =	131040,
+	.periods_min =		1,
+	.periods_max =		1024,
+	.fifo_size =		0,
+};
+
+static int snd_ice1712_playback_pro_open(snd_pcm_substream_t * substream)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	ice1712_t *ice = snd_pcm_substream_chip(substream);
+
+	ice->playback_pro_substream = substream;
+	runtime->hw = snd_ice1712_playback_pro;
+	snd_pcm_set_sync(substream);
+	snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
+	snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates);
+
+	if (ice->spdif.ops.open)
+		ice->spdif.ops.open(ice, substream);
+
+	return 0;
+}
+
+static int snd_ice1712_capture_pro_open(snd_pcm_substream_t * substream)
+{
+	ice1712_t *ice = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+
+	ice->capture_pro_substream = substream;
+	runtime->hw = snd_ice1712_capture_pro;
+	snd_pcm_set_sync(substream);
+	snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
+	snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates);
+	return 0;
+}
+
+static int snd_ice1712_playback_pro_close(snd_pcm_substream_t * substream)
+{
+	ice1712_t *ice = snd_pcm_substream_chip(substream);
+
+	ice->playback_pro_substream = NULL;
+	if (ice->spdif.ops.close)
+		ice->spdif.ops.close(ice, substream);
+
+	return 0;
+}
+
+static int snd_ice1712_capture_pro_close(snd_pcm_substream_t * substream)
+{
+	ice1712_t *ice = snd_pcm_substream_chip(substream);
+
+	ice->capture_pro_substream = NULL;
+	return 0;
+}
+
+static void snd_ice1712_pcm_profi_free(snd_pcm_t *pcm)
+{
+	ice1712_t *ice = snd_magic_cast(ice1712_t, pcm->private_data, return);
+	ice->pcm_pro = NULL;
+	snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+static snd_pcm_ops_t snd_ice1712_playback_pro_ops = {
+	.open =		snd_ice1712_playback_pro_open,
+	.close =	snd_ice1712_playback_pro_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	snd_ice1712_hw_params,
+	.hw_free =	snd_ice1712_hw_free,
+	.prepare =	snd_ice1712_playback_pro_prepare,
+	.trigger =	snd_ice1712_pro_trigger,
+	.pointer =	snd_ice1712_playback_pro_pointer,
+};
+
+static snd_pcm_ops_t snd_ice1712_capture_pro_ops = {
+	.open =		snd_ice1712_capture_pro_open,
+	.close =	snd_ice1712_capture_pro_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	snd_ice1712_hw_params,
+	.hw_free =	snd_ice1712_hw_free,
+	.prepare =	snd_ice1712_capture_pro_prepare,
+	.trigger =	snd_ice1712_pro_trigger,
+	.pointer =	snd_ice1712_capture_pro_pointer,
+};
+
+static int __devinit snd_ice1712_pcm_profi(ice1712_t * ice, int device, snd_pcm_t ** rpcm)
+{
+	snd_pcm_t *pcm;
+	int err;
+
+	if (rpcm)
+		*rpcm = NULL;
+	err = snd_pcm_new(ice->card, "ICE1712 multi", device, 1, 1, &pcm);
+	if (err < 0)
+		return err;
+
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ice1712_playback_pro_ops);
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ice1712_capture_pro_ops);
+
+	pcm->private_data = ice;
+	pcm->private_free = snd_ice1712_pcm_profi_free;
+	pcm->info_flags = 0;
+	strcpy(pcm->name, "ICE1712 multi");
+
+	snd_pcm_lib_preallocate_pci_pages_for_all(ice->pci, pcm, 256*1024, 256*1024);
+
+	ice->pcm_pro = pcm;
+	if (rpcm)
+		*rpcm = pcm;
+	
+	if (ice->cs8427) {
+		/* assign channels to iec958 */
+		err = snd_cs8427_iec958_build(ice->cs8427,
+					      pcm->streams[0].substream,
+					      pcm->streams[1].substream);
+		if (err < 0)
+			return err;
+	}
+
+	if ((err = snd_ice1712_build_pro_mixer(ice)) < 0)
+		return err;
+	return 0;
+}
+
+/*
+ *  Mixer section
+ */
+
+static void snd_ice1712_update_volume(ice1712_t *ice, int index)
+{
+	unsigned int vol = ice->pro_volumes[index];
+	unsigned short val = 0;
+
+	val |= (vol & 0x8000) == 0 ? (96 - (vol & 0x7f)) : 0x7f;
+	val |= ((vol & 0x80000000) == 0 ? (96 - ((vol >> 16) & 0x7f)) : 0x7f) << 8;
+	outb(index, ICEMT(ice, MONITOR_INDEX));
+	outw(val, ICEMT(ice, MONITOR_VOLUME));
+}
+
+static int snd_ice1712_pro_mixer_switch_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+	uinfo->count = 2;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 1;
+	return 0;
+}
+
+static int snd_ice1712_pro_mixer_switch_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int index = kcontrol->private_value;
+	
+	spin_lock_irqsave(&ice->reg_lock, flags);
+	ucontrol->value.integer.value[0] = !((ice->pro_volumes[index] >> 15) & 1);
+	ucontrol->value.integer.value[1] = !((ice->pro_volumes[index] >> 31) & 1);
+	spin_unlock_irqrestore(&ice->reg_lock, flags);
+	return 0;
+}
+
+static int snd_ice1712_pro_mixer_switch_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	unsigned long flags;
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	int index = kcontrol->private_value;
+	unsigned int nval, change;
+
+	nval = (ucontrol->value.integer.value[0] ? 0 : 0x00008000) |
+	       (ucontrol->value.integer.value[1] ? 0 : 0x80000000);
+	spin_lock_irqsave(&ice->reg_lock, flags);
+	nval |= ice->pro_volumes[index] & ~0x80008000;
+	change = nval != ice->pro_volumes[index];
+	ice->pro_volumes[index] = nval;
+	snd_ice1712_update_volume(ice, index);
+	spin_unlock_irqrestore(&ice->reg_lock, flags);
+	return change;
+}
+
+static int snd_ice1712_pro_mixer_volume_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 2;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 96;
+	return 0;
+}
+
+static int snd_ice1712_pro_mixer_volume_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int index = kcontrol->private_value;
+	
+	spin_lock_irqsave(&ice->reg_lock, flags);
+	ucontrol->value.integer.value[0] = (ice->pro_volumes[index] >> 0) & 127;
+	ucontrol->value.integer.value[1] = (ice->pro_volumes[index] >> 16) & 127;
+	spin_unlock_irqrestore(&ice->reg_lock, flags);
+	return 0;
+}
+
+static int snd_ice1712_pro_mixer_volume_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	unsigned long flags;
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	int index = kcontrol->private_value;
+	unsigned int nval, change;
+
+	nval = (ucontrol->value.integer.value[0] & 127) |
+	       ((ucontrol->value.integer.value[1] & 127) << 16);
+	spin_lock_irqsave(&ice->reg_lock, flags);
+	nval |= ice->pro_volumes[index] & ~0x007f007f;
+	change = nval != ice->pro_volumes[index];
+	ice->pro_volumes[index] = nval;
+	snd_ice1712_update_volume(ice, index);
+	spin_unlock_irqrestore(&ice->reg_lock, flags);
+	return change;
+}
+
+
+static int __init snd_ice1712_build_pro_mixer(ice1712_t *ice)
+{
+	snd_card_t * card = ice->card;
+	snd_kcontrol_t ctl;
+	int idx, err;
+
+	/* PCM playback */
+	for (idx = 0; idx < 10; idx++) {
+		memset(&ctl, 0, sizeof(ctl));
+		strcpy(ctl.id.name, "Multi Playback Switch");
+		ctl.id.index = idx;
+		ctl.id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+		ctl.info = snd_ice1712_pro_mixer_switch_info;
+		ctl.access = SNDRV_CTL_ELEM_ACCESS_READ|SNDRV_CTL_ELEM_ACCESS_WRITE;
+		ctl.get = snd_ice1712_pro_mixer_switch_get;
+		ctl.put = snd_ice1712_pro_mixer_switch_put;
+		ctl.private_value = idx;
+		ctl.private_data = ice;
+		if ((err = snd_ctl_add(card, snd_ctl_new(&ctl))) < 0)
+			return err;
+		memset(&ctl, 0, sizeof(ctl));
+		strcpy(ctl.id.name, "Multi Playback Volume");
+		ctl.id.index = idx;
+		ctl.id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+		ctl.info = snd_ice1712_pro_mixer_volume_info;
+		ctl.access = SNDRV_CTL_ELEM_ACCESS_READ|SNDRV_CTL_ELEM_ACCESS_WRITE;
+		ctl.get = snd_ice1712_pro_mixer_volume_get;
+		ctl.put = snd_ice1712_pro_mixer_volume_put;
+		ctl.private_value = idx;
+		ctl.private_data = ice;
+		if ((err = snd_ctl_add(card, snd_ctl_new(&ctl))) < 0)
+			return err;
+	}
+
+	/* PCM capture */
+	for (idx = 0; idx < 10; idx++) {
+		memset(&ctl, 0, sizeof(ctl));
+		strcpy(ctl.id.name, "Multi Capture Switch");
+		ctl.id.index = idx;
+		ctl.id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+		ctl.info = snd_ice1712_pro_mixer_switch_info;
+		ctl.access = SNDRV_CTL_ELEM_ACCESS_READ|SNDRV_CTL_ELEM_ACCESS_WRITE;
+		ctl.get = snd_ice1712_pro_mixer_switch_get;
+		ctl.put = snd_ice1712_pro_mixer_switch_put;
+		ctl.private_value = idx + 10;
+		ctl.private_data = ice;
+		if ((err = snd_ctl_add(card, snd_ctl_new(&ctl))) < 0)
+			return err;
+		memset(&ctl, 0, sizeof(ctl));
+		strcpy(ctl.id.name, "Multi Capture Volume");
+		ctl.id.index = idx;
+		ctl.id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+		ctl.info = snd_ice1712_pro_mixer_volume_info;
+		ctl.access = SNDRV_CTL_ELEM_ACCESS_READ|SNDRV_CTL_ELEM_ACCESS_WRITE;
+		ctl.get = snd_ice1712_pro_mixer_volume_get;
+		ctl.put = snd_ice1712_pro_mixer_volume_put;
+		ctl.private_value = idx + 10;
+		ctl.private_data = ice;
+		if ((err = snd_ctl_add(card, snd_ctl_new(&ctl))) < 0)
+			return err;
+	}
+	
+	/* initialize volumes */
+	for (idx = 0; idx < 20; idx++) {
+		ice->pro_volumes[idx] = 0x80008000;	/* mute */
+		snd_ice1712_update_volume(ice, idx);
+	}
+	return 0;
+}
+
+static void snd_ice1712_mixer_free_ac97(ac97_t *ac97)
+{
+	ice1712_t *ice = snd_magic_cast(ice1712_t, ac97->private_data, return);
+	ice->ac97 = NULL;
+}
+
+static int __devinit snd_ice1712_ac97_mixer(ice1712_t * ice)
+{
+	int err;
+
+	if (!(ice->eeprom.codec & ICE1712_CFG_NO_CON_AC97)) {
+		ac97_t ac97;
+		memset(&ac97, 0, sizeof(ac97));
+		ac97.write = snd_ice1712_ac97_write;
+		ac97.read = snd_ice1712_ac97_read;
+		ac97.private_data = ice;
+		ac97.private_free = snd_ice1712_mixer_free_ac97;
+		if ((err = snd_ac97_mixer(ice->card, &ac97, &ice->ac97)) < 0)
+			printk(KERN_WARNING "ice1712: cannot initialize ac97 for consumer, skipped\n");
+		else {
+			if ((err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_mixer_digmix_route_ac97, ice))) < 0)
+				return err;
+			return 0;
+		}
+	}
+	/* hmm.. can we have both consumer and pro ac97 mixers? */
+	if (! (ice->eeprom.aclink & ICE1712_CFG_PRO_I2S)) {
+		ac97_t ac97;
+		memset(&ac97, 0, sizeof(ac97));
+		ac97.write = snd_ice1712_pro_ac97_write;
+		ac97.read = snd_ice1712_pro_ac97_read;
+		ac97.private_data = ice;
+		ac97.private_free = snd_ice1712_mixer_free_ac97;
+		if ((err = snd_ac97_mixer(ice->card, &ac97, &ice->ac97)) < 0)
+			printk(KERN_WARNING "ice1712: cannot initialize pro ac97, skipped\n");
+		else
+			return 0;
+	}
+	/* I2S mixer only */
+	strcat(ice->card->mixername, "ICE1712 - multitrack");
+	return 0;
+}
+
+/*
+ *
+ */
+
+static void snd_ice1712_proc_read(snd_info_entry_t *entry, 
+				  snd_info_buffer_t * buffer)
+{
+	ice1712_t *ice = snd_magic_cast(ice1712_t, entry->private_data, return);
+	unsigned int idx;
+
+	snd_iprintf(buffer, "ICE1712\n\n");
+	snd_iprintf(buffer, "EEPROM:\n");
+	snd_iprintf(buffer, "  Subvendor        : 0x%x\n", ice->eeprom.subvendor);
+	snd_iprintf(buffer, "  Size             : %i bytes\n", ice->eeprom.size);
+	snd_iprintf(buffer, "  Version          : %i\n", ice->eeprom.version);
+	snd_iprintf(buffer, "  Codec            : 0x%x\n", ice->eeprom.codec);
+	snd_iprintf(buffer, "  ACLink           : 0x%x\n", ice->eeprom.aclink);
+	snd_iprintf(buffer, "  I2S ID           : 0x%x\n", ice->eeprom.i2sID);
+	snd_iprintf(buffer, "  S/PDIF           : 0x%x\n", ice->eeprom.spdif);
+	snd_iprintf(buffer, "  GPIO mask        : 0x%x\n", ice->eeprom.gpiomask);
+	snd_iprintf(buffer, "  GPIO state       : 0x%x\n", ice->eeprom.gpiostate);
+	snd_iprintf(buffer, "  GPIO direction   : 0x%x\n", ice->eeprom.gpiodir);
+	snd_iprintf(buffer, "  AC'97 main       : 0x%x\n", ice->eeprom.ac97main);
+	snd_iprintf(buffer, "  AC'97 pcm        : 0x%x\n", ice->eeprom.ac97pcm);
+	snd_iprintf(buffer, "  AC'97 record     : 0x%x\n", ice->eeprom.ac97rec);
+	snd_iprintf(buffer, "  AC'97 record src : 0x%x\n", ice->eeprom.ac97recsrc);
+	for (idx = 0; idx < 4; idx++)
+		snd_iprintf(buffer, "  DAC ID #%i        : 0x%x\n", idx, ice->eeprom.dacID[idx]);
+	for (idx = 0; idx < 4; idx++)
+		snd_iprintf(buffer, "  ADC ID #%i        : 0x%x\n", idx, ice->eeprom.adcID[idx]);
+	for (idx = 0x1c; idx < ice->eeprom.size && idx < 0x1c + sizeof(ice->eeprom.extra); idx++)
+		snd_iprintf(buffer, "  Extra #%02i        : 0x%x\n", idx, ice->eeprom.extra[idx - 0x1c]);
+}
+
+static void __devinit snd_ice1712_proc_init(ice1712_t * ice)
+{
+	snd_info_entry_t *entry;
+
+	if ((entry = snd_info_create_card_entry(ice->card, "ice1712", ice->card->proc_root)) != NULL) {
+		entry->content = SNDRV_INFO_CONTENT_TEXT;
+		entry->private_data = ice;
+		entry->mode = S_IFREG | S_IRUGO | S_IWUSR;
+		entry->c.text.read_size = 2048;
+		entry->c.text.read = snd_ice1712_proc_read;
+		if (snd_info_register(entry) < 0) {
+			snd_info_free_entry(entry);
+			entry = NULL;
+		}
+	}
+	ice->proc_entry = entry;
+}
+
+static void snd_ice1712_proc_done(ice1712_t * ice)
+{
+	if (ice->proc_entry) {
+		snd_info_unregister(ice->proc_entry);
+		ice->proc_entry = NULL;
+	}
+}
+
+/*
+ *
+ */
+
+static int snd_ice1712_eeprom_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
+	uinfo->count = 32;
+	return 0;
+}
+
+static int snd_ice1712_eeprom_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	
+	memcpy(ucontrol->value.bytes.data, &ice->eeprom, 32);
+	return 0;
+}
+
+static snd_kcontrol_new_t snd_ice1712_eeprom __devinitdata = {
+	.iface = SNDRV_CTL_ELEM_IFACE_CARD,
+	.name = "ICE1712 EEPROM",
+	.access = SNDRV_CTL_ELEM_ACCESS_READ,
+	.info = snd_ice1712_eeprom_info,
+	.get = snd_ice1712_eeprom_get
+};
+
+/*
+ */
+static int snd_ice1712_spdif_default_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_ice1712_spdif_default_get(snd_kcontrol_t * kcontrol,
+					 snd_ctl_elem_value_t * ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	if (ice->spdif.ops.default_get)
+		ice->spdif.ops.default_get(ice, ucontrol); 
+	return 0;
+}
+
+static int snd_ice1712_spdif_default_put(snd_kcontrol_t * kcontrol,
+					 snd_ctl_elem_value_t * ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	if (ice->spdif.ops.default_put)
+		return ice->spdif.ops.default_put(ice, ucontrol);
+	return 0;
+}
+
+static snd_kcontrol_new_t snd_ice1712_spdif_default __devinitdata =
+{
+	.iface =		SNDRV_CTL_ELEM_IFACE_PCM,
+	.name =           SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
+	.info =		snd_ice1712_spdif_default_info,
+	.get =		snd_ice1712_spdif_default_get,
+	.put =		snd_ice1712_spdif_default_put
+};
+
+static int snd_ice1712_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_ice1712_spdif_maskc_get(snd_kcontrol_t * kcontrol,
+				       snd_ctl_elem_value_t * ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	if (ice->spdif.ops.default_get) {
+		ucontrol->value.iec958.status[0] = IEC958_AES0_NONAUDIO |
+						     IEC958_AES0_PROFESSIONAL |
+						     IEC958_AES0_CON_NOT_COPYRIGHT |
+						     IEC958_AES0_CON_EMPHASIS;
+		ucontrol->value.iec958.status[1] = IEC958_AES1_CON_ORIGINAL |
+						     IEC958_AES1_CON_CATEGORY;
+		ucontrol->value.iec958.status[3] = IEC958_AES3_CON_FS;
+	} else {
+		ucontrol->value.iec958.status[0] = 0xff;
+		ucontrol->value.iec958.status[1] = 0xff;
+		ucontrol->value.iec958.status[2] = 0xff;
+		ucontrol->value.iec958.status[3] = 0xff;
+		ucontrol->value.iec958.status[4] = 0xff;
+	}
+	return 0;
+}
+
+static int snd_ice1712_spdif_maskp_get(snd_kcontrol_t * kcontrol,
+				       snd_ctl_elem_value_t * ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	if (ice->spdif.ops.default_get) {
+		ucontrol->value.iec958.status[0] = IEC958_AES0_NONAUDIO |
+						     IEC958_AES0_PROFESSIONAL |
+						     IEC958_AES0_PRO_FS |
+						     IEC958_AES0_PRO_EMPHASIS;
+		ucontrol->value.iec958.status[1] = IEC958_AES1_PRO_MODE;
+	} else {
+		ucontrol->value.iec958.status[0] = 0xff;
+		ucontrol->value.iec958.status[1] = 0xff;
+		ucontrol->value.iec958.status[2] = 0xff;
+		ucontrol->value.iec958.status[3] = 0xff;
+		ucontrol->value.iec958.status[4] = 0xff;
+	}
+	return 0;
+}
+
+static snd_kcontrol_new_t snd_ice1712_spdif_maskc __devinitdata =
+{
+	.access =		SNDRV_CTL_ELEM_ACCESS_READ,
+	.iface =		SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name =           SNDRV_CTL_NAME_IEC958("",PLAYBACK,CON_MASK),
+	.info =		snd_ice1712_spdif_mask_info,
+	.get =		snd_ice1712_spdif_maskc_get,
+};
+
+static snd_kcontrol_new_t snd_ice1712_spdif_maskp __devinitdata =
+{
+	.access =		SNDRV_CTL_ELEM_ACCESS_READ,
+	.iface =		SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name =           SNDRV_CTL_NAME_IEC958("",PLAYBACK,PRO_MASK),
+	.info =		snd_ice1712_spdif_mask_info,
+	.get =		snd_ice1712_spdif_maskp_get,
+};
+
+static int snd_ice1712_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_ice1712_spdif_stream_get(snd_kcontrol_t * kcontrol,
+					snd_ctl_elem_value_t * ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	if (ice->spdif.ops.stream_get)
+		ice->spdif.ops.stream_get(ice, ucontrol);
+	return 0;
+}
+
+static int snd_ice1712_spdif_stream_put(snd_kcontrol_t * kcontrol,
+					snd_ctl_elem_value_t * ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	if (ice->spdif.ops.stream_put)
+		return ice->spdif.ops.stream_put(ice, ucontrol);
+	return 0;
+}
+
+static snd_kcontrol_new_t snd_ice1712_spdif_stream __devinitdata =
+{
+	.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_ice1712_spdif_stream_info,
+	.get =		snd_ice1712_spdif_stream_get,
+	.put =		snd_ice1712_spdif_stream_put
+};
+
+int snd_ice1712_gpio_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;
+}
+
+int snd_ice1712_gpio_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	unsigned char mask = kcontrol->private_value & 0xff;
+	int invert = (kcontrol->private_value & (1<<24)) ? 1 : 0;
+	unsigned char saved[2];
+	
+	snd_ice1712_save_gpio_status(ice, saved);
+	ucontrol->value.integer.value[0] = (snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA) & mask ? 1 : 0) ^ invert;
+	snd_ice1712_restore_gpio_status(ice, saved);
+	return 0;
+}
+
+int snd_ice1712_gpio_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	unsigned char mask = kcontrol->private_value & 0xff;
+	int invert = (kcontrol->private_value & (1<<24)) ? mask : 0;
+	unsigned char saved[2];
+	int val, nval;
+
+	if (kcontrol->private_value & (1 << 31))
+		return -EPERM;
+	nval = (ucontrol->value.integer.value[0] ? mask : 0) ^ invert;
+	snd_ice1712_save_gpio_status(ice, saved);
+	val = snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA);
+	nval |= val & ~mask;
+	snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, nval);
+	snd_ice1712_restore_gpio_status(ice, saved);
+	return val != nval;
+}
+
+static int snd_ice1712_pro_spdif_master_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_ice1712_pro_spdif_master_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	
+	spin_lock_irqsave(&ice->reg_lock, flags);
+	ucontrol->value.integer.value[0] = inb(ICEMT(ice, RATE)) & ICE1712_SPDIF_MASTER ? 1 : 0;
+	spin_unlock_irqrestore(&ice->reg_lock, flags);
+	return 0;
+}
+
+static int snd_ice1712_pro_spdif_master_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	unsigned long flags;
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	int nval, change;
+
+	nval = ucontrol->value.integer.value[0] ? ICE1712_SPDIF_MASTER : 0;
+	spin_lock_irqsave(&ice->reg_lock, flags);
+	nval |= inb(ICEMT(ice, RATE)) & ~ICE1712_SPDIF_MASTER;
+	change = inb(ICEMT(ice, RATE)) != nval;
+	outb(nval, ICEMT(ice, RATE));
+	spin_unlock_irqrestore(&ice->reg_lock, flags);
+
+	if (ice->cs8427) {
+		/* change CS8427 clock source too */
+		snd_ice1712_cs8427_set_input_clock(ice, ucontrol->value.integer.value[0]);
+	}
+
+	return change;
+}
+
+static snd_kcontrol_new_t snd_ice1712_pro_spdif_master __devinitdata = {
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "Multi Track IEC958 Master",
+	.info = snd_ice1712_pro_spdif_master_info,
+	.get = snd_ice1712_pro_spdif_master_get,
+	.put = snd_ice1712_pro_spdif_master_put
+};
+
+/*
+ * routing
+ */
+static int snd_ice1712_pro_route_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	static char *texts[] = {
+		"PCM Out", /* 0 */
+		"H/W In 0", "H/W In 1", "H/W In 2", "H/W In 3", /* 1-4 */
+		"H/W In 4", "H/W In 5", "H/W In 6", "H/W In 7", /* 5-8 */
+		"IEC958 In L", "IEC958 In R", /* 9-10 */
+		"Digital Mixer", /* 11 - optional */
+	};
+	
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 1;
+	uinfo->value.enumerated.items = kcontrol->id.index < 2 ? 12 : 11;
+	if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
+		uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
+	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+	return 0;
+}
+
+static int snd_ice1712_pro_route_analog_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	int idx = kcontrol->id.index;
+	unsigned int val, cval;
+	val = inw(ICEMT(ice, ROUTE_PSDOUT03));
+	val >>= ((idx % 2) * 8) + ((idx / 2) * 2);
+	val &= 3;
+	cval = inl(ICEMT(ice, ROUTE_CAPTURE));
+	cval >>= ((idx / 2) * 8) + ((idx % 2) * 4);
+	if (val == 1 && idx < 2)
+		ucontrol->value.enumerated.item[0] = 11;
+	else if (val == 2)
+		ucontrol->value.enumerated.item[0] = (cval & 7) + 1;
+	else if (val == 3)
+		ucontrol->value.enumerated.item[0] = ((cval >> 3) & 1) + 9;
+	else
+		ucontrol->value.enumerated.item[0] = 0;
+	return 0;
+}
+
+static int snd_ice1712_pro_route_analog_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	int change, shift;
+	int idx = kcontrol->id.index;
+	unsigned int val, old_val, nval;
+	
+	/* update PSDOUT */
+	if (ucontrol->value.enumerated.item[0] >= 11)
+		nval = idx < 2 ? 1 : 0; /* dig mixer (or pcm) */
+	else if (ucontrol->value.enumerated.item[0] >= 9)
+		nval = 3; /* spdif in */
+	else if (ucontrol->value.enumerated.item[0] >= 1)
+		nval = 2; /* analog in */
+	else
+		nval = 0; /* pcm */
+	shift = ((idx % 2) * 8) + ((idx / 2) * 2);
+	val = old_val = inw(ICEMT(ice, ROUTE_PSDOUT03));
+	val &= ~(0x03 << shift);
+	val |= nval << shift;
+	change = val != old_val;
+	if (change)
+		outw(val, ICEMT(ice, ROUTE_PSDOUT03));
+	if (nval < 2) /* dig mixer of pcm */
+		return change;
+
+	/* update CAPTURE */
+	val = old_val = inl(ICEMT(ice, ROUTE_CAPTURE));
+	shift = ((idx / 2) * 8) + ((idx % 2) * 4);
+	if (nval == 2) { /* analog in */
+		nval = ucontrol->value.enumerated.item[0] - 1;
+		val &= ~(0x07 << shift);
+		val |= nval << shift;
+	} else { /* spdif in */
+		nval = (ucontrol->value.enumerated.item[0] - 9) << 3;
+		val &= ~(0x08 << shift);
+		val |= nval << shift;
+	}
+	if (val != old_val) {
+		change = 1;
+		outl(val, ICEMT(ice, ROUTE_CAPTURE));
+	}
+	return change;
+}
+
+static int snd_ice1712_pro_route_spdif_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	int idx = kcontrol->id.index;
+	unsigned int val, cval;
+	val = inw(ICEMT(ice, ROUTE_SPDOUT));
+	cval = (val >> (idx * 4 + 8)) & 0x0f;
+	val = (val >> (idx * 2)) & 0x03;
+	if (val == 1)
+		ucontrol->value.enumerated.item[0] = 11;
+	else if (val == 2)
+		ucontrol->value.enumerated.item[0] = (cval & 7) + 1;
+	else if (val == 3)
+		ucontrol->value.enumerated.item[0] = ((cval >> 3) & 1) + 9;
+	else
+		ucontrol->value.enumerated.item[0] = 0;
+	return 0;
+}
+
+static int snd_ice1712_pro_route_spdif_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	int change, shift;
+	int idx = kcontrol->id.index;
+	unsigned int val, old_val, nval;
+	
+	/* update SPDOUT */
+	val = old_val = inw(ICEMT(ice, ROUTE_SPDOUT));
+	if (ucontrol->value.enumerated.item[0] >= 11)
+		nval = 1;
+	else if (ucontrol->value.enumerated.item[0] >= 9)
+		nval = 3;
+	else if (ucontrol->value.enumerated.item[0] >= 1)
+		nval = 2;
+	else
+		nval = 0;
+	shift = idx * 2;
+	val &= ~(0x03 << shift);
+	val |= nval << shift;
+	shift = idx * 4 + 8;
+	if (nval == 2) {
+		nval = ucontrol->value.enumerated.item[0] - 1;
+		val &= ~(0x07 << shift);
+		val |= nval << shift;
+	} else if (nval == 3) {
+		nval = (ucontrol->value.enumerated.item[0] - 9) << 3;
+		val &= ~(0x08 << shift);
+		val |= nval << shift;
+	}
+	change = val != old_val;
+	if (change)
+		outw(val, ICEMT(ice, ROUTE_SPDOUT));
+	return change;
+}
+
+static snd_kcontrol_new_t snd_ice1712_mixer_pro_analog_route __devinitdata = {
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "H/W Playback Route",
+	.info = snd_ice1712_pro_route_info,
+	.get = snd_ice1712_pro_route_analog_get,
+	.put = snd_ice1712_pro_route_analog_put,
+};
+
+static snd_kcontrol_new_t snd_ice1712_mixer_pro_spdif_route __devinitdata = {
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "IEC958 Playback Route",
+	.info = snd_ice1712_pro_route_info,
+	.get = snd_ice1712_pro_route_spdif_get,
+	.put = snd_ice1712_pro_route_spdif_put,
+};
+
+
+static int snd_ice1712_pro_volume_rate_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 1;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 255;
+	return 0;
+}
+
+static int snd_ice1712_pro_volume_rate_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	
+	spin_lock_irqsave(&ice->reg_lock, flags);
+	ucontrol->value.integer.value[0] = inb(ICEMT(ice, MONITOR_RATE));
+	spin_unlock_irqrestore(&ice->reg_lock, flags);
+	return 0;
+}
+
+static int snd_ice1712_pro_volume_rate_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	unsigned long flags;
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	int change;
+
+	spin_lock_irqsave(&ice->reg_lock, flags);
+	change = inb(ICEMT(ice, MONITOR_RATE)) != ucontrol->value.integer.value[0];
+	outb(ucontrol->value.integer.value[0], ICEMT(ice, MONITOR_RATE));
+	spin_unlock_irqrestore(&ice->reg_lock, flags);
+	return change;
+}
+
+static snd_kcontrol_new_t snd_ice1712_mixer_pro_volume_rate __devinitdata = {
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "Multi Track Volume Rate",
+	.info = snd_ice1712_pro_volume_rate_info,
+	.get = snd_ice1712_pro_volume_rate_get,
+	.put = snd_ice1712_pro_volume_rate_put
+};
+
+static int snd_ice1712_pro_peak_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 22;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = 255;
+	return 0;
+}
+
+static int snd_ice1712_pro_peak_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
+{
+	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	int idx;
+	
+	spin_lock_irqsave(&ice->reg_lock, flags);
+	for (idx = 0; idx < 22; idx++) {
+		outb(idx, ICEMT(ice, MONITOR_PEAKINDEX));
+		ucontrol->value.integer.value[idx] = inb(ICEMT(ice, MONITOR_PEAKDATA));
+	}
+	spin_unlock_irqrestore(&ice->reg_lock, flags);
+	return 0;
+}
+
+static snd_kcontrol_new_t snd_ice1712_mixer_pro_peak __devinitdata = {
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "Multi Track Peak",
+	.access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+	.info = snd_ice1712_pro_peak_info,
+	.get = snd_ice1712_pro_peak_get
+};
+
+/*
+ *
+ */
+
+static unsigned char __devinit snd_ice1712_read_i2c(ice1712_t *ice,
+						 unsigned char dev,
+						 unsigned char addr)
+{
+	long t = 0x10000;
+
+	outb(addr, ICEREG(ice, I2C_BYTE_ADDR));
+	outb(dev & ~ICE1712_I2C_WRITE, ICEREG(ice, I2C_DEV_ADDR));
+	while (t-- > 0 && (inb(ICEREG(ice, I2C_CTRL)) & ICE1712_I2C_BUSY)) ;
+	return inb(ICEREG(ice, I2C_DATA));
+}
+
+static int __devinit snd_ice1712_read_eeprom(ice1712_t *ice)
+{
+	int dev = 0xa0;		/* EEPROM device address */
+	unsigned int idx;
+
+	if ((inb(ICEREG(ice, I2C_CTRL)) & ICE1712_I2C_EEPROM) == 0) {
+		snd_printk("ICE1712 has not detected EEPROM\n");
+		return -EIO;
+	}
+	ice->eeprom.subvendor = (snd_ice1712_read_i2c(ice, dev, 0x00) << 0) |
+				(snd_ice1712_read_i2c(ice, dev, 0x01) << 8) | 
+				(snd_ice1712_read_i2c(ice, dev, 0x02) << 16) | 
+				(snd_ice1712_read_i2c(ice, dev, 0x03) << 24);
+	ice->eeprom.size = snd_ice1712_read_i2c(ice, dev, 0x04);
+	if (ice->eeprom.size < 28) {
+		snd_printk("invalid EEPROM (size = %i)\n", ice->eeprom.size);
+		return -EIO;
+	}
+	ice->eeprom.version = snd_ice1712_read_i2c(ice, dev, 0x05);
+	if (ice->eeprom.version != 1) {
+		snd_printk("invalid EEPROM version %i\n", ice->eeprom.version);
+		return -EIO;
+	}
+	ice->eeprom.codec = snd_ice1712_read_i2c(ice, dev, 0x06);
+	ice->eeprom.aclink = snd_ice1712_read_i2c(ice, dev, 0x07);
+	ice->eeprom.i2sID = snd_ice1712_read_i2c(ice, dev, 0x08);
+	ice->eeprom.spdif = snd_ice1712_read_i2c(ice, dev, 0x09);
+	ice->eeprom.gpiomask = snd_ice1712_read_i2c(ice, dev, 0x0a);
+	ice->eeprom.gpiostate = snd_ice1712_read_i2c(ice, dev, 0x0b);
+	ice->eeprom.gpiodir = snd_ice1712_read_i2c(ice, dev, 0x0c);
+	ice->eeprom.ac97main = (snd_ice1712_read_i2c(ice, dev, 0x0d) << 0) |
+			       (snd_ice1712_read_i2c(ice, dev, 0x0e) << 8);
+	ice->eeprom.ac97pcm = (snd_ice1712_read_i2c(ice, dev, 0x0f) << 0) |
+			      (snd_ice1712_read_i2c(ice, dev, 0x10) << 8);
+	ice->eeprom.ac97rec = (snd_ice1712_read_i2c(ice, dev, 0x11) << 0) |
+			      (snd_ice1712_read_i2c(ice, dev, 0x12) << 8);
+	ice->eeprom.ac97recsrc = snd_ice1712_read_i2c(ice, dev, 0x13) << 0;
+	for (idx = 0; idx < 4; idx++) {
+		ice->eeprom.dacID[idx] = snd_ice1712_read_i2c(ice, dev, 0x14 + idx);
+		ice->eeprom.adcID[idx] = snd_ice1712_read_i2c(ice, dev, 0x18 + idx);
+	}
+	for (idx = 0x1c; idx < ice->eeprom.size && idx < 0x1c + sizeof(ice->eeprom.extra); idx++)
+		ice->eeprom.extra[idx - 0x1c] = snd_ice1712_read_i2c(ice, dev, idx);
+	return 0;
+}
+
+
+
+static int __devinit snd_ice1712_chip_init(ice1712_t *ice)
+{
+	outb(ICE1712_RESET | ICE1712_NATIVE, ICEREG(ice, CONTROL));
+	udelay(200);
+	outb(ICE1712_NATIVE, ICEREG(ice, CONTROL));
+	udelay(200);
+	pci_write_config_byte(ice->pci, 0x60, ice->eeprom.codec);
+	pci_write_config_byte(ice->pci, 0x61, ice->eeprom.aclink);
+	pci_write_config_byte(ice->pci, 0x62, ice->eeprom.i2sID);
+	pci_write_config_byte(ice->pci, 0x63, ice->eeprom.spdif);
+	if (ice->eeprom.subvendor != ICE1712_SUBDEVICE_STDSP24) {
+		ice->gpio_write_mask = ice->eeprom.gpiomask;
+		ice->gpio_direction = ice->eeprom.gpiodir;
+		snd_ice1712_write(ice, ICE1712_IREG_GPIO_WRITE_MASK, ice->eeprom.gpiomask);
+		snd_ice1712_write(ice, ICE1712_IREG_GPIO_DIRECTION, ice->eeprom.gpiodir);
+		snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, ice->eeprom.gpiostate);
+	} else {
+		ice->gpio_write_mask = 0xc0;
+		ice->gpio_direction = 0xff;
+		snd_ice1712_write(ice, ICE1712_IREG_GPIO_WRITE_MASK, 0xc0);
+		snd_ice1712_write(ice, ICE1712_IREG_GPIO_DIRECTION, 0xff);
+		snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, ICE1712_STDSP24_CLOCK_BIT);
+	}
+	snd_ice1712_write(ice, ICE1712_IREG_PRO_POWERDOWN, 0);
+	if (!(ice->eeprom.codec & ICE1712_CFG_NO_CON_AC97)) {
+		outb(ICE1712_AC97_WARM, ICEREG(ice, AC97_CMD));
+		udelay(100);
+		outb(0, ICEREG(ice, AC97_CMD));
+		udelay(200);
+		snd_ice1712_write(ice, ICE1712_IREG_CONSUMER_POWERDOWN, 0);
+	}
+
+	return 0;
+}
+
+
+int __devinit snd_ice1712_spdif_build_controls(ice1712_t *ice)
+{
+	int err;
+	snd_kcontrol_t *kctl;
+
+	snd_assert(ice->pcm_pro != NULL, return -EIO);
+	err = snd_ctl_add(ice->card, kctl = snd_ctl_new1(&snd_ice1712_spdif_default, ice));
+	if (err < 0)
+		return err;
+	kctl->id.device = ice->pcm_pro->device;
+	err = snd_ctl_add(ice->card, kctl = snd_ctl_new1(&snd_ice1712_spdif_maskc, ice));
+	if (err < 0)
+		return err;
+	kctl->id.device = ice->pcm_pro->device;
+	err = snd_ctl_add(ice->card, kctl = snd_ctl_new1(&snd_ice1712_spdif_maskp, ice));
+	if (err < 0)
+		return err;
+	kctl->id.device = ice->pcm_pro->device;
+	err = snd_ctl_add(ice->card, kctl = snd_ctl_new1(&snd_ice1712_spdif_stream, ice));
+	if (err < 0)
+		return err;
+	kctl->id.device = ice->pcm_pro->device;
+	ice->spdif.stream_ctl = kctl;
+	return 0;
+}
+
+
+static int __devinit snd_ice1712_build_controls(ice1712_t *ice)
+{
+	unsigned int idx;
+	snd_kcontrol_t *kctl;
+	int err;
+
+	err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_eeprom, ice));
+	if (err < 0)
+		return err;
+	err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_pro_spdif_master, ice));
+	if (err < 0)
+		return err;
+	for (idx = 0; idx < ice->num_total_dacs; idx++) {
+		kctl = snd_ctl_new1(&snd_ice1712_mixer_pro_analog_route, ice);
+		if (kctl == NULL)
+			return -ENOMEM;
+		kctl->id.index = idx;
+		err = snd_ctl_add(ice->card, kctl);
+		if (err < 0)
+			return err;
+	}
+	for (idx = 0; idx < 2; idx++) {
+		kctl = snd_ctl_new1(&snd_ice1712_mixer_pro_spdif_route, ice);
+		if (kctl == NULL)
+			return -ENOMEM;
+		kctl->id.index = idx;
+		err = snd_ctl_add(ice->card, kctl);
+		if (err < 0)
+			return err;
+	}
+	err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_mixer_pro_volume_rate, ice));
+	if (err < 0)
+		return err;
+	err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_mixer_pro_peak, ice));
+	if (err < 0)
+		return err;
+	return 0;
+}
+
+static int snd_ice1712_free(ice1712_t *ice)
+{
+	if (ice->res_port == NULL)
+		goto __hw_end;
+	/* mask all interrupts */
+	outb(0xc0, ICEMT(ice, IRQ));
+	outb(0xff, ICEREG(ice, IRQMASK));
+	/* --- */
+      __hw_end:
+	snd_ice1712_proc_done(ice);
+	if (ice->irq >= 0) {
+		synchronize_irq(ice->irq);
+		free_irq(ice->irq, (void *) ice);
+	}
+	if (ice->res_port) {
+		release_resource(ice->res_port);
+		kfree_nocheck(ice->res_port);
+	}
+	if (ice->res_ddma_port) {
+		release_resource(ice->res_ddma_port);
+		kfree_nocheck(ice->res_ddma_port);
+	}
+	if (ice->res_dmapath_port) {
+		release_resource(ice->res_dmapath_port);
+		kfree_nocheck(ice->res_dmapath_port);
+	}
+	if (ice->res_profi_port) {
+		release_resource(ice->res_profi_port);
+		kfree_nocheck(ice->res_profi_port);
+	}
+	snd_magic_kfree(ice);
+	return 0;
+}
+
+static int snd_ice1712_dev_free(snd_device_t *device)
+{
+	ice1712_t *ice = snd_magic_cast(ice1712_t, device->device_data, return -ENXIO);
+	return snd_ice1712_free(ice);
+}
+
+static int __devinit snd_ice1712_create(snd_card_t * card,
+				     struct pci_dev *pci,
+				     int omni,
+				     ice1712_t ** r_ice1712)
+{
+	ice1712_t *ice;
+	int err;
+	static snd_device_ops_t ops = {
+		.dev_free =	snd_ice1712_dev_free,
+	};
+
+	*r_ice1712 = NULL;
+
+        /* enable PCI device */
+	if ((err = pci_enable_device(pci)) < 0)
+		return err;
+	/* check, if we can restrict PCI DMA transfers to 28 bits */
+	if (!pci_dma_supported(pci, 0x0fffffff)) {
+		snd_printk("architecture does not support 28bit PCI busmaster DMA\n");
+		return -ENXIO;
+	}
+	pci_set_dma_mask(pci, 0x0fffffff);
+
+	ice = snd_magic_kcalloc(ice1712_t, 0, GFP_KERNEL);
+	if (ice == NULL)
+		return -ENOMEM;
+	ice->omni = omni ? 1 : 0;
+	spin_lock_init(&ice->reg_lock);
+	init_MUTEX(&ice->gpio_mutex);
+	ice->spdif.cs8403_bits =
+		ice->spdif.cs8403_stream_bits = (0x01 |	/* consumer format */
+						 0x10 |	/* no emphasis */
+						 0x20);	/* PCM encoder/decoder */
+	ice->card = card;
+	ice->pci = pci;
+	ice->irq = -1;
+	ice->port = pci_resource_start(pci, 0);
+	ice->ddma_port = pci_resource_start(pci, 1);
+	ice->dmapath_port = pci_resource_start(pci, 2);
+	ice->profi_port = pci_resource_start(pci, 3);
+	pci_set_master(pci);
+	pci_write_config_word(ice->pci, 0x40, 0x807f);
+	pci_write_config_word(ice->pci, 0x42, 0x0006);
+	snd_ice1712_proc_init(ice);
+	synchronize_irq(pci->irq);
+
+	if ((ice->res_port = request_region(ice->port, 32, "ICE1712 - Controller")) == NULL) {
+		snd_ice1712_free(ice);
+		snd_printk("unable to grab ports 0x%lx-0x%lx\n", ice->port, ice->port + 32 - 1);
+		return -EIO;
+	}
+	if ((ice->res_ddma_port = request_region(ice->ddma_port, 16, "ICE1712 - DDMA")) == NULL) {
+		snd_ice1712_free(ice);
+		snd_printk("unable to grab ports 0x%lx-0x%lx\n", ice->ddma_port, ice->ddma_port + 16 - 1);
+		return -EIO;
+	}
+	if ((ice->res_dmapath_port = request_region(ice->dmapath_port, 16, "ICE1712 - DMA path")) == NULL) {
+		snd_ice1712_free(ice);
+		snd_printk("unable to grab ports 0x%lx-0x%lx\n", ice->dmapath_port, ice->dmapath_port + 16 - 1);
+		return -EIO;
+	}
+	if ((ice->res_profi_port = request_region(ice->profi_port, 64, "ICE1712 - Professional")) == NULL) {
+		snd_ice1712_free(ice);
+		snd_printk("unable to grab ports 0x%lx-0x%lx\n", ice->profi_port, ice->profi_port + 16 - 1);
+		return -EIO;
+	}
+	if (request_irq(pci->irq, snd_ice1712_interrupt, SA_INTERRUPT|SA_SHIRQ, "ICE1712", (void *) ice)) {
+		snd_ice1712_free(ice);
+		snd_printk("unable to grab IRQ %d\n", pci->irq);
+		return -EIO;
+	}
+	ice->irq = pci->irq;
+
+	if (snd_ice1712_read_eeprom(ice) < 0) {
+		snd_ice1712_free(ice);
+		return -EIO;
+	}
+	if (snd_ice1712_chip_init(ice) < 0) {
+		snd_ice1712_free(ice);
+		return -EIO;
+	}
+
+	/* unmask used interrupts */
+	outb((ice->eeprom.codec & ICE1712_CFG_2xMPU401) == 0 ? ICE1712_IRQ_MPU2 : 0 |
+	     (ice->eeprom.codec & ICE1712_CFG_NO_CON_AC97) ? ICE1712_IRQ_PBKDS | ICE1712_IRQ_CONCAP | ICE1712_IRQ_CONPBK : 0,
+	     ICEREG(ice, IRQMASK));
+	outb(0x00, ICEMT(ice, IRQ));
+
+	if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, ice, &ops)) < 0) {
+		snd_ice1712_free(ice);
+		return err;
+	}
+
+	*r_ice1712 = ice;
+	return 0;
+}
+
+
+/*
+ *
+ * Registraton
+ *
+ */
+
+static struct snd_ice1712_card_info no_matched __devinitdata;
+
+static struct snd_ice1712_card_info *card_tables[] __devinitdata = {
+	snd_ice1712_hoontech_cards,
+	snd_ice1712_delta_cards,
+	snd_ice1712_ews_cards,
+	0,
+};
+
+
+static int __devinit snd_ice1712_probe(struct pci_dev *pci,
+				       const struct pci_device_id *id)
+{
+	static int dev;
+	snd_card_t *card;
+	ice1712_t *ice;
+	int pcm_dev = 0, err;
+	struct snd_ice1712_card_info **tbl, *c;
+
+	if (dev >= SNDRV_CARDS)
+		return -ENODEV;
+	if (!snd_enable[dev]) {
+		dev++;
+		return -ENOENT;
+	}
+
+	card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, 0);
+	if (card == NULL)
+		return -ENOMEM;
+
+	strcpy(card->driver, "ICE1712");
+	strcpy(card->shortname, "ICEnsemble ICE1712");
+	
+	if ((err = snd_ice1712_create(card, pci, snd_omni[dev], &ice)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+
+	for (tbl = card_tables; *tbl; tbl++) {
+		for (c = *tbl; c->subvendor; c++) {
+			if (c->subvendor == ice->eeprom.subvendor) {
+				strcpy(card->shortname, c->name);
+				if (c->chip_init) {
+					if ((err = c->chip_init(ice)) < 0) {
+						snd_card_free(card);
+						return err;
+					}
+				}
+				goto __found;
+			}
+		}
+	}
+	c = &no_matched;
+ __found:
+
+	if ((err = snd_ice1712_pcm_profi(ice, pcm_dev++, NULL)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	
+	if (!(ice->eeprom.codec & ICE1712_CFG_NO_CON_AC97))
+		if ((err = snd_ice1712_pcm(ice, pcm_dev++, NULL)) < 0) {
+			snd_card_free(card);
+			return err;
+		}
+
+	if ((err = snd_ice1712_ac97_mixer(ice)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+
+	if ((err = snd_ice1712_build_controls(ice)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+
+	if (c->build_controls) {
+		if ((err = c->build_controls(ice)) < 0) {
+			snd_card_free(card);
+			return err;
+		}
+	}
+
+	if (!(ice->eeprom.codec & ICE1712_CFG_NO_CON_AC97))
+		if ((err = snd_ice1712_pcm_ds(ice, pcm_dev++, NULL)) < 0) {
+			snd_card_free(card);
+			return err;
+		}
+
+	if (! c->no_mpu401) {
+		if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_ICE1712,
+					       ICEREG(ice, MPU1_CTRL), 1,
+					       ice->irq, 0,
+					       &ice->rmidi[0])) < 0) {
+			snd_card_free(card);
+			return err;
+		}
+
+		if (ice->eeprom.codec & ICE1712_CFG_2xMPU401)
+			if ((err = snd_mpu401_uart_new(card, 1, MPU401_HW_ICE1712,
+						       ICEREG(ice, MPU2_CTRL), 1,
+						       ice->irq, 0,
+						       &ice->rmidi[1])) < 0) {
+				snd_card_free(card);
+				return err;
+			}
+	}
+
+	sprintf(card->longname, "%s at 0x%lx, irq %i",
+		card->shortname, ice->port, ice->irq);
+
+	if ((err = snd_card_register(card)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	pci_set_drvdata(pci, card);
+	dev++;
+	return 0;
+}
+
+static void __devexit snd_ice1712_remove(struct pci_dev *pci)
+{
+	snd_card_free(pci_get_drvdata(pci));
+	pci_set_drvdata(pci, NULL);
+}
+
+static struct pci_driver driver = {
+	.name = "ICE1712",
+	.id_table = snd_ice1712_ids,
+	.probe = snd_ice1712_probe,
+	.remove = __devexit_p(snd_ice1712_remove),
+};
+
+static int __init alsa_card_ice1712_init(void)
+{
+	int err;
+
+	if ((err = pci_module_init(&driver)) < 0) {
+#ifdef MODULE
+		printk(KERN_ERR "ICE1712 soundcard not found or device busy\n");
+#endif
+		return err;
+	}
+	return 0;
+}
+
+static void __exit alsa_card_ice1712_exit(void)
+{
+	pci_unregister_driver(&driver);
+}
+
+module_init(alsa_card_ice1712_init)
+module_exit(alsa_card_ice1712_exit)
+
+#ifndef MODULE
+
+/* format is: snd-ice1712=snd_enable,snd_index,snd_id */
+
+static int __init alsa_card_ice1712_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-ice1712=", alsa_card_ice1712_setup);
+
+#endif /* ifndef MODULE */
diff -Nru a/sound/pci/ice1712/ice1712.h b/sound/pci/ice1712/ice1712.h
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/sound/pci/ice1712/ice1712.h	Mon Oct  7 11:25:40 2002
@@ -0,0 +1,388 @@
+#ifndef __SOUND_ICE1712_H
+#define __SOUND_ICE1712_H
+
+/*
+ *   ALSA driver for ICEnsemble ICE1712 (Envy24)
+ *
+ *	Copyright (c) 2000 Jaroslav Kysela <perex@suse.cz>
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */      
+
+#include <sound/control.h>
+#include <sound/ac97_codec.h>
+#include <sound/rawmidi.h>
+#include <sound/i2c.h>
+#include <sound/pcm.h>
+
+
+/*
+ *  Direct registers
+ */
+
+#define ICEREG(ice, x) ((ice)->port + ICE1712_REG_##x)
+
+#define ICE1712_REG_CONTROL		0x00	/* byte */
+#define   ICE1712_RESET			0x80	/* reset whole chip */
+#define   ICE1712_SERR_LEVEL		0x04	/* SERR# level otherwise edge */
+#define   ICE1712_NATIVE		0x01	/* native mode otherwise SB */
+#define ICE1712_REG_IRQMASK		0x01	/* byte */
+#define   ICE1712_IRQ_MPU1		0x80
+#define   ICE1712_IRQ_TIMER		0x40
+#define   ICE1712_IRQ_MPU2		0x20
+#define   ICE1712_IRQ_PROPCM		0x10
+#define   ICE1712_IRQ_FM		0x08	/* FM/MIDI - legacy */
+#define   ICE1712_IRQ_PBKDS		0x04	/* playback DS channels */
+#define   ICE1712_IRQ_CONCAP		0x02	/* consumer capture */
+#define   ICE1712_IRQ_CONPBK		0x01	/* consumer playback */
+#define ICE1712_REG_IRQSTAT		0x02	/* byte */
+/* look to ICE1712_IRQ_* */
+#define ICE1712_REG_INDEX		0x03	/* byte - indirect CCIxx regs */
+#define ICE1712_REG_DATA		0x04	/* byte - indirect CCIxx regs */
+#define ICE1712_REG_NMI_STAT1		0x05	/* byte */
+#define ICE1712_REG_NMI_DATA		0x06	/* byte */
+#define ICE1712_REG_NMI_INDEX		0x07	/* byte */
+#define ICE1712_REG_AC97_INDEX		0x08	/* byte */
+#define ICE1712_REG_AC97_CMD		0x09	/* byte */
+#define   ICE1712_AC97_COLD		0x80	/* cold reset */
+#define   ICE1712_AC97_WARM		0x40	/* warm reset */
+#define   ICE1712_AC97_WRITE		0x20	/* W: write, R: write in progress */
+#define   ICE1712_AC97_READ		0x10	/* W: read, R: read in progress */
+#define   ICE1712_AC97_READY		0x08	/* codec ready status bit */
+#define   ICE1712_AC97_PBK_VSR		0x02	/* playback VSR */
+#define   ICE1712_AC97_CAP_VSR		0x01	/* capture VSR */
+#define ICE1712_REG_AC97_DATA		0x0a	/* word (little endian) */
+#define ICE1712_REG_MPU1_CTRL		0x0c	/* byte */
+#define ICE1712_REG_MPU1_DATA		0x0d	/* byte */
+#define ICE1712_REG_I2C_DEV_ADDR	0x10	/* byte */
+#define   ICE1712_I2C_WRITE		0x01	/* write direction */
+#define ICE1712_REG_I2C_BYTE_ADDR	0x11	/* byte */
+#define ICE1712_REG_I2C_DATA		0x12	/* byte */
+#define ICE1712_REG_I2C_CTRL		0x13	/* byte */
+#define   ICE1712_I2C_EEPROM		0x80	/* EEPROM exists */
+#define   ICE1712_I2C_BUSY		0x01	/* busy bit */
+#define ICE1712_REG_CONCAP_ADDR		0x14	/* dword - consumer capture */
+#define ICE1712_REG_CONCAP_COUNT	0x18	/* word - current/base count */
+#define ICE1712_REG_SERR_SHADOW		0x1b	/* byte */
+#define ICE1712_REG_MPU2_CTRL		0x1c	/* byte */
+#define ICE1712_REG_MPU2_DATA		0x1d	/* byte */
+#define ICE1712_REG_TIMER		0x1e	/* word */
+
+/*
+ *  Indirect registers
+ */
+
+#define ICE1712_IREG_PBK_COUNT_HI	0x00
+#define ICE1712_IREG_PBK_COUNT_LO	0x01
+#define ICE1712_IREG_PBK_CTRL		0x02
+#define ICE1712_IREG_PBK_LEFT		0x03	/* left volume */
+#define ICE1712_IREG_PBK_RIGHT		0x04	/* right volume */
+#define ICE1712_IREG_PBK_SOFT		0x05	/* soft volume */
+#define ICE1712_IREG_PBK_RATE_LO	0x06
+#define ICE1712_IREG_PBK_RATE_MID	0x07
+#define ICE1712_IREG_PBK_RATE_HI	0x08
+#define ICE1712_IREG_CAP_COUNT_HI	0x10
+#define ICE1712_IREG_CAP_COUNT_LO	0x11
+#define ICE1712_IREG_CAP_CTRL		0x12
+#define ICE1712_IREG_GPIO_DATA		0x20
+#define ICE1712_IREG_GPIO_WRITE_MASK	0x21
+#define ICE1712_IREG_GPIO_DIRECTION	0x22
+#define ICE1712_IREG_CONSUMER_POWERDOWN	0x30
+#define ICE1712_IREG_PRO_POWERDOWN	0x31
+
+/*
+ *  Consumer section direct DMA registers
+ */
+
+#define ICEDS(ice, x) ((ice)->dmapath_port + ICE1712_DS_##x)
+ 
+#define ICE1712_DS_INTMASK		0x00	/* word - interrupt mask */
+#define ICE1712_DS_INTSTAT		0x02	/* word - interrupt status */
+#define ICE1712_DS_DATA			0x04	/* dword - channel data */
+#define ICE1712_DS_INDEX		0x08	/* dword - channel index */
+
+/*
+ *  Consumer section channel registers
+ */
+ 
+#define ICE1712_DSC_ADDR0		0x00	/* dword - base address 0 */
+#define ICE1712_DSC_COUNT0		0x01	/* word - count 0 */
+#define ICE1712_DSC_ADDR1		0x02	/* dword - base address 1 */
+#define ICE1712_DSC_COUNT1		0x03	/* word - count 1 */
+#define ICE1712_DSC_CONTROL		0x04	/* byte - control & status */
+#define   ICE1712_BUFFER1		0x80	/* buffer1 is active */
+#define   ICE1712_BUFFER1_AUTO		0x40	/* buffer1 auto init */
+#define   ICE1712_BUFFER0_AUTO		0x20	/* buffer0 auto init */
+#define   ICE1712_FLUSH			0x10	/* flush FIFO */
+#define   ICE1712_STEREO		0x08	/* stereo */
+#define   ICE1712_16BIT			0x04	/* 16-bit data */
+#define   ICE1712_PAUSE			0x02	/* pause */
+#define   ICE1712_START			0x01	/* start */
+#define ICE1712_DSC_RATE		0x05	/* dword - rate */
+#define ICE1712_DSC_VOLUME		0x06	/* word - volume control */
+
+/* 
+ *  Professional multi-track direct control registers
+ */
+
+#define ICEMT(ice, x) ((ice)->profi_port + ICE1712_MT_##x)
+
+#define ICE1712_MT_IRQ			0x00	/* byte - interrupt mask */
+#define   ICE1712_MULTI_CAPTURE		0x80	/* capture IRQ */
+#define   ICE1712_MULTI_PLAYBACK	0x40	/* playback IRQ */
+#define   ICE1712_MULTI_CAPSTATUS	0x02	/* capture IRQ status */
+#define   ICE1712_MULTI_PBKSTATUS	0x01	/* playback IRQ status */
+#define ICE1712_MT_RATE			0x01	/* byte - sampling rate select */
+#define   ICE1712_SPDIF_MASTER		0x10	/* S/PDIF input is master clock */
+#define ICE1712_MT_I2S_FORMAT		0x02	/* byte - I2S data format */
+#define ICE1712_MT_AC97_INDEX		0x04	/* byte - AC'97 index */
+#define ICE1712_MT_AC97_CMD		0x05	/* byte - AC'97 command & status */
+/* look to ICE1712_AC97_* */
+#define ICE1712_MT_AC97_DATA		0x06	/* word - AC'97 data */
+#define ICE1712_MT_PLAYBACK_ADDR	0x10	/* dword - playback address */
+#define ICE1712_MT_PLAYBACK_SIZE	0x14	/* word - playback size */
+#define ICE1712_MT_PLAYBACK_COUNT	0x16	/* word - playback count */
+#define ICE1712_MT_PLAYBACK_CONTROL	0x18	/* byte - control */
+#define   ICE1712_CAPTURE_START_SHADOW	0x04	/* capture start */
+#define   ICE1712_PLAYBACK_PAUSE	0x02	/* playback pause */
+#define   ICE1712_PLAYBACK_START	0x01	/* playback start */
+#define ICE1712_MT_CAPTURE_ADDR		0x20	/* dword - capture address */
+#define ICE1712_MT_CAPTURE_SIZE		0x24	/* word - capture size */
+#define ICE1712_MT_CAPTURE_COUNT	0x26	/* word - capture count */
+#define ICE1712_MT_CAPTURE_CONTROL	0x28	/* byte - control */
+#define   ICE1712_CAPTURE_START		0x01	/* capture start */
+#define ICE1712_MT_ROUTE_PSDOUT03	0x30	/* word */
+#define ICE1712_MT_ROUTE_SPDOUT		0x32	/* word */
+#define ICE1712_MT_ROUTE_CAPTURE	0x34	/* dword */
+#define ICE1712_MT_MONITOR_VOLUME	0x38	/* word */
+#define ICE1712_MT_MONITOR_INDEX	0x3a	/* byte */
+#define ICE1712_MT_MONITOR_RATE		0x3b	/* byte */
+#define ICE1712_MT_MONITOR_ROUTECTRL	0x3c	/* byte */
+#define   ICE1712_ROUTE_AC97		0x01	/* route digital mixer output to AC'97 */
+#define ICE1712_MT_MONITOR_PEAKINDEX	0x3e	/* byte */
+#define ICE1712_MT_MONITOR_PEAKDATA	0x3f	/* byte */
+
+/*
+ *  Codec configuration bits
+ */
+
+/* PCI[60] System Configuration */
+#define ICE1712_CFG_CLOCK	0xc0
+#define   ICE1712_CFG_CLOCK512	0x00	/* 22.5692Mhz, 44.1kHz*512 */
+#define   ICE1712_CFG_CLOCK384  0x40	/* 16.9344Mhz, 44.1kHz*384 */
+#define   ICE1712_CFG_EXT	0x80	/* external clock */
+#define ICE1712_CFG_2xMPU401	0x20	/* two MPU401 UARTs */
+#define ICE1712_CFG_NO_CON_AC97 0x10	/* consumer AC'97 codec is not present */
+#define ICE1712_CFG_ADC_MASK	0x0c	/* one, two, three, four stereo ADCs */
+#define ICE1712_CFG_DAC_MASK	0x03	/* one, two, three, four stereo DACs */
+/* PCI[61] AC-Link Configuration */
+#define ICE1712_CFG_PRO_I2S	0x80	/* multitrack converter: I2S or AC'97 */
+#define ICE1712_CFG_AC97_PACKED	0x01	/* split or packed mode - AC'97 */
+/* PCI[62] I2S Features */
+#define ICE1712_CFG_I2S_VOLUME	0x80	/* volume/mute capability */
+#define ICE1712_CFG_I2S_96KHZ	0x40	/* supports 96kHz sampling */
+#define ICE1712_CFG_I2S_RESMASK	0x30	/* resolution mask, 16,18,20,24-bit */
+#define ICE1712_CFG_I2S_OTHER	0x0f	/* other I2S IDs */
+/* PCI[63] S/PDIF Configuration */
+#define ICE1712_CFG_I2S_CHIPID	0xfc	/* I2S chip ID */
+#define ICE1712_CFG_SPDIF_IN	0x02	/* S/PDIF input is present */
+#define ICE1712_CFG_SPDIF_OUT	0x01	/* S/PDIF output is present */
+
+/*
+ * DMA mode values
+ * identical with DMA_XXX on i386 architecture.
+ */
+#define ICE1712_DMA_MODE_WRITE		0x48
+#define ICE1712_DMA_AUTOINIT		0x10
+
+
+/*
+ *  
+ */
+
+typedef struct _snd_ice1712 ice1712_t;
+typedef struct snd_ak4524 ak4524_t;
+
+typedef struct {
+	unsigned int subvendor;	/* PCI[2c-2f] */
+	unsigned char size;	/* size of EEPROM image in bytes */
+	unsigned char version;	/* must be 1 */
+	unsigned char codec;	/* codec configuration PCI[60] */
+	unsigned char aclink;	/* ACLink configuration PCI[61] */
+	unsigned char i2sID;	/* PCI[62] */
+	unsigned char spdif;	/* S/PDIF configuration PCI[63] */
+	unsigned char gpiomask;	/* GPIO initial mask, 0 = write, 1 = don't */
+	unsigned char gpiostate; /* GPIO initial state */
+	unsigned char gpiodir;	/* GPIO direction state */
+	unsigned short ac97main;
+	unsigned short ac97pcm;
+	unsigned short ac97rec;
+	unsigned char ac97recsrc;
+	unsigned char dacID[4];	/* I2S IDs for DACs */
+	unsigned char adcID[4];	/* I2S IDs for ADCs */
+	unsigned char extra[4];
+} ice1712_eeprom_t;
+
+struct snd_ak4524 {
+	int num_adcs;			/* AK4524 or AK4528 ADCs */
+	int num_dacs;			/* AK4524 or AK4528 DACs */
+	unsigned char images[4][8];
+	unsigned char ipga_gain[4][2];
+	/* */
+	unsigned int is_ak4528: 1;	/* AK4524 or AK4528 */
+	unsigned int cif: 1;
+	unsigned char data_mask;
+	unsigned char clk_mask;
+	unsigned char codecs_mask;
+	unsigned char add_flags;
+	struct snd_ak4524_ops {
+		int (*start)(ice1712_t *, unsigned char *, int);
+		void (*stop)(ice1712_t *, unsigned char *);
+		void (*set_rate_val)(ice1712_t *, unsigned char);
+	} ops;
+};
+
+struct snd_ice1712_spdif {
+	unsigned char cs8403_bits;
+	unsigned char cs8403_stream_bits;
+	snd_kcontrol_t *stream_ctl;
+
+	struct snd_ice1712_spdif_ops {
+		void (*open)(ice1712_t *, snd_pcm_substream_t *);
+		void (*setup)(ice1712_t *, snd_pcm_substream_t *);
+		void (*close)(ice1712_t *, snd_pcm_substream_t *);
+		void (*default_get)(ice1712_t *, snd_ctl_elem_value_t * ucontrol);
+		int (*default_put)(ice1712_t *, snd_ctl_elem_value_t * ucontrol);
+		void (*stream_get)(ice1712_t *, snd_ctl_elem_value_t * ucontrol);
+		int (*stream_put)(ice1712_t *, snd_ctl_elem_value_t * ucontrol);
+	} ops;
+};
+
+
+struct _snd_ice1712 {
+	unsigned long conp_dma_size;
+	unsigned long conc_dma_size;
+	unsigned long prop_dma_size;
+	unsigned long proc_dma_size;
+	int irq;
+
+	unsigned long port;
+	struct resource *res_port;
+	unsigned long ddma_port;
+	struct resource *res_ddma_port;
+	unsigned long dmapath_port;
+	struct resource *res_dmapath_port;
+	unsigned long profi_port;
+	struct resource *res_profi_port;
+
+	unsigned int config;	/* system configuration */
+
+	struct pci_dev *pci;
+	snd_card_t *card;
+	snd_pcm_t *pcm;
+	snd_pcm_t *pcm_ds;
+	snd_pcm_t *pcm_pro;
+        snd_pcm_substream_t *playback_con_substream;
+        snd_pcm_substream_t *playback_con_substream_ds[6];
+        snd_pcm_substream_t *capture_con_substream;
+        snd_pcm_substream_t *playback_pro_substream;
+        snd_pcm_substream_t *capture_pro_substream;
+	unsigned int playback_pro_size;
+	unsigned int capture_pro_size;
+	unsigned int playback_con_virt_addr[6];
+	unsigned int playback_con_active_buf[6];
+	unsigned int capture_con_virt_addr;
+	unsigned int ac97_ext_id;
+	ac97_t *ac97;
+	snd_rawmidi_t *rmidi[2];
+
+	spinlock_t reg_lock;
+	struct semaphore gpio_mutex;
+	snd_info_entry_t *proc_entry;
+
+	ice1712_eeprom_t eeprom;
+
+	unsigned int pro_volumes[20];
+	int omni: 1;			/* Delta Omni I/O */
+	int num_total_dacs;		/* total DACs */
+	unsigned char hoontech_boxbits[4];
+	unsigned int hoontech_config;
+	unsigned short hoontech_boxconfig[4];
+
+	struct snd_ak4524 ak4524;
+	struct snd_ice1712_spdif spdif;
+
+	snd_i2c_bus_t *i2c;		/* I2C bus */
+	snd_i2c_device_t *cs8404;	/* CS8404A I2C device */
+	snd_i2c_device_t *cs8427;	/* CS8427 I2C device */
+	snd_i2c_device_t *i2cdevs[2];	/* additional i2c devices */
+	
+	unsigned char gpio_direction, gpio_write_mask;
+};
+
+#define chip_t ice1712_t
+
+
+#define ICE1712_GPIO(xiface, xname, xindex, mask, invert, xaccess) \
+{ .iface = xiface, .name = xname, .access = xaccess, .info = snd_ice1712_gpio_info, \
+  .get = snd_ice1712_gpio_get, .put = snd_ice1712_gpio_put, \
+  .private_value = mask | (invert << 24) }
+
+int snd_ice1712_gpio_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo);
+int snd_ice1712_gpio_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol);
+int snd_ice1712_gpio_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol);
+
+void snd_ice1712_gpio_write_bits(ice1712_t *ice, int mask, int bits);
+void snd_ice1712_save_gpio_status(ice1712_t *ice, unsigned char *tmp);
+void snd_ice1712_restore_gpio_status(ice1712_t *ice, unsigned char *tmp);
+
+
+int snd_ice1712_spdif_build_controls(ice1712_t *ice);
+
+void snd_ice1712_ak4524_write(ice1712_t *ice, int chip, unsigned char addr, unsigned char data);
+void snd_ice1712_ak4524_reset(ice1712_t *ice, int state);
+void snd_ice1712_ak4524_init(ice1712_t *ice);
+int snd_ice1712_ak4524_build_controls(ice1712_t *ice);
+
+int snd_ice1712_init_cs8427(ice1712_t *ice, int addr);
+
+static inline void snd_ice1712_write(ice1712_t * ice, u8 addr, u8 data)
+{
+	outb(addr, ICEREG(ice, INDEX));
+	outb(data, ICEREG(ice, DATA));
+}
+
+static inline u8 snd_ice1712_read(ice1712_t * ice, u8 addr)
+{
+	outb(addr, ICEREG(ice, INDEX));
+	return inb(ICEREG(ice, DATA));
+}
+
+
+/*
+ * entry pointer
+ */
+
+struct snd_ice1712_card_info {
+	unsigned int subvendor;
+	char *name;
+	int (*chip_init)(ice1712_t *);
+	int (*build_controls)(ice1712_t *);
+	int no_mpu401: 1;
+};
+
+
+#endif /* __SOUND_ICE1712_H */
diff -Nru a/sound/pci/ice1712.c b/sound/pci/ice1712.c
--- a/sound/pci/ice1712.c	Mon Oct  7 11:25:36 2002
+++ /dev/null	Wed Dec 31 16:00:00 1969
@@ -1,4375 +0,0 @@
-/*
- *   ALSA driver for ICEnsemble ICE1712 (Envy24)
- *
- *	Copyright (c) 2000 Jaroslav Kysela <perex@suse.cz>
- *
- *   This program is free software; you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License as published by
- *   the Free Software Foundation; either version 2 of the License, or
- *   (at your option) any later version.
- *
- *   This program is distributed in the hope that it will be useful,
- *   but WITHOUT ANY WARRANTY; without even the implied warranty of
- *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *   GNU General Public License for more details.
- *
- *   You should have received a copy of the GNU General Public License
- *   along with this program; if not, write to the Free Software
- *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
- *
- */      
-
-/*
-  NOTES:
-  - spdif nonaudio consumer mode does not work (at least with my
-    Sony STR-DB830)
-*/
-
-#include <sound/driver.h>
-#include <asm/io.h>
-#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>
-#include <sound/pcm.h>
-#include <sound/ac97_codec.h>
-#include <sound/mpu401.h>
-#include <sound/i2c.h>
-#include <sound/cs8427.h>
-#include <sound/info.h>
-#define SNDRV_GET_ID
-#include <sound/initval.h>
-
-#include <sound/asoundef.h>
-
-#define SND_CS8403
-#define SND_CS8404
-#include <sound/cs8403.h>
-
-MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
-MODULE_DESCRIPTION("ICEnsemble ICE1712 (Envy24)");
-MODULE_LICENSE("GPL");
-MODULE_CLASSES("{sound}");
-MODULE_DEVICES("{{Hoontech SoundTrack DSP 24},"
-		"{MidiMan M Audio,Delta 1010},"
-		"{MidiMan M Audio,Delta DiO 2496},"
-		"{MidiMan M Audio,Delta 66},"
-		"{MidiMan M Audio,Delta 44},"
-		"{MidiMan M Audio,Audiophile 24/96},"
-		"{TerraTec,EWX 24/96},"
-		"{TerraTec,EWS 88MT},"
-		"{TerraTec,EWS 88D},"
-		"{TerraTec,DMX 6Fire},"
-		"{ICEnsemble,Generic ICE1712},"
-		"{ICEnsemble,Generic Envy24}}");
-
-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 */
-static int snd_omni[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS-1)] = 0};	/* Delta44 & 66 Omni I/O support */
-
-MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
-MODULE_PARM_DESC(snd_index, "Index value for ICE1712 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 ICE1712 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 ICE1712 soundcard.");
-MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC);
-MODULE_PARM(snd_omni, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
-MODULE_PARM_DESC(snd_omni, "Enable Midiman M-Audio Delta Omni I/O support.");
-MODULE_PARM_SYNTAX(snd_omni, SNDRV_ENABLED "," SNDRV_ENABLE_DESC);
-
-#ifndef PCI_VENDOR_ID_ICE
-#define PCI_VENDOR_ID_ICE		0x1412
-#endif
-#ifndef PCI_DEVICE_ID_ICE_1712
-#define PCI_DEVICE_ID_ICE_1712		0x1712
-#endif
-
-#define ICE1712_SUBDEVICE_STDSP24	0x12141217	/* Hoontech SoundTrack Audio DSP 24 */
-#define ICE1712_SUBDEVICE_DELTA1010	0x121430d6
-#define ICE1712_SUBDEVICE_DELTADIO2496	0x121431d6
-#define ICE1712_SUBDEVICE_DELTA66	0x121432d6
-#define ICE1712_SUBDEVICE_DELTA44	0x121433d6
-#define ICE1712_SUBDEVICE_AUDIOPHILE	0x121434d6
-#define ICE1712_SUBDEVICE_DELTA1010LT	0x12143bd6
-#define ICE1712_SUBDEVICE_EWX2496	0x3b153011
-#define ICE1712_SUBDEVICE_EWS88MT	0x3b151511
-#define ICE1712_SUBDEVICE_EWS88D	0x3b152b11
-#define ICE1712_SUBDEVICE_DMX6FIRE	0x3b153811
-
-/*
- *  Direct registers
- */
-
-#define ICEREG(ice, x) ((ice)->port + ICE1712_REG_##x)
-
-#define ICE1712_REG_CONTROL		0x00	/* byte */
-#define   ICE1712_RESET			0x80	/* reset whole chip */
-#define   ICE1712_SERR_LEVEL		0x04	/* SERR# level otherwise edge */
-#define   ICE1712_NATIVE		0x01	/* native mode otherwise SB */
-#define ICE1712_REG_IRQMASK		0x01	/* byte */
-#define   ICE1712_IRQ_MPU1		0x80
-#define   ICE1712_IRQ_TIMER		0x40
-#define   ICE1712_IRQ_MPU2		0x20
-#define   ICE1712_IRQ_PROPCM		0x10
-#define   ICE1712_IRQ_FM		0x08	/* FM/MIDI - legacy */
-#define   ICE1712_IRQ_PBKDS		0x04	/* playback DS channels */
-#define   ICE1712_IRQ_CONCAP		0x02	/* consumer capture */
-#define   ICE1712_IRQ_CONPBK		0x01	/* consumer playback */
-#define ICE1712_REG_IRQSTAT		0x02	/* byte */
-/* look to ICE1712_IRQ_* */
-#define ICE1712_REG_INDEX		0x03	/* byte - indirect CCIxx regs */
-#define ICE1712_REG_DATA		0x04	/* byte - indirect CCIxx regs */
-#define ICE1712_REG_NMI_STAT1		0x05	/* byte */
-#define ICE1712_REG_NMI_DATA		0x06	/* byte */
-#define ICE1712_REG_NMI_INDEX		0x07	/* byte */
-#define ICE1712_REG_AC97_INDEX		0x08	/* byte */
-#define ICE1712_REG_AC97_CMD		0x09	/* byte */
-#define   ICE1712_AC97_COLD		0x80	/* cold reset */
-#define   ICE1712_AC97_WARM		0x40	/* warm reset */
-#define   ICE1712_AC97_WRITE		0x20	/* W: write, R: write in progress */
-#define   ICE1712_AC97_READ		0x10	/* W: read, R: read in progress */
-#define   ICE1712_AC97_READY		0x08	/* codec ready status bit */
-#define   ICE1712_AC97_PBK_VSR		0x02	/* playback VSR */
-#define   ICE1712_AC97_CAP_VSR		0x01	/* capture VSR */
-#define ICE1712_REG_AC97_DATA		0x0a	/* word (little endian) */
-#define ICE1712_REG_MPU1_CTRL		0x0c	/* byte */
-#define ICE1712_REG_MPU1_DATA		0x0d	/* byte */
-#define ICE1712_REG_I2C_DEV_ADDR	0x10	/* byte */
-#define   ICE1712_I2C_WRITE		0x01	/* write direction */
-#define ICE1712_REG_I2C_BYTE_ADDR	0x11	/* byte */
-#define ICE1712_REG_I2C_DATA		0x12	/* byte */
-#define ICE1712_REG_I2C_CTRL		0x13	/* byte */
-#define   ICE1712_I2C_EEPROM		0x80	/* EEPROM exists */
-#define   ICE1712_I2C_BUSY		0x01	/* busy bit */
-#define ICE1712_REG_CONCAP_ADDR		0x14	/* dword - consumer capture */
-#define ICE1712_REG_CONCAP_COUNT	0x18	/* word - current/base count */
-#define ICE1712_REG_SERR_SHADOW		0x1b	/* byte */
-#define ICE1712_REG_MPU2_CTRL		0x1c	/* byte */
-#define ICE1712_REG_MPU2_DATA		0x1d	/* byte */
-#define ICE1712_REG_TIMER		0x1e	/* word */
-
-/*
- *  Indirect registers
- */
-
-#define ICE1712_IREG_PBK_COUNT_HI	0x00
-#define ICE1712_IREG_PBK_COUNT_LO	0x01
-#define ICE1712_IREG_PBK_CTRL		0x02
-#define ICE1712_IREG_PBK_LEFT		0x03	/* left volume */
-#define ICE1712_IREG_PBK_RIGHT		0x04	/* right volume */
-#define ICE1712_IREG_PBK_SOFT		0x05	/* soft volume */
-#define ICE1712_IREG_PBK_RATE_LO	0x06
-#define ICE1712_IREG_PBK_RATE_MID	0x07
-#define ICE1712_IREG_PBK_RATE_HI	0x08
-#define ICE1712_IREG_CAP_COUNT_HI	0x10
-#define ICE1712_IREG_CAP_COUNT_LO	0x11
-#define ICE1712_IREG_CAP_CTRL		0x12
-#define ICE1712_IREG_GPIO_DATA		0x20
-#define ICE1712_IREG_GPIO_WRITE_MASK	0x21
-#define ICE1712_IREG_GPIO_DIRECTION	0x22
-#define ICE1712_IREG_CONSUMER_POWERDOWN	0x30
-#define ICE1712_IREG_PRO_POWERDOWN	0x31
-
-/*
- *  Consumer section direct DMA registers
- */
-
-#define ICEDS(ice, x) ((ice)->dmapath_port + ICE1712_DS_##x)
- 
-#define ICE1712_DS_INTMASK		0x00	/* word - interrupt mask */
-#define ICE1712_DS_INTSTAT		0x02	/* word - interrupt status */
-#define ICE1712_DS_DATA			0x04	/* dword - channel data */
-#define ICE1712_DS_INDEX		0x08	/* dword - channel index */
-
-/*
- *  Consumer section channel registers
- */
- 
-#define ICE1712_DSC_ADDR0		0x00	/* dword - base address 0 */
-#define ICE1712_DSC_COUNT0		0x01	/* word - count 0 */
-#define ICE1712_DSC_ADDR1		0x02	/* dword - base address 1 */
-#define ICE1712_DSC_COUNT1		0x03	/* word - count 1 */
-#define ICE1712_DSC_CONTROL		0x04	/* byte - control & status */
-#define   ICE1712_BUFFER1		0x80	/* buffer1 is active */
-#define   ICE1712_BUFFER1_AUTO		0x40	/* buffer1 auto init */
-#define   ICE1712_BUFFER0_AUTO		0x20	/* buffer0 auto init */
-#define   ICE1712_FLUSH			0x10	/* flush FIFO */
-#define   ICE1712_STEREO		0x08	/* stereo */
-#define   ICE1712_16BIT			0x04	/* 16-bit data */
-#define   ICE1712_PAUSE			0x02	/* pause */
-#define   ICE1712_START			0x01	/* start */
-#define ICE1712_DSC_RATE		0x05	/* dword - rate */
-#define ICE1712_DSC_VOLUME		0x06	/* word - volume control */
-
-/* 
- *  Professional multi-track direct control registers
- */
-
-#define ICEMT(ice, x) ((ice)->profi_port + ICE1712_MT_##x)
-
-#define ICE1712_MT_IRQ			0x00	/* byte - interrupt mask */
-#define   ICE1712_MULTI_CAPTURE		0x80	/* capture IRQ */
-#define   ICE1712_MULTI_PLAYBACK	0x40	/* playback IRQ */
-#define   ICE1712_MULTI_CAPSTATUS	0x02	/* capture IRQ status */
-#define   ICE1712_MULTI_PBKSTATUS	0x01	/* playback IRQ status */
-#define ICE1712_MT_RATE			0x01	/* byte - sampling rate select */
-#define   ICE1712_SPDIF_MASTER		0x10	/* S/PDIF input is master clock */
-#define ICE1712_MT_I2S_FORMAT		0x02	/* byte - I2S data format */
-#define ICE1712_MT_AC97_INDEX		0x04	/* byte - AC'97 index */
-#define ICE1712_MT_AC97_CMD		0x05	/* byte - AC'97 command & status */
-/* look to ICE1712_AC97_* */
-#define ICE1712_MT_AC97_DATA		0x06	/* word - AC'97 data */
-#define ICE1712_MT_PLAYBACK_ADDR	0x10	/* dword - playback address */
-#define ICE1712_MT_PLAYBACK_SIZE	0x14	/* word - playback size */
-#define ICE1712_MT_PLAYBACK_COUNT	0x16	/* word - playback count */
-#define ICE1712_MT_PLAYBACK_CONTROL	0x18	/* byte - control */
-#define   ICE1712_CAPTURE_START_SHADOW	0x04	/* capture start */
-#define   ICE1712_PLAYBACK_PAUSE	0x02	/* playback pause */
-#define   ICE1712_PLAYBACK_START	0x01	/* playback start */
-#define ICE1712_MT_CAPTURE_ADDR		0x20	/* dword - capture address */
-#define ICE1712_MT_CAPTURE_SIZE		0x24	/* word - capture size */
-#define ICE1712_MT_CAPTURE_COUNT	0x26	/* word - capture count */
-#define ICE1712_MT_CAPTURE_CONTROL	0x28	/* byte - control */
-#define   ICE1712_CAPTURE_START		0x01	/* capture start */
-#define ICE1712_MT_ROUTE_PSDOUT03	0x30	/* word */
-#define ICE1712_MT_ROUTE_SPDOUT		0x32	/* word */
-#define ICE1712_MT_ROUTE_CAPTURE	0x34	/* dword */
-#define ICE1712_MT_MONITOR_VOLUME	0x38	/* word */
-#define ICE1712_MT_MONITOR_INDEX	0x3a	/* byte */
-#define ICE1712_MT_MONITOR_RATE		0x3b	/* byte */
-#define ICE1712_MT_MONITOR_ROUTECTRL	0x3c	/* byte */
-#define   ICE1712_ROUTE_AC97		0x01	/* route digital mixer output to AC'97 */
-#define ICE1712_MT_MONITOR_PEAKINDEX	0x3e	/* byte */
-#define ICE1712_MT_MONITOR_PEAKDATA	0x3f	/* byte */
-
-/*
- *  Codec configuration bits
- */
-
-/* PCI[60] System Configuration */
-#define ICE1712_CFG_CLOCK	0xc0
-#define   ICE1712_CFG_CLOCK512	0x00	/* 22.5692Mhz, 44.1kHz*512 */
-#define   ICE1712_CFG_CLOCK384  0x40	/* 16.9344Mhz, 44.1kHz*384 */
-#define   ICE1712_CFG_EXT	0x80	/* external clock */
-#define ICE1712_CFG_2xMPU401	0x20	/* two MPU401 UARTs */
-#define ICE1712_CFG_NO_CON_AC97 0x10	/* consumer AC'97 codec is not present */
-#define ICE1712_CFG_ADC_MASK	0x0c	/* one, two, three, four stereo ADCs */
-#define ICE1712_CFG_DAC_MASK	0x03	/* one, two, three, four stereo DACs */
-/* PCI[61] AC-Link Configuration */
-#define ICE1712_CFG_PRO_I2S	0x80	/* multitrack converter: I2S or AC'97 */
-#define ICE1712_CFG_AC97_PACKED	0x01	/* split or packed mode - AC'97 */
-/* PCI[62] I2S Features */
-#define ICE1712_CFG_I2S_VOLUME	0x80	/* volume/mute capability */
-#define ICE1712_CFG_I2S_96KHZ	0x40	/* supports 96kHz sampling */
-#define ICE1712_CFG_I2S_RESMASK	0x30	/* resolution mask, 16,18,20,24-bit */
-#define ICE1712_CFG_I2S_OTHER	0x0f	/* other I2S IDs */
-/* PCI[63] S/PDIF Configuration */
-#define ICE1712_CFG_I2S_CHIPID	0xfc	/* I2S chip ID */
-#define ICE1712_CFG_SPDIF_IN	0x02	/* S/PDIF input is present */
-#define ICE1712_CFG_SPDIF_OUT	0x01	/* S/PDIF output is present */
-
-/*
- *  MidiMan M-Audio Delta GPIO definitions
- */
-
-/* MidiMan M-Audio Delta1010 */
-#define ICE1712_DELTA_DFS 0x01		/* fast/slow sample rate mode */
-					/* (>48kHz must be 1) */
-#define ICE1712_DELTA_SPDIF_IN_STAT 0x02
-					/* S/PDIF input status */
-					/* 0 = valid signal is present */
-					/* all except Delta44 */
-					/* look to CS8414 datasheet */
-#define ICE1712_DELTA_SPDIF_OUT_STAT_CLOCK 0x04
-					/* S/PDIF output status clock */
-					/* (writting on rising edge - 0->1) */
-					/* all except Delta44 */
-					/* look to CS8404A datasheet */
-#define ICE1712_DELTA_SPDIF_OUT_STAT_DATA 0x08
-					/* S/PDIF output status data */
-					/* all except Delta44 */
-					/* look to CS8404A datasheet */
-/* MidiMan M-Audio DeltaDiO */
-/* 0x01 = DFS */
-/* 0x02 = SPDIF_IN_STAT */
-/* 0x04 = SPDIF_OUT_STAT_CLOCK */
-/* 0x08 = SPDIF_OUT_STAT_DATA */
-#define ICE1712_DELTA_SPDIF_INPUT_SELECT 0x10
-					/* coaxial (0), optical (1) */
-					/* S/PDIF input select*/
-
-/* MidiMan M-Audio Delta1010 */
-/* 0x01 = DFS */
-/* 0x02 = SPDIF_IN_STAT */
-/* 0x04 = SPDIF_OUT_STAT_CLOCK */
-/* 0x08 = SPDIF_OUT_STAT_DATA */
-#define ICE1712_DELTA_WORD_CLOCK_SELECT 0x10
-					/* 1 - clock are taken from S/PDIF input */
-					/* 0 - clock are taken from Word Clock input */
-					/* affected SPMCLKIN pin of Envy24 */
-#define ICE1712_DELTA_WORD_CLOCK_STATUS	0x20
-					/* 0 = valid word clock signal is present */
-
-/* MidiMan M-Audio Delta66 */
-/* 0x01 = DFS */
-/* 0x02 = SPDIF_IN_STAT */
-/* 0x04 = SPDIF_OUT_STAT_CLOCK */
-/* 0x08 = SPDIF_OUT_STAT_DATA */
-#define ICE1712_DELTA_CODEC_SERIAL_DATA 0x10
-					/* AKM4524 serial data */
-#define ICE1712_DELTA_CODEC_SERIAL_CLOCK 0x20
-					/* AKM4524 serial clock */
-					/* (writting on rising edge - 0->1 */
-#define ICE1712_DELTA_CODEC_CHIP_A	0x40
-#define ICE1712_DELTA_CODEC_CHIP_B	0x80
-					/* 1 - select chip A or B */
-
-/* MidiMan M-Audio Delta44 */
-/* 0x01 = DFS */
-/* 0x10 = CODEC_SERIAL_DATA */
-/* 0x20 = CODEC_SERIAL_CLOCK */
-/* 0x40 = CODEC_CHIP_A */
-/* 0x80 = CODEC_CHIP_B */
-
-/* MidiMan M-Audio Audiophile definitions */
-/* 0x01 = DFS */
-#define ICE1712_DELTA_AP_CCLK	0x02	/* SPI clock */
-					/* (clocking on rising edge - 0->1) */
-#define ICE1712_DELTA_AP_DIN	0x04	/* data input */
-#define ICE1712_DELTA_AP_DOUT	0x08	/* data output */
-#define ICE1712_DELTA_AP_CS_DIGITAL 0x10 /* CS8427 chip select */
-					/* low signal = select */
-#define ICE1712_DELTA_AP_CS_CODEC 0x20	/* AK4528 chip select */
-					/* low signal = select */
-
-/* Hoontech SoundTrack Audio DSP 24 GPIO definitions */
-
-#define ICE1712_STDSP24_0_BOX(r, x)	r[0] = ((r[0] & ~3) | ((x)&3))
-#define ICE1712_STDSP24_0_DAREAR(r, x)	r[0] = ((r[0] & ~4) | (((x)&1)<<2))
-#define ICE1712_STDSP24_1_CHN1(r, x)	r[1] = ((r[1] & ~1) | ((x)&1))
-#define ICE1712_STDSP24_1_CHN2(r, x)	r[1] = ((r[1] & ~2) | (((x)&1)<<1))
-#define ICE1712_STDSP24_1_CHN3(r, x)	r[1] = ((r[1] & ~4) | (((x)&1)<<2))
-#define ICE1712_STDSP24_2_CHN4(r, x)	r[2] = ((r[2] & ~1) | ((x)&1))
-#define ICE1712_STDSP24_2_MIDIIN(r, x)	r[2] = ((r[2] & ~2) | (((x)&1)<<1))
-#define ICE1712_STDSP24_2_MIDI1(r, x)	r[2] = ((r[2] & ~4) | (((x)&1)<<2))
-#define ICE1712_STDSP24_3_MIDI2(r, x)	r[3] = ((r[3] & ~1) | ((x)&1))
-#define ICE1712_STDSP24_3_MUTE(r, x)	r[3] = ((r[3] & ~2) | (((x)&1)<<1))
-#define ICE1712_STDSP24_3_INSEL(r, x)	r[3] = ((r[3] & ~4) | (((x)&1)<<2))
-#define ICE1712_STDSP24_SET_ADDR(r, a)	r[a&3] = ((r[a&3] & ~0x18) | (((a)&3)<<3))
-#define ICE1712_STDSP24_CLOCK(r, a, c)	r[a&3] = ((r[a&3] & ~0x20) | (((c)&1)<<5))
-#define ICE1712_STDSP24_CLOCK_BIT	(1<<5)
-
-/* Hoontech SoundTrack Audio DSP 24 box configuration definitions */
-
-#define ICE1712_STDSP24_DAREAR		(1<<0)
-#define ICE1712_STDSP24_MUTE		(1<<1)
-#define ICE1712_STDSP24_INSEL		(1<<2)
-
-#define ICE1712_STDSP24_BOX_CHN1	(1<<0)	/* input channel 1 */
-#define ICE1712_STDSP24_BOX_CHN2	(1<<1)	/* input channel 2 */
-#define ICE1712_STDSP24_BOX_CHN3	(1<<2)	/* input channel 3 */
-#define ICE1712_STDSP24_BOX_CHN4	(1<<3)	/* input channel 4 */
-#define ICE1712_STDSP24_BOX_MIDI1	(1<<8)
-#define ICE1712_STDSP24_BOX_MIDI2	(1<<9)
-
-/* TerraTec EWX 24/96 configuration definitions */
-
-#define ICE1712_EWX2496_AK4524_CS	0x01	/* AK4524 chip select; low = active */
-#define ICE1712_EWX2496_AIN_SEL		0x02	/* input sensitivity switch; high = louder */
-#define ICE1712_EWX2496_AOUT_SEL	0x04	/* output sensitivity switch; high = louder */
-#define ICE1712_EWX2496_RW		0x08	/* read/write switch for i2c; high = write  */
-#define ICE1712_EWX2496_SERIAL_DATA	0x10	/* i2c & ak4524 data */
-#define ICE1712_EWX2496_SERIAL_CLOCK	0x20	/* i2c & ak4524 clock */
-#define ICE1712_EWX2496_TX2		0x40	/* MIDI2 (not used) */
-#define ICE1712_EWX2496_RX2		0x80	/* MIDI2 (not used) */
-
-/* TerraTec EWS 88MT/D configuration definitions */
-/* RW, SDA snd SCLK are identical with EWX24/96 */
-#define ICE1712_EWS88_CS8414_RATE	0x07	/* CS8414 sample rate: gpio 0-2 */
-#define ICE1712_EWS88_RW		0x08	/* read/write switch for i2c; high = write  */
-#define ICE1712_EWS88_SERIAL_DATA	0x10	/* i2c & ak4524 data */
-#define ICE1712_EWS88_SERIAL_CLOCK	0x20	/* i2c & ak4524 clock */
-#define ICE1712_EWS88_TX2		0x40	/* MIDI2 (only on 88D) */
-#define ICE1712_EWS88_RX2		0x80	/* MIDI2 (only on 88D) */
-
-/* i2c address */
-#define ICE1712_EWS88MT_CS8404_ADDR	(0x40>>1)
-#define ICE1712_EWS88MT_INPUT_ADDR	(0x46>>1)
-#define ICE1712_EWS88MT_OUTPUT_ADDR	(0x48>>1)
-#define ICE1712_EWS88MT_OUTPUT_SENSE	0x40	/* mask */
-#define ICE1712_EWS88D_PCF_ADDR		(0x40>>1)
-
-/* TerraTec DMX 6Fire configuration definitions */
-#define ICE1712_6FIRE_AK4524_CS_MASK	0x07	/* AK4524 chip select #1-#3 */
-#define ICE1712_6FIRE_RW		0x08	/* read/write switch for i2c; high = write  */
-#define ICE1712_6FIRE_SERIAL_DATA	0x10	/* i2c & ak4524 data */
-#define ICE1712_6FIRE_SERIAL_CLOCK	0x20	/* i2c & ak4524 clock */
-#define ICE1712_6FIRE_TX2		0x40	/* MIDI2 */
-#define ICE1712_6FIRE_RX2		0x80	/* MIDI2 */
-
-#define ICE1712_6FIRE_CS8427_ADDR	(0x22>>1) /* ?? */
-
-/*
- * DMA mode values
- * identical with DMA_XXX on i386 architecture.
- */
-#define ICE1712_DMA_MODE_WRITE		0x48
-#define ICE1712_DMA_AUTOINIT		0x10
-
-
-/*
- *  
- */
-
-typedef struct _snd_ice1712 ice1712_t;
-
-typedef struct {
-	unsigned int subvendor;	/* PCI[2c-2f] */
-	unsigned char size;	/* size of EEPROM image in bytes */
-	unsigned char version;	/* must be 1 */
-	unsigned char codec;	/* codec configuration PCI[60] */
-	unsigned char aclink;	/* ACLink configuration PCI[61] */
-	unsigned char i2sID;	/* PCI[62] */
-	unsigned char spdif;	/* S/PDIF configuration PCI[63] */
-	unsigned char gpiomask;	/* GPIO initial mask, 0 = write, 1 = don't */
-	unsigned char gpiostate; /* GPIO initial state */
-	unsigned char gpiodir;	/* GPIO direction state */
-	unsigned short ac97main;
-	unsigned short ac97pcm;
-	unsigned short ac97rec;
-	unsigned char ac97recsrc;
-	unsigned char dacID[4];	/* I2S IDs for DACs */
-	unsigned char adcID[4];	/* I2S IDs for ADCs */
-	unsigned char extra[4];
-} ice1712_eeprom_t;
-
-struct _snd_ice1712 {
-	unsigned long conp_dma_size;
-	unsigned long conc_dma_size;
-	unsigned long prop_dma_size;
-	unsigned long proc_dma_size;
-	int irq;
-
-	unsigned long port;
-	struct resource *res_port;
-	unsigned long ddma_port;
-	struct resource *res_ddma_port;
-	unsigned long dmapath_port;
-	struct resource *res_dmapath_port;
-	unsigned long profi_port;
-	struct resource *res_profi_port;
-
-	unsigned int config;	/* system configuration */
-
-	struct pci_dev *pci;
-	snd_card_t *card;
-	snd_pcm_t *pcm;
-	snd_pcm_t *pcm_ds;
-	snd_pcm_t *pcm_pro;
-        snd_pcm_substream_t *playback_con_substream;
-        snd_pcm_substream_t *playback_con_substream_ds[6];
-        snd_pcm_substream_t *capture_con_substream;
-        snd_pcm_substream_t *playback_pro_substream;
-        snd_pcm_substream_t *capture_pro_substream;
-	unsigned int playback_pro_size;
-	unsigned int capture_pro_size;
-	unsigned int playback_con_virt_addr[6];
-	unsigned int playback_con_active_buf[6];
-	unsigned int capture_con_virt_addr;
-	unsigned int ac97_ext_id;
-	ac97_t *ac97;
-	snd_rawmidi_t *rmidi[2];
-
-	spinlock_t reg_lock;
-	struct semaphore gpio_mutex;
-	snd_info_entry_t *proc_entry;
-
-	ice1712_eeprom_t eeprom;
-
-	unsigned int pro_volumes[20];
-	int ak4528: 1,			/* AK4524 or AK4528 */
-	    omni: 1;			/* Delta Omni I/O */
-	int num_adcs;			/* AK4524 or AK4528 ADCs */
-	int num_dacs;			/* AK4524 or AK4528 DACs */
-	int num_total_dacs;		/* total DACs */
-	unsigned char ak4524_images[4][8];
-	unsigned char ak4524_ipga_gain[4][2];
-	unsigned char hoontech_boxbits[4];
-	unsigned int hoontech_config;
-	unsigned short hoontech_boxconfig[4];
-
-	snd_i2c_bus_t *i2c;		/* I2C bus */
-	snd_i2c_device_t *cs8404;	/* CS8404A I2C device */
-	snd_i2c_device_t *cs8427;	/* CS8427 I2C device */
-	snd_i2c_device_t *pcf8574[2];	/* PCF8574 Output/Input (EWS88MT) */
-	snd_i2c_device_t *pcf8575;	/* PCF8575 (EWS88D) */
-	
-	unsigned char cs8403_spdif_bits;
-	unsigned char cs8403_spdif_stream_bits;
-	snd_kcontrol_t *spdif_stream_ctl;
-
-	unsigned char gpio_direction, gpio_write_mask;
-};
-
-#define chip_t ice1712_t
-
-static struct pci_device_id snd_ice1712_ids[] __devinitdata = {
-	{ PCI_VENDOR_ID_ICE, PCI_DEVICE_ID_ICE_1712, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, },   /* ICE1712 */
-	{ 0, }
-};
-
-MODULE_DEVICE_TABLE(pci, snd_ice1712_ids);
-
-static int snd_ice1712_build_pro_mixer(ice1712_t *ice);
-static int snd_ice1712_build_controls(ice1712_t *ice);
-
-/*
- *  Basic I/O
- */
- 
-static inline void snd_ice1712_write(ice1712_t * ice, u8 addr, u8 data)
-{
-	outb(addr, ICEREG(ice, INDEX));
-	outb(data, ICEREG(ice, DATA));
-}
-
-static inline u8 snd_ice1712_read(ice1712_t * ice, u8 addr)
-{
-	outb(addr, ICEREG(ice, INDEX));
-	return inb(ICEREG(ice, DATA));
-}
-
-static inline void snd_ice1712_ds_write(ice1712_t * ice, u8 channel, u8 addr, u32 data)
-{
-	outb((channel << 4) | addr, ICEDS(ice, INDEX));
-	outl(data, ICEDS(ice, DATA));
-}
-
-static inline u32 snd_ice1712_ds_read(ice1712_t * ice, u8 channel, u8 addr)
-{
-	outb((channel << 4) | addr, ICEDS(ice, INDEX));
-	return inl(ICEDS(ice, DATA));
-}
-
-static void snd_ice1712_ac97_write(ac97_t *ac97,
-				   unsigned short reg,
-				   unsigned short val)
-{
-	ice1712_t *ice = (ice1712_t *)ac97->private_data;
-	int tm;
-	unsigned char old_cmd = 0;
-
-	for (tm = 0; tm < 0x10000; tm++) {
-		old_cmd = inb(ICEREG(ice, AC97_CMD));
-		if (old_cmd & (ICE1712_AC97_WRITE | ICE1712_AC97_READ))
-			continue;
-		if (!(old_cmd & ICE1712_AC97_READY))
-			continue;
-		break;
-	}
-	outb(reg, ICEREG(ice, AC97_INDEX));
-	outw(val, ICEREG(ice, AC97_DATA));
-	old_cmd &= ~(ICE1712_AC97_PBK_VSR | ICE1712_AC97_CAP_VSR);
-	outb(old_cmd | ICE1712_AC97_WRITE, ICEREG(ice, AC97_CMD));
-	for (tm = 0; tm < 0x10000; tm++)
-		if ((inb(ICEREG(ice, AC97_CMD)) & ICE1712_AC97_WRITE) == 0)
-			break;
-}
-
-static unsigned short snd_ice1712_ac97_read(ac97_t *ac97,
-					    unsigned short reg)
-{
-	ice1712_t *ice = (ice1712_t *)ac97->private_data;
-	int tm;
-	unsigned char old_cmd = 0;
-
-	for (tm = 0; tm < 0x10000; tm++) {
-		old_cmd = inb(ICEREG(ice, AC97_CMD));
-		if (old_cmd & (ICE1712_AC97_WRITE | ICE1712_AC97_READ))
-			continue;
-		if (!(old_cmd & ICE1712_AC97_READY))
-			continue;
-		break;
-	}
-	outb(reg, ICEREG(ice, AC97_INDEX));
-	outb(old_cmd | ICE1712_AC97_READ, ICEREG(ice, AC97_CMD));
-	for (tm = 0; tm < 0x10000; tm++)
-		if ((inb(ICEREG(ice, AC97_CMD)) & ICE1712_AC97_READ) == 0)
-			break;
-	if (tm >= 0x10000)		/* timeout */
-		return ~0;
-	return inw(ICEREG(ice, AC97_DATA));
-}
-
-/*
- * pro ac97 section
- */
-
-static void snd_ice1712_pro_ac97_write(ac97_t *ac97,
-				       unsigned short reg,
-				       unsigned short val)
-{
-	ice1712_t *ice = (ice1712_t *)ac97->private_data;
-	int tm;
-	unsigned char old_cmd = 0;
-
-	for (tm = 0; tm < 0x10000; tm++) {
-		old_cmd = inb(ICEMT(ice, AC97_CMD));
-		if (old_cmd & (ICE1712_AC97_WRITE | ICE1712_AC97_READ))
-			continue;
-		if (!(old_cmd & ICE1712_AC97_READY))
-			continue;
-		break;
-	}
-	outb(reg, ICEMT(ice, AC97_INDEX));
-	outw(val, ICEMT(ice, AC97_DATA));
-	old_cmd &= ~(ICE1712_AC97_PBK_VSR | ICE1712_AC97_CAP_VSR);
-	outb(old_cmd | ICE1712_AC97_WRITE, ICEMT(ice, AC97_CMD));
-	for (tm = 0; tm < 0x10000; tm++)
-		if ((inb(ICEMT(ice, AC97_CMD)) & ICE1712_AC97_WRITE) == 0)
-			break;
-}
-
-
-static unsigned short snd_ice1712_pro_ac97_read(ac97_t *ac97,
-						unsigned short reg)
-{
-	ice1712_t *ice = (ice1712_t *)ac97->private_data;
-	int tm;
-	unsigned char old_cmd = 0;
-
-	for (tm = 0; tm < 0x10000; tm++) {
-		old_cmd = inb(ICEMT(ice, AC97_CMD));
-		if (old_cmd & (ICE1712_AC97_WRITE | ICE1712_AC97_READ))
-			continue;
-		if (!(old_cmd & ICE1712_AC97_READY))
-			continue;
-		break;
-	}
-	outb(reg, ICEMT(ice, AC97_INDEX));
-	outb(old_cmd | ICE1712_AC97_READ, ICEMT(ice, AC97_CMD));
-	for (tm = 0; tm < 0x10000; tm++)
-		if ((inb(ICEMT(ice, AC97_CMD)) & ICE1712_AC97_READ) == 0)
-			break;
-	if (tm >= 0x10000)		/* timeout */
-		return ~0;
-	return inw(ICEMT(ice, AC97_DATA));
-}
-
-static int snd_ice1712_digmix_route_ac97_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_ice1712_digmix_route_ac97_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
-{
-	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
-	unsigned long flags;
-	
-	spin_lock_irqsave(&ice->reg_lock, flags);
-	ucontrol->value.integer.value[0] = inb(ICEMT(ice, MONITOR_ROUTECTRL)) & ICE1712_ROUTE_AC97 ? 1 : 0;
-	spin_unlock_irqrestore(&ice->reg_lock, flags);
-	return 0;
-}
-
-static int snd_ice1712_digmix_route_ac97_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
-{
-	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
-	unsigned char val, nval;
-	unsigned long flags;
-	
-	spin_lock_irqsave(&ice->reg_lock, flags);
-	val = inb(ICEMT(ice, MONITOR_ROUTECTRL));
-	nval = val & ~ICE1712_ROUTE_AC97;
-	if (ucontrol->value.integer.value[0]) nval |= ICE1712_ROUTE_AC97;
-	outb(nval, ICEMT(ice, MONITOR_ROUTECTRL));
-	spin_unlock_irqrestore(&ice->reg_lock, flags);
-	return val != nval;
-}
-
-static snd_kcontrol_new_t snd_ice1712_mixer_digmix_route_ac97 __devinitdata = {
-	iface: SNDRV_CTL_ELEM_IFACE_MIXER,
-	name: "Digital Mixer To AC97",
-	info: snd_ice1712_digmix_route_ac97_info,
-	get: snd_ice1712_digmix_route_ac97_get,
-	put: snd_ice1712_digmix_route_ac97_put,
-};
-
-
-/*
- */
-
-static void snd_ice1712_delta_cs8403_spdif_write(ice1712_t *ice, unsigned char bits)
-{
-	unsigned char tmp, mask1, mask2;
-	int idx;
-	/* send byte to transmitter */
-	mask1 = ICE1712_DELTA_SPDIF_OUT_STAT_CLOCK;
-	mask2 = ICE1712_DELTA_SPDIF_OUT_STAT_DATA;
-	down(&ice->gpio_mutex);
-	tmp = snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA);
-	for (idx = 7; idx >= 0; idx--) {
-		tmp &= ~(mask1 | mask2);
-		if (bits & (1 << idx))
-			tmp |= mask2;
-		snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp);
-		udelay(100);
-		tmp |= mask1;
-		snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp);
-		udelay(100);
-	}
-	tmp &= ~mask1;
-	snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp);
-	up(&ice->gpio_mutex);
-}
-
-
-/*
- * set gpio direction, write mask and data
- */
-static void snd_ice1712_gpio_write_bits(ice1712_t *ice, int mask, int bits)
-{
-	ice->gpio_direction |= mask;
-	snd_ice1712_write(ice, ICE1712_IREG_GPIO_DIRECTION, ice->gpio_direction);
-	snd_ice1712_write(ice, ICE1712_IREG_GPIO_WRITE_MASK, ~mask);
-	snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, mask & bits);
-}
-
-/*
- */
-static void save_gpio_status(ice1712_t *ice, unsigned char *tmp)
-{
-	down(&ice->gpio_mutex);
-	tmp[0] = ice->gpio_direction;
-	tmp[1] = ice->gpio_write_mask;
-}
-
-static void restore_gpio_status(ice1712_t *ice, unsigned char *tmp)
-{
-	snd_ice1712_write(ice, ICE1712_IREG_GPIO_DIRECTION, tmp[0]);
-	snd_ice1712_write(ice, ICE1712_IREG_GPIO_WRITE_MASK, tmp[1]);
-	ice->gpio_direction = tmp[0];
-	ice->gpio_write_mask = tmp[1];
-	up(&ice->gpio_mutex);
-}
-
-/*
- * CS8427 via SPI mode (for Audiophile), emulated I2C
- */
-
-/* send 8 bits */
-static void ap_cs8427_write_byte(ice1712_t *ice, unsigned char data, unsigned char tmp)
-{
-	int idx;
-
-	for (idx = 7; idx >= 0; idx--) {
-		tmp &= ~(ICE1712_DELTA_AP_DOUT|ICE1712_DELTA_AP_CCLK);
-		if (data & (1 << idx))
-			tmp |= ICE1712_DELTA_AP_DOUT;
-		snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp);
-		udelay(5);
-		tmp |= ICE1712_DELTA_AP_CCLK;
-		snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp);
-		udelay(5);
-	}
-}
-
-/* read 8 bits */
-static unsigned char ap_cs8427_read_byte(ice1712_t *ice, unsigned char tmp)
-{
-	unsigned char data = 0;
-	int idx;
-	
-	for (idx = 7; idx >= 0; idx--) {
-		tmp &= ~ICE1712_DELTA_AP_CCLK;
-		snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp);
-		udelay(5);
-		if (snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA) & ICE1712_DELTA_AP_DIN)
-			data |= 1 << idx;
-		tmp |= ICE1712_DELTA_AP_CCLK;
-		snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp);
-		udelay(5);
-	}
-	return data;
-}
-
-/* assert chip select */
-static unsigned char ap_cs8427_codec_select(ice1712_t *ice)
-{
-	unsigned char tmp;
-	tmp = snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA);
-	tmp |= ICE1712_DELTA_AP_CCLK | ICE1712_DELTA_AP_CS_CODEC;
-	tmp &= ~ICE1712_DELTA_AP_CS_DIGITAL;
-	snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp);
-	udelay(5);
-	return tmp;
-}
-
-/* deassert chip select */
-static void ap_cs8427_codec_deassert(ice1712_t *ice, unsigned char tmp)
-{
-	tmp |= ICE1712_DELTA_AP_CS_DIGITAL;
-	snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp);
-}
-
-/* sequential write */
-static int ap_cs8427_sendbytes(snd_i2c_device_t *device, unsigned char *bytes, int count)
-{
-	ice1712_t *ice = snd_magic_cast(ice1712_t, device->bus->private_data, return -EIO);
-	int res = count;
-	unsigned char tmp;
-
-	down(&ice->gpio_mutex);
-	tmp = ap_cs8427_codec_select(ice);
-	ap_cs8427_write_byte(ice, (device->addr << 1) | 0, tmp); /* address + write mode */
-	while (count-- > 0)
-		ap_cs8427_write_byte(ice, *bytes++, tmp);
-	ap_cs8427_codec_deassert(ice, tmp);
-	up(&ice->gpio_mutex);
-	return res;
-}
-
-/* sequential read */
-static int ap_cs8427_readbytes(snd_i2c_device_t *device, unsigned char *bytes, int count)
-{
-	ice1712_t *ice = snd_magic_cast(ice1712_t, device->bus->private_data, return -EIO);
-	int res = count;
-	unsigned char tmp;
-	
-	down(&ice->gpio_mutex);
-	tmp = ap_cs8427_codec_select(ice);
-	ap_cs8427_write_byte(ice, (device->addr << 1) | 1, tmp); /* address + read mode */
-	while (count-- > 0)
-		*bytes++ = ap_cs8427_read_byte(ice, tmp);
-	ap_cs8427_codec_deassert(ice, tmp);
-	up(&ice->gpio_mutex);
-	return res;
-}
-
-static int ap_cs8427_probeaddr(snd_i2c_bus_t *bus, unsigned short addr)
-{
-	if (addr == 0x10)
-		return 1;
-	return -ENOENT;
-}
-
-static snd_i2c_ops_t ap_cs8427_i2c_ops = {
-	sendbytes: ap_cs8427_sendbytes,
-	readbytes: ap_cs8427_readbytes,
-	probeaddr: ap_cs8427_probeaddr,
-};
-
-/*
- * access via i2c mode (for EWX 24/96, EWS 88MT&D)
- */
-
-/* send SDA and SCL */
-static void ewx_i2c_setlines(snd_i2c_bus_t *bus, int clk, int data)
-{
-	ice1712_t *ice = snd_magic_cast(ice1712_t, bus->private_data, return);
-	unsigned char tmp = 0;
-	if (clk)
-		tmp |= ICE1712_EWX2496_SERIAL_CLOCK;
-	if (data)
-		tmp |= ICE1712_EWX2496_SERIAL_DATA;
-	snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp);
-	udelay(5);
-}
-
-static int ewx_i2c_getclock(snd_i2c_bus_t *bus)
-{
-	ice1712_t *ice = snd_magic_cast(ice1712_t, bus->private_data, return -EIO);
-	return snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA) & ICE1712_EWX2496_SERIAL_CLOCK ? 1 : 0;
-}
-
-static int ewx_i2c_getdata(snd_i2c_bus_t *bus, int ack)
-{
-	ice1712_t *ice = snd_magic_cast(ice1712_t, bus->private_data, return -EIO);
-	int bit;
-	/* set RW pin to low */
-	snd_ice1712_write(ice, ICE1712_IREG_GPIO_WRITE_MASK, ~ICE1712_EWX2496_RW);
-	snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, 0);
-	if (ack)
-		udelay(5);
-	bit = snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA) & ICE1712_EWX2496_SERIAL_DATA ? 1 : 0;
-	/* set RW pin to high */
-	snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, ICE1712_EWX2496_RW);
-	/* reset write mask */
-	snd_ice1712_write(ice, ICE1712_IREG_GPIO_WRITE_MASK, ~ICE1712_EWX2496_SERIAL_CLOCK);
-	return bit;
-}
-
-static void ewx_i2c_start(snd_i2c_bus_t *bus)
-{
-	ice1712_t *ice = snd_magic_cast(ice1712_t, bus->private_data, return);
-	unsigned char mask;
-
-	save_gpio_status(ice, (unsigned char *)&bus->private_value);
-	/* set RW high */
-	mask = ICE1712_EWX2496_RW;
-	switch (ice->eeprom.subvendor) {
-	case ICE1712_SUBDEVICE_EWX2496:
-		mask |= ICE1712_EWX2496_AK4524_CS; /* CS high also */
-		break;
-	case ICE1712_SUBDEVICE_DMX6FIRE:
-		mask |= ICE1712_6FIRE_AK4524_CS_MASK; /* CS high also */
-		break;
-	}
-	snd_ice1712_gpio_write_bits(ice, mask, mask);
-}
-
-static void ewx_i2c_stop(snd_i2c_bus_t *bus)
-{
-	ice1712_t *ice = snd_magic_cast(ice1712_t, bus->private_data, return);
-	restore_gpio_status(ice, (unsigned char *)&bus->private_value);
-}
-
-static void ewx_i2c_direction(snd_i2c_bus_t *bus, int clock, int data)
-{
-	ice1712_t *ice = snd_magic_cast(ice1712_t, bus->private_data, return);
-	unsigned char mask = 0;
-
-	if (clock)
-		mask |= ICE1712_EWX2496_SERIAL_CLOCK; /* write SCL */
-	if (data)
-		mask |= ICE1712_EWX2496_SERIAL_DATA; /* write SDA */
-	ice->gpio_direction &= ~(ICE1712_EWX2496_SERIAL_CLOCK|ICE1712_EWX2496_SERIAL_DATA);
-	ice->gpio_direction |= mask;
-	snd_ice1712_write(ice, ICE1712_IREG_GPIO_DIRECTION, ice->gpio_direction);
-	snd_ice1712_write(ice, ICE1712_IREG_GPIO_WRITE_MASK, ~mask);
-}
-
-static snd_i2c_bit_ops_t snd_ice1712_ewx_cs8427_bit_ops = {
-	start: ewx_i2c_start,
-	stop: ewx_i2c_stop,
-	direction: ewx_i2c_direction,
-	setlines: ewx_i2c_setlines,
-	getclock: ewx_i2c_getclock,
-	getdata: ewx_i2c_getdata,
-};
-
-/* AK4524 chip select; address 0x48 bit 0-3 */
-static void snd_ice1712_ews88mt_chip_select(ice1712_t *ice, int chip_mask)
-{
-	unsigned char data, ndata;
-
-	snd_assert(chip_mask >= 0 && chip_mask <= 0x0f, return);
-	snd_i2c_lock(ice->i2c);
-	snd_runtime_check(snd_i2c_readbytes(ice->pcf8574[1], &data, 1) == 1, snd_i2c_unlock(ice->i2c); return);
-	ndata = (data & 0xf0) | chip_mask;
-	if (ndata != data)
-		snd_runtime_check(snd_i2c_sendbytes(ice->pcf8574[1], &ndata, 1) == 1, snd_i2c_unlock(ice->i2c); return);
-	snd_i2c_unlock(ice->i2c);
-}
-
-/*
- * write AK4524 register
- */
-static void snd_ice1712_ak4524_write(ice1712_t *ice, int chip,
-				     unsigned char addr, unsigned char data)
-{
-	unsigned char tmp, data_mask, clk_mask, saved[2];
-	unsigned char codecs_mask;
-	int idx, cif;
-	unsigned int addrdata;
-
-	snd_assert(chip >= 0 && chip < 4, return);
-
-	if (ice->eeprom.subvendor == ICE1712_SUBDEVICE_EWS88MT) {
-		/* assert AK4524 CS */
-		snd_ice1712_ews88mt_chip_select(ice, ~(1 << chip) & 0x0f);
-		//snd_ice1712_ews88mt_chip_select(ice, 0x0f);
-	}
-
-	cif = 0; /* the default level of the CIF pin from AK4524 */
-	save_gpio_status(ice, saved);
-	tmp = snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA);
-	switch (ice->eeprom.subvendor) {
-	case ICE1712_SUBDEVICE_AUDIOPHILE:
-		data_mask = ICE1712_DELTA_AP_DOUT;
-		clk_mask = ICE1712_DELTA_AP_CCLK;
-		codecs_mask = ICE1712_DELTA_AP_CS_CODEC; /* select AK4528 codec */
-		tmp |= ICE1712_DELTA_AP_CS_DIGITAL; /* assert digital high */
-		break;
-	case ICE1712_SUBDEVICE_EWX2496:
-		data_mask = ICE1712_EWX2496_SERIAL_DATA;
-		clk_mask = ICE1712_EWX2496_SERIAL_CLOCK;
-		codecs_mask = ICE1712_EWX2496_AK4524_CS;
-		tmp |= ICE1712_EWX2496_RW; /* set rw bit high */
-		snd_ice1712_write(ice, ICE1712_IREG_GPIO_DIRECTION,
-				  ice->gpio_direction | data_mask | clk_mask |
-				  codecs_mask | ICE1712_EWX2496_RW);
-		snd_ice1712_write(ice, ICE1712_IREG_GPIO_WRITE_MASK,
-				  ~(data_mask | clk_mask |
-				    codecs_mask | ICE1712_EWX2496_RW));
-		cif = 1; /* CIF high */
-		break;
-	case ICE1712_SUBDEVICE_EWS88MT:
-		data_mask = ICE1712_EWS88_SERIAL_DATA;
-		clk_mask = ICE1712_EWS88_SERIAL_CLOCK;
-		codecs_mask = 0; /* no chip select on gpio */
-		tmp |= ICE1712_EWS88_RW; /* set rw bit high */
-		snd_ice1712_write(ice, ICE1712_IREG_GPIO_DIRECTION,
-				  ice->gpio_direction | data_mask | clk_mask |
-				  codecs_mask | ICE1712_EWS88_RW);
-		snd_ice1712_write(ice, ICE1712_IREG_GPIO_WRITE_MASK,
-				  ~(data_mask | clk_mask |
-				    codecs_mask | ICE1712_EWS88_RW));
-		cif = 1; /* CIF high */
-		break;
-	case ICE1712_SUBDEVICE_DMX6FIRE:
-		data_mask = ICE1712_6FIRE_SERIAL_DATA;
-		clk_mask = ICE1712_6FIRE_SERIAL_CLOCK;
-		codecs_mask = (1 << chip) & ICE1712_6FIRE_AK4524_CS_MASK;
-		tmp |= ICE1712_6FIRE_RW; /* set rw bit high */
-		snd_ice1712_write(ice, ICE1712_IREG_GPIO_DIRECTION,
-				  ice->gpio_direction | data_mask | clk_mask |
-				  codecs_mask | ICE1712_6FIRE_RW);
-		snd_ice1712_write(ice, ICE1712_IREG_GPIO_WRITE_MASK,
-				  ~(data_mask | clk_mask |
-				    codecs_mask | ICE1712_6FIRE_RW));
-		cif = 1; /* CIF high */
-		break;
-	default:
-		data_mask = ICE1712_DELTA_CODEC_SERIAL_DATA;
-		clk_mask = ICE1712_DELTA_CODEC_SERIAL_CLOCK;
-		codecs_mask = chip == 0 ? ICE1712_DELTA_CODEC_CHIP_A : ICE1712_DELTA_CODEC_CHIP_B;
-		break;
-	}
-
-	if (cif) {
-		tmp |= codecs_mask; /* start without chip select */
-	} else {
-		tmp &= ~codecs_mask; /* chip select low */
-		snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp);
-		udelay(1);
-	}
-
-	addr &= 0x07;
-	/* build I2C address + data byte */
-	addrdata = 0xa000 | (addr << 8) | data;
-	for (idx = 15; idx >= 0; idx--) {
-		tmp &= ~(data_mask|clk_mask);
-		if (addrdata & (1 << idx))
-			tmp |= data_mask;
-		snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp);
-		//udelay(200);
-		udelay(1);
-		tmp |= clk_mask;
-		snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp);
-		udelay(1);
-	}
-
-	if (ice->eeprom.subvendor == ICE1712_SUBDEVICE_EWS88MT) {
-		restore_gpio_status(ice, saved);
-		//snd_ice1712_ews88mt_chip_select(ice, ~(1 << chip) & 0x0f);
-		udelay(1);
-		snd_ice1712_ews88mt_chip_select(ice, 0x0f);
-	} else {
-		if (cif) {
-			/* assert a cs pulse to trigger */
-			tmp &= ~codecs_mask;
-			snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp);
-			udelay(1);
-		}
-		tmp |= codecs_mask; /* chip select high to trigger */
-		snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp);
-		udelay(1);
-		restore_gpio_status(ice, saved);
-	}
-
-	if ((addr != 0x04 && addr != 0x05) || (data & 0x80) == 0)
-		ice->ak4524_images[chip][addr] = data;
-	else
-		ice->ak4524_ipga_gain[chip][addr-4] = data;
-}
-
-static void snd_ice1712_ak4524_reset(ice1712_t *ice, int state)
-{
-	int chip;
-	unsigned char reg;
-	
-	for (chip = 0; chip < ice->num_dacs/2; chip++) {
-		snd_ice1712_ak4524_write(ice, chip, 0x01, state ? 0x00 : 0x03);
-		if (state)
-			continue;
-		for (reg = 0x04; reg < (ice->ak4528 ? 0x06 : 0x08); reg++)
-			snd_ice1712_ak4524_write(ice, chip, reg, ice->ak4524_images[chip][reg]);
-		if (ice->ak4528)
-			continue;
-		for (reg = 0x04; reg < 0x06; reg++)
-			snd_ice1712_ak4524_write(ice, chip, reg, ice->ak4524_ipga_gain[chip][reg-4]);
-	}
-}
-
-static void snd_ice1712_ews_cs8404_spdif_write(ice1712_t *ice, unsigned char bits)
-{
-	unsigned char bytes[2];
-
-	snd_i2c_lock(ice->i2c);
-	switch (ice->eeprom.subvendor) {
-	case ICE1712_SUBDEVICE_EWS88MT:
-		snd_runtime_check(snd_i2c_sendbytes(ice->cs8404, &bits, 1) == 1, snd_i2c_unlock(ice->i2c); return);
-		break;
-	case ICE1712_SUBDEVICE_EWS88D:
-		snd_runtime_check(snd_i2c_readbytes(ice->pcf8575, bytes, 2) == 2, snd_i2c_unlock(ice->i2c); return);
-		if (bits != bytes[1]) {
-			bytes[1] = bits;
-			snd_runtime_check(snd_i2c_readbytes(ice->pcf8575, bytes, 2) == 2, snd_i2c_unlock(ice->i2c); return);
-		}
-		break;
-	}
-	snd_i2c_unlock(ice->i2c);
-}
-
-
-/*
- * change the input clock selection
- * spdif_clock = 1 - IEC958 input, 0 - Envy24
- */
-static int snd_ice1712_cs8427_set_input_clock(ice1712_t *ice, int spdif_clock)
-{
-	unsigned char reg[2] = { 0x80 | 4, 0 };   /* CS8427 auto increment | register number 4 + data */
-	unsigned char val, nval;
-	int res = 0;
-	
-	snd_i2c_lock(ice->i2c);
-	if (snd_i2c_sendbytes(ice->cs8427, reg, 1) != 1) {
-		snd_i2c_unlock(ice->i2c);
-		return -EREMOTE;
-	}
-	if (snd_i2c_readbytes(ice->cs8427, &val, 1) != 1) {
-		snd_i2c_unlock(ice->i2c);
-		return -EREMOTE;
-	}
-	nval = val & 0xf0;
-	if (spdif_clock)
-		nval |= 0x01;
-	else
-		nval |= 0x04;
-	if (val != nval) {
-		reg[1] = nval;
-		if (snd_i2c_sendbytes(ice->cs8427, reg, 2) != 2) {
-			res = -EREMOTE;
-		} else {
-			res++;
-		}
-	}
-	snd_i2c_unlock(ice->i2c);
-	return res;
-}
-
-/*
- */
-static void snd_ice1712_set_pro_rate(ice1712_t *ice, snd_pcm_substream_t *substream)
-{
-	unsigned long flags;
-	unsigned int rate;
-	unsigned char val, tmp;
-
-	spin_lock_irqsave(&ice->reg_lock, flags);
-	if (inb(ICEMT(ice, PLAYBACK_CONTROL)) & (ICE1712_CAPTURE_START_SHADOW|
-						 ICE1712_PLAYBACK_PAUSE|
-						 ICE1712_PLAYBACK_START))
-		goto __end;
-	if (inb(ICEMT(ice, RATE)) & ICE1712_SPDIF_MASTER)
-		goto __end;
-	rate = substream->runtime->rate;
-	switch (rate) {
-	case 8000: val = 6; break;
-	case 9600: val = 3; break;
-	case 11025: val = 10; break;
-	case 12000: val = 2; break;
-	case 16000: val = 5; break;
-	case 22050: val = 9; break;
-	case 24000: val = 1; break;
-	case 32000: val = 4; break;
-	case 44100: val = 8; break;
-	case 48000: val = 0; break;
-	case 64000: val = 15; break;
-	case 88200: val = 11; break;
-	case 96000: val = 7; break;
-	default:
-		snd_BUG();
-		val = 0;
-		break;
-	}
-	outb(val, ICEMT(ice, RATE));
-	switch (ice->eeprom.subvendor) {
-	case ICE1712_SUBDEVICE_DELTA1010:
-	case ICE1712_SUBDEVICE_DELTA1010LT:	// check it --jk
-	case ICE1712_SUBDEVICE_DELTADIO2496:
-	case ICE1712_SUBDEVICE_DELTA66:
-	case ICE1712_SUBDEVICE_DELTA44:
-	case ICE1712_SUBDEVICE_AUDIOPHILE:
-		spin_unlock_irqrestore(&ice->reg_lock, flags);
-		snd_ice1712_ak4524_reset(ice, 1);
-		down(&ice->gpio_mutex);
-		tmp = snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA);
-		if (val == 15 || val == 11 || val == 7) {
-			tmp |= ICE1712_DELTA_DFS;
-		} else {
-			tmp &= ~ICE1712_DELTA_DFS;
-		}
-		snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, tmp);
-		up(&ice->gpio_mutex);
-		snd_ice1712_ak4524_reset(ice, 0);
-		return;
-	}
-      __end:
-	spin_unlock_irqrestore(&ice->reg_lock, flags);
-}
-
-/*
- *  Interrupt handler
- */
-
-static void snd_ice1712_interrupt(int irq, void *dev_id, struct pt_regs *regs)
-{
-	ice1712_t *ice = snd_magic_cast(ice1712_t, dev_id, return);
-	unsigned char status;
-
-	while (1) {
-		status = inb(ICEREG(ice, IRQSTAT));
-		if (status == 0)
-			break;
-		if (status & ICE1712_IRQ_MPU1) {
-			if (ice->rmidi[0])
-				snd_mpu401_uart_interrupt(irq, ice->rmidi[0]->private_data, regs);
-			outb(ICE1712_IRQ_MPU1, ICEREG(ice, IRQSTAT));
-			status &= ~ICE1712_IRQ_MPU1;
-		}
-		if (status & ICE1712_IRQ_TIMER)
-			outb(ICE1712_IRQ_TIMER, ICEREG(ice, IRQSTAT));
-		if (status & ICE1712_IRQ_MPU2) {
-			if (ice->rmidi[1])
-				snd_mpu401_uart_interrupt(irq, ice->rmidi[1]->private_data, regs);
-			outb(ICE1712_IRQ_MPU2, ICEREG(ice, IRQSTAT));
-			status &= ~ICE1712_IRQ_MPU2;
-		}
-		if (status & ICE1712_IRQ_PROPCM) {
-			unsigned char mtstat = inb(ICEMT(ice, IRQ));
-			if (mtstat & ICE1712_MULTI_PBKSTATUS) {
-				if (ice->playback_pro_substream)
-					snd_pcm_period_elapsed(ice->playback_pro_substream);
-				outb(ICE1712_MULTI_PBKSTATUS, ICEMT(ice, IRQ));
-			}
-			if (mtstat & ICE1712_MULTI_CAPSTATUS) {
-				if (ice->capture_pro_substream)
-					snd_pcm_period_elapsed(ice->capture_pro_substream);
-				outb(ICE1712_MULTI_CAPSTATUS, ICEMT(ice, IRQ));
-			}
-		}
-		if (status & ICE1712_IRQ_FM)
-			outb(ICE1712_IRQ_FM, ICEREG(ice, IRQSTAT));
-		if (status & ICE1712_IRQ_PBKDS) {
-			u32 idx;
-			u16 pbkstatus;
-			snd_pcm_substream_t *substream;
-			pbkstatus = inw(ICEDS(ice, INTSTAT));
-			//printk("pbkstatus = 0x%x\n", pbkstatus);
-			for (idx = 0; idx < 6; idx++) {
-				if ((pbkstatus & (3 << (idx * 2))) == 0)
-					continue;
-				if ((substream = ice->playback_con_substream_ds[idx]) != NULL)
-					snd_pcm_period_elapsed(substream);
-				outw(3 << (idx * 2), ICEDS(ice, INTSTAT));
-			}
-			outb(ICE1712_IRQ_PBKDS, ICEREG(ice, IRQSTAT));
-		}
-		if (status & ICE1712_IRQ_CONCAP) {
-			if (ice->capture_con_substream)
-				snd_pcm_period_elapsed(ice->capture_con_substream);
-			outb(ICE1712_IRQ_CONCAP, ICEREG(ice, IRQSTAT));
-		}
-		if (status & ICE1712_IRQ_CONPBK) {
-			if (ice->playback_con_substream)
-				snd_pcm_period_elapsed(ice->playback_con_substream);
-			outb(ICE1712_IRQ_CONPBK, ICEREG(ice, IRQSTAT));
-		}
-	}
-}
-
-/*
- *  PCM part - misc
- */
-
-static int snd_ice1712_hw_params(snd_pcm_substream_t * substream,
-				 snd_pcm_hw_params_t * hw_params)
-{
-	return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
-}
-
-static int snd_ice1712_hw_free(snd_pcm_substream_t * substream)
-{
-	return snd_pcm_lib_free_pages(substream);
-}
-
-/*
- *  PCM part - consumer I/O
- */
-
-static int snd_ice1712_playback_trigger(snd_pcm_substream_t * substream,
-					int cmd)
-{
-	ice1712_t *ice = snd_pcm_substream_chip(substream);
-	int result = 0;
-	u32 tmp;
-	
-	spin_lock(&ice->reg_lock);
-	tmp = snd_ice1712_read(ice, ICE1712_IREG_PBK_CTRL);
-	if (cmd == SNDRV_PCM_TRIGGER_START) {
-		tmp |= 1;
-	} else if (cmd == SNDRV_PCM_TRIGGER_STOP) {
-		tmp &= ~1;
-	} else if (cmd == SNDRV_PCM_TRIGGER_PAUSE_PUSH) {
-		tmp |= 2;
-	} else if (cmd == SNDRV_PCM_TRIGGER_PAUSE_RELEASE) {
-		tmp &= ~2;
-	} else {
-		result = -EINVAL;
-	}
-	snd_ice1712_write(ice, ICE1712_IREG_PBK_CTRL, tmp);
-	spin_unlock(&ice->reg_lock);
-	return result;
-}
-
-static int snd_ice1712_playback_ds_trigger(snd_pcm_substream_t * substream,
-					   int cmd)
-{
-	ice1712_t *ice = snd_pcm_substream_chip(substream);
-	int result = 0;
-	u32 tmp;
-	
-	spin_lock(&ice->reg_lock);
-	tmp = snd_ice1712_ds_read(ice, substream->number * 2, ICE1712_DSC_CONTROL);
-	if (cmd == SNDRV_PCM_TRIGGER_START) {
-		tmp |= 1;
-	} else if (cmd == SNDRV_PCM_TRIGGER_STOP) {
-		tmp &= ~1;
-	} else if (cmd == SNDRV_PCM_TRIGGER_PAUSE_PUSH) {
-		tmp |= 2;
-	} else if (cmd == SNDRV_PCM_TRIGGER_PAUSE_RELEASE) {
-		tmp &= ~2;
-	} else {
-		result = -EINVAL;
-	}
-	snd_ice1712_ds_write(ice, substream->number * 2, ICE1712_DSC_CONTROL, tmp);
-	spin_unlock(&ice->reg_lock);
-	return result;
-}
-
-static int snd_ice1712_capture_trigger(snd_pcm_substream_t * substream,
-				       int cmd)
-{
-	ice1712_t *ice = snd_pcm_substream_chip(substream);
-	int result = 0;
-	u8 tmp;
-	
-	spin_lock(&ice->reg_lock);
-	tmp = snd_ice1712_read(ice, ICE1712_IREG_CAP_CTRL);
-	if (cmd == SNDRV_PCM_TRIGGER_START) {
-		tmp |= 1;
-	} else if (cmd == SNDRV_PCM_TRIGGER_STOP) {
-		tmp &= ~1;
-	} else {
-		result = -EINVAL;
-	}
-	snd_ice1712_write(ice, ICE1712_IREG_CAP_CTRL, tmp);
-	spin_unlock(&ice->reg_lock);
-	return result;
-}
-
-static int snd_ice1712_playback_prepare(snd_pcm_substream_t * substream)
-{
-	unsigned long flags;
-	ice1712_t *ice = snd_pcm_substream_chip(substream);
-	snd_pcm_runtime_t *runtime = substream->runtime;
-	u32 period_size, buf_size, rate, tmp;
-
-	period_size = (snd_pcm_lib_period_bytes(substream) >> 2) - 1;
-	buf_size = snd_pcm_lib_buffer_bytes(substream) - 1;
-	tmp = 0x0000;
-	if (snd_pcm_format_width(runtime->format) == 16)
-		tmp |= 0x10;
-	if (runtime->channels == 2)
-		tmp |= 0x08;
-	rate = (runtime->rate * 8192) / 375;
-	if (rate > 0x000fffff)
-		rate = 0x000fffff;
-	spin_lock_irqsave(&ice->reg_lock, flags);
-	outb(0, ice->ddma_port + 15);
-	outb(ICE1712_DMA_MODE_WRITE | ICE1712_DMA_AUTOINIT, ice->ddma_port + 0x0b);
-	outl(runtime->dma_addr, ice->ddma_port + 0);
-	outw(buf_size, ice->ddma_port + 4);
-	snd_ice1712_write(ice, ICE1712_IREG_PBK_RATE_LO, rate & 0xff);
-	snd_ice1712_write(ice, ICE1712_IREG_PBK_RATE_MID, (rate >> 8) & 0xff);
-	snd_ice1712_write(ice, ICE1712_IREG_PBK_RATE_HI, (rate >> 16) & 0xff);
-	snd_ice1712_write(ice, ICE1712_IREG_PBK_CTRL, tmp);
-	snd_ice1712_write(ice, ICE1712_IREG_PBK_COUNT_LO, period_size & 0xff);
-	snd_ice1712_write(ice, ICE1712_IREG_PBK_COUNT_HI, period_size >> 8);
-	snd_ice1712_write(ice, ICE1712_IREG_PBK_LEFT, 0);
-	snd_ice1712_write(ice, ICE1712_IREG_PBK_RIGHT, 0);
-	spin_unlock_irqrestore(&ice->reg_lock, flags);
-	return 0;
-}
-
-static int snd_ice1712_playback_ds_prepare(snd_pcm_substream_t * substream)
-{
-	unsigned long flags;
-	ice1712_t *ice = snd_pcm_substream_chip(substream);
-	snd_pcm_runtime_t *runtime = substream->runtime;
-	u32 period_size, buf_size, rate, tmp, chn;
-
-	period_size = snd_pcm_lib_period_bytes(substream) - 1;
-	buf_size = snd_pcm_lib_buffer_bytes(substream) - 1;
-	tmp = 0x0064;
-	if (snd_pcm_format_width(runtime->format) == 16)
-		tmp &= ~0x04;
-	if (runtime->channels == 2)
-		tmp |= 0x08;
-	rate = (runtime->rate * 8192) / 375;
-	if (rate > 0x000fffff)
-		rate = 0x000fffff;
-	ice->playback_con_active_buf[substream->number] = 0;
-	ice->playback_con_virt_addr[substream->number] = runtime->dma_addr;
-	chn = substream->number * 2;
-	spin_lock_irqsave(&ice->reg_lock, flags);
-	snd_ice1712_ds_write(ice, chn, ICE1712_DSC_ADDR0, runtime->dma_addr);
-	snd_ice1712_ds_write(ice, chn, ICE1712_DSC_COUNT0, period_size);
-	snd_ice1712_ds_write(ice, chn, ICE1712_DSC_ADDR1, runtime->dma_addr + (runtime->periods > 1 ? period_size + 1 : 0));
-	snd_ice1712_ds_write(ice, chn, ICE1712_DSC_COUNT1, period_size);
-	snd_ice1712_ds_write(ice, chn, ICE1712_DSC_RATE, rate);
-	snd_ice1712_ds_write(ice, chn, ICE1712_DSC_VOLUME, 0);
-	snd_ice1712_ds_write(ice, chn, ICE1712_DSC_CONTROL, tmp);
-	if (runtime->channels == 2) {
-		snd_ice1712_ds_write(ice, chn + 1, ICE1712_DSC_RATE, rate);
-		snd_ice1712_ds_write(ice, chn + 1, ICE1712_DSC_VOLUME, 0);
-	}
-	spin_unlock_irqrestore(&ice->reg_lock, flags);
-	return 0;
-}
-
-static int snd_ice1712_capture_prepare(snd_pcm_substream_t * substream)
-{
-	unsigned long flags;
-	ice1712_t *ice = snd_pcm_substream_chip(substream);
-	snd_pcm_runtime_t *runtime = substream->runtime;
-	u32 period_size, buf_size;
-	u8 tmp;
-
-	period_size = (snd_pcm_lib_period_bytes(substream) >> 2) - 1;
-	buf_size = snd_pcm_lib_buffer_bytes(substream) - 1;
-	tmp = 0x06;
-	if (snd_pcm_format_width(runtime->format) == 16)
-		tmp &= ~0x04;
-	if (runtime->channels == 2)
-		tmp &= ~0x02;
-	spin_lock_irqsave(&ice->reg_lock, flags);
-	outl(ice->capture_con_virt_addr = runtime->dma_addr, ICEREG(ice, CONCAP_ADDR));
-	outw(buf_size, ICEREG(ice, CONCAP_COUNT));
-	snd_ice1712_write(ice, ICE1712_IREG_CAP_COUNT_HI, period_size >> 8);
-	snd_ice1712_write(ice, ICE1712_IREG_CAP_COUNT_LO, period_size & 0xff);
-	snd_ice1712_write(ice, ICE1712_IREG_CAP_CTRL, tmp);
-	spin_unlock_irqrestore(&ice->reg_lock, flags);
-	snd_ac97_set_rate(ice->ac97, AC97_PCM_LR_ADC_RATE, runtime->rate);
-	return 0;
-}
-
-static snd_pcm_uframes_t snd_ice1712_playback_pointer(snd_pcm_substream_t * substream)
-{
-	ice1712_t *ice = snd_pcm_substream_chip(substream);
-	snd_pcm_runtime_t *runtime = substream->runtime;
-	size_t ptr;
-
-	if (!(snd_ice1712_read(ice, ICE1712_IREG_PBK_CTRL) & 1))
-		return 0;
-	ptr = runtime->buffer_size - inw(ice->ddma_port + 4);
-	return bytes_to_frames(substream->runtime, ptr);
-}
-
-static snd_pcm_uframes_t snd_ice1712_playback_ds_pointer(snd_pcm_substream_t * substream)
-{
-	ice1712_t *ice = snd_pcm_substream_chip(substream);
-	u8 addr;
-	size_t ptr;
-
-	if (!(snd_ice1712_ds_read(ice, substream->number * 2, ICE1712_DSC_CONTROL) & 1))
-		return 0;
-	if (ice->playback_con_active_buf[substream->number])
-		addr = ICE1712_DSC_ADDR1;
-	else
-		addr = ICE1712_DSC_ADDR0;
-	ptr = snd_ice1712_ds_read(ice, substream->number * 2, addr) -
-		ice->playback_con_virt_addr[substream->number];
-	return bytes_to_frames(substream->runtime, ptr);
-}
-
-static snd_pcm_uframes_t snd_ice1712_capture_pointer(snd_pcm_substream_t * substream)
-{
-	ice1712_t *ice = snd_pcm_substream_chip(substream);
-	size_t ptr;
-
-	if (!(snd_ice1712_read(ice, ICE1712_IREG_CAP_CTRL) & 1))
-		return 0;
-	ptr = inl(ICEREG(ice, CONCAP_ADDR)) - ice->capture_con_virt_addr;
-	return bytes_to_frames(substream->runtime, ptr);
-}
-
-static snd_pcm_hardware_t snd_ice1712_playback =
-{
-	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
-				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
-				 SNDRV_PCM_INFO_MMAP_VALID |
-				 SNDRV_PCM_INFO_PAUSE),
-	formats:		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
-	rates:			SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
-	rate_min:		4000,
-	rate_max:		48000,
-	channels_min:		1,
-	channels_max:		2,
-	buffer_bytes_max:	(64*1024),
-	period_bytes_min:	64,
-	period_bytes_max:	(64*1024),
-	periods_min:		1,
-	periods_max:		1024,
-	fifo_size:		0,
-};
-
-static snd_pcm_hardware_t snd_ice1712_playback_ds =
-{
-	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
-				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
-				 SNDRV_PCM_INFO_MMAP_VALID |
-				 SNDRV_PCM_INFO_PAUSE),
-	formats:		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
-	rates:			SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
-	rate_min:		4000,
-	rate_max:		48000,
-	channels_min:		1,
-	channels_max:		2,
-	buffer_bytes_max:	(128*1024),
-	period_bytes_min:	64,
-	period_bytes_max:	(128*1024),
-	periods_min:		2,
-	periods_max:		2,
-	fifo_size:		0,
-};
-
-static snd_pcm_hardware_t snd_ice1712_capture =
-{
-	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
-				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
-				 SNDRV_PCM_INFO_MMAP_VALID),
-	formats:		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
-	rates:			SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
-	rate_min:		4000,
-	rate_max:		48000,
-	channels_min:		1,
-	channels_max:		2,
-	buffer_bytes_max:	(64*1024),
-	period_bytes_min:	64,
-	period_bytes_max:	(64*1024),
-	periods_min:		1,
-	periods_max:		1024,
-	fifo_size:		0,
-};
-
-static int snd_ice1712_playback_open(snd_pcm_substream_t * substream)
-{
-	snd_pcm_runtime_t *runtime = substream->runtime;
-	ice1712_t *ice = snd_pcm_substream_chip(substream);
-
-	ice->playback_con_substream = substream;
-	runtime->hw = snd_ice1712_playback;
-	return 0;
-}
-
-static int snd_ice1712_playback_ds_open(snd_pcm_substream_t * substream)
-{
-	snd_pcm_runtime_t *runtime = substream->runtime;
-	ice1712_t *ice = snd_pcm_substream_chip(substream);
-	unsigned long flags;
-	u32 tmp;
-
-	ice->playback_con_substream_ds[substream->number] = substream;
-	runtime->hw = snd_ice1712_playback_ds;
-	spin_lock_irqsave(&ice->reg_lock, flags); 
-	tmp = inw(ICEDS(ice, INTMASK)) & ~(1 << (substream->number * 2));
-	outw(tmp, ICEDS(ice, INTMASK));
-	spin_unlock_irqrestore(&ice->reg_lock, flags);
-	return 0;
-}
-
-static int snd_ice1712_capture_open(snd_pcm_substream_t * substream)
-{
-	snd_pcm_runtime_t *runtime = substream->runtime;
-	ice1712_t *ice = snd_pcm_substream_chip(substream);
-
-	ice->capture_con_substream = substream;
-	runtime->hw = snd_ice1712_capture;
-	runtime->hw.rates = ice->ac97->rates_adc;
-	if (!(runtime->hw.rates & SNDRV_PCM_RATE_8000))
-		runtime->hw.rate_min = 48000;
-	return 0;
-}
-
-static int snd_ice1712_playback_close(snd_pcm_substream_t * substream)
-{
-	ice1712_t *ice = snd_pcm_substream_chip(substream);
-
-	ice->playback_con_substream = NULL;
-	return 0;
-}
-
-static int snd_ice1712_playback_ds_close(snd_pcm_substream_t * substream)
-{
-	ice1712_t *ice = snd_pcm_substream_chip(substream);
-	unsigned long flags;
-	u32 tmp;
-
-	spin_lock_irqsave(&ice->reg_lock, flags); 
-	tmp = inw(ICEDS(ice, INTMASK)) | (3 << (substream->number * 2));
-	outw(tmp, ICEDS(ice, INTMASK));
-	spin_unlock_irqrestore(&ice->reg_lock, flags);
-	ice->playback_con_substream_ds[substream->number] = NULL;
-	return 0;
-}
-
-static int snd_ice1712_capture_close(snd_pcm_substream_t * substream)
-{
-	ice1712_t *ice = snd_pcm_substream_chip(substream);
-
-	/* disable ADC power */
-	snd_ac97_update_bits(ice->ac97, AC97_POWERDOWN, 0x0100, 0x0100);
-	ice->capture_con_substream = NULL;
-	return 0;
-}
-
-static snd_pcm_ops_t snd_ice1712_playback_ops = {
-	open:		snd_ice1712_playback_open,
-	close:		snd_ice1712_playback_close,
-	ioctl:		snd_pcm_lib_ioctl,
-	hw_params:	snd_ice1712_hw_params,
-	hw_free:	snd_ice1712_hw_free,
-	prepare:	snd_ice1712_playback_prepare,
-	trigger:	snd_ice1712_playback_trigger,
-	pointer:	snd_ice1712_playback_pointer,
-};
-
-static snd_pcm_ops_t snd_ice1712_playback_ds_ops = {
-	open:		snd_ice1712_playback_ds_open,
-	close:		snd_ice1712_playback_ds_close,
-	ioctl:		snd_pcm_lib_ioctl,
-	hw_params:	snd_ice1712_hw_params,
-	hw_free:	snd_ice1712_hw_free,
-	prepare:	snd_ice1712_playback_ds_prepare,
-	trigger:	snd_ice1712_playback_ds_trigger,
-	pointer:	snd_ice1712_playback_ds_pointer,
-};
-
-static snd_pcm_ops_t snd_ice1712_capture_ops = {
-	open:		snd_ice1712_capture_open,
-	close:		snd_ice1712_capture_close,
-	ioctl:		snd_pcm_lib_ioctl,
-	hw_params:	snd_ice1712_hw_params,
-	hw_free:	snd_ice1712_hw_free,
-	prepare:	snd_ice1712_capture_prepare,
-	trigger:	snd_ice1712_capture_trigger,
-	pointer:	snd_ice1712_capture_pointer,
-};
-
-static void snd_ice1712_pcm_free(snd_pcm_t *pcm)
-{
-	ice1712_t *ice = snd_magic_cast(ice1712_t, pcm->private_data, return);
-	ice->pcm = NULL;
-	snd_pcm_lib_preallocate_free_for_all(pcm);
-}
-
-static int __devinit snd_ice1712_pcm(ice1712_t * ice, int device, snd_pcm_t ** rpcm)
-{
-	snd_pcm_t *pcm;
-	int err;
-
-	if (rpcm)
-		*rpcm = NULL;
-	err = snd_pcm_new(ice->card, "ICE1712 consumer", device, 1, 1, &pcm);
-	if (err < 0)
-		return err;
-
-	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ice1712_playback_ops);
-	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ice1712_capture_ops);
-
-	pcm->private_data = ice;
-	pcm->private_free = snd_ice1712_pcm_free;
-	pcm->info_flags = 0;
-	strcpy(pcm->name, "ICE1712 consumer");
-	ice->pcm = pcm;
-
-	snd_pcm_lib_preallocate_pci_pages_for_all(ice->pci, pcm, 64*1024, 64*1024);
-
-	if (rpcm)
-		*rpcm = pcm;
-
-	printk(KERN_WARNING "Consumer PCM code does not work well at the moment --jk\n");
-
-	return 0;
-}
-
-static void snd_ice1712_pcm_free_ds(snd_pcm_t *pcm)
-{
-	ice1712_t *ice = snd_magic_cast(ice1712_t, pcm->private_data, return);
-	ice->pcm_ds = NULL;
-	snd_pcm_lib_preallocate_free_for_all(pcm);
-}
-
-static int __devinit snd_ice1712_pcm_ds(ice1712_t * ice, int device, snd_pcm_t ** rpcm)
-{
-	snd_pcm_t *pcm;
-	int err;
-
-	if (rpcm)
-		*rpcm = NULL;
-	err = snd_pcm_new(ice->card, "ICE1712 consumer (DS)", device, 6, 0, &pcm);
-	if (err < 0)
-		return err;
-
-	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ice1712_playback_ds_ops);
-
-	pcm->private_data = ice;
-	pcm->private_free = snd_ice1712_pcm_free_ds;
-	pcm->info_flags = 0;
-	strcpy(pcm->name, "ICE1712 consumer (DS)");
-	ice->pcm_ds = pcm;
-
-	snd_pcm_lib_preallocate_pci_pages_for_all(ice->pci, pcm, 64*1024, 128*1024);
-
-	if (rpcm)
-		*rpcm = pcm;
-
-	return 0;
-}
-
-/*
- *  PCM code - professional part (multitrack)
- */
-
-static unsigned int rates[] = { 8000, 9600, 11025, 12000, 16000, 22050, 24000,
-				32000, 44100, 48000, 64000, 88200, 96000 };
-
-#define RATES sizeof(rates) / sizeof(rates[0])
-
-static snd_pcm_hw_constraint_list_t hw_constraints_rates = {
-	count: RATES,
-	list: rates,
-	mask: 0,
-};
-
-#if 0
-
-static int snd_ice1712_playback_pro_ioctl(snd_pcm_substream_t * substream,
-					  unsigned int cmd,
-					  void *arg)
-{
-	ice1712_t *ice = snd_pcm_substream_chip(substream);
-
-	switch (cmd) {
-	case SNDRV_PCM_IOCTL1_DIG_INFO:
-	{
-		snd_pcm_dig_info_t *info = arg;
-		switch (ice->eeprom.subvendor) {
-		case ICE1712_SUBDEVICE_DELTA1010:
-		case ICE1712_SUBDEVICE_DELTADIO2496:
-		case ICE1712_SUBDEVICE_DELTA66:
-			return snd_ice1712_dig_cs8403_info(substream, info);
-		}
-	}
-	case SNDRV_PCM_IOCTL1_DIG_PARAMS:
-	{
-		snd_pcm_dig_params_t *params = arg;
-		switch (ice->eeprom.subvendor) {
-		case ICE1712_SUBDEVICE_DELTA1010:
-		case ICE1712_SUBDEVICE_DELTADIO2496:
-		case ICE1712_SUBDEVICE_DELTA66:
-			return snd_ice1712_dig_cs8403_params(substream, params);
-		}
-	}
-	default:
-		break;
-	}
-	return snd_pcm_lib_ioctl(substream, cmd, arg);
-}
-
-#endif
-
-static int snd_ice1712_pro_trigger(snd_pcm_substream_t *substream,
-				   int cmd)
-{
-	ice1712_t *ice = snd_pcm_substream_chip(substream);
-	switch (cmd) {
-	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
-	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
-	{
-		unsigned int what;
-		unsigned int old;
-		if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
-			return -EINVAL;
-		what = ICE1712_PLAYBACK_PAUSE;
-		snd_pcm_trigger_done(substream, substream);
-		spin_lock(&ice->reg_lock);
-		old = inl(ICEMT(ice, PLAYBACK_CONTROL));
-		if (cmd == SNDRV_PCM_TRIGGER_PAUSE_PUSH)
-			old |= what;
-		else
-			old &= ~what;
-		outl(old, ICEMT(ice, PLAYBACK_CONTROL));
-		spin_unlock(&ice->reg_lock);
-		break;
-	}
-	case SNDRV_PCM_TRIGGER_START:
-	case SNDRV_PCM_TRIGGER_STOP:
-	{
-		unsigned int what = 0;
-		unsigned int old;
-		snd_pcm_substream_t *s = substream;
-		do {
-			if (s == ice->playback_pro_substream) {
-				what |= ICE1712_PLAYBACK_START;
-				snd_pcm_trigger_done(s, substream);
-			} else if (s == ice->capture_pro_substream) {
-				what |= ICE1712_CAPTURE_START_SHADOW;
-				snd_pcm_trigger_done(s, substream);
-			}
-			s = s->link_next;
-		} while (s != substream);
-		spin_lock(&ice->reg_lock);
-		old = inl(ICEMT(ice, PLAYBACK_CONTROL));
-		if (cmd == SNDRV_PCM_TRIGGER_START)
-			old |= what;
-		else
-			old &= ~what;
-		outl(old, ICEMT(ice, PLAYBACK_CONTROL));
-		spin_unlock(&ice->reg_lock);
-		break;
-	}
-	default:
-		return -EINVAL;
-	}
-	return 0;
-}
-
-static int snd_ice1712_playback_pro_prepare(snd_pcm_substream_t * substream)
-{
-	unsigned long flags;
-	ice1712_t *ice = snd_pcm_substream_chip(substream);
-	unsigned int tmp;
-	int change;
-
-	ice->playback_pro_size = snd_pcm_lib_buffer_bytes(substream);
-	snd_ice1712_set_pro_rate(ice, substream);
-	spin_lock_irqsave(&ice->reg_lock, flags);
-	outl(substream->runtime->dma_addr, ICEMT(ice, PLAYBACK_ADDR));
-	outw((ice->playback_pro_size >> 2) - 1, ICEMT(ice, PLAYBACK_SIZE));
-	outw((snd_pcm_lib_period_bytes(substream) >> 2) - 1, ICEMT(ice, PLAYBACK_COUNT));
-	switch (ice->eeprom.subvendor) {
-	case ICE1712_SUBDEVICE_DELTA1010:
-	case ICE1712_SUBDEVICE_DELTADIO2496:
-	case ICE1712_SUBDEVICE_DELTA66:
-		/* setup S/PDIF */
-		tmp = ice->cs8403_spdif_stream_bits;
-		if (tmp & 0x01)		/* consumer */
-			tmp &= (tmp & 0x01) ? ~0x06 : ~0x18;
-		switch (substream->runtime->rate) {
-		case 32000: tmp |= (tmp & 0x01) ? 0x04 : 0x00; break;
-		case 44100: tmp |= (tmp & 0x01) ? 0x00 : 0x10; break;
-		case 48000: tmp |= (tmp & 0x01) ? 0x02 : 0x08; break;
-		default: tmp |= (tmp & 0x01) ? 0x00 : 0x18; break;
-		}
-		change = ice->cs8403_spdif_stream_bits != tmp;
-		ice->cs8403_spdif_stream_bits = tmp;
-		spin_unlock_irqrestore(&ice->reg_lock, flags);
-		if (change)
-			snd_ctl_notify(ice->card, SNDRV_CTL_EVENT_MASK_VALUE, &ice->spdif_stream_ctl->id);
-		snd_ice1712_delta_cs8403_spdif_write(ice, tmp);
-		return 0;
-	case ICE1712_SUBDEVICE_EWX2496:
-	case ICE1712_SUBDEVICE_AUDIOPHILE:
-		snd_cs8427_iec958_pcm(ice->cs8427, substream->runtime->rate);
-		break;
-	case ICE1712_SUBDEVICE_EWS88MT:
-	case ICE1712_SUBDEVICE_EWS88D:
-		/* setup S/PDIF */
-		tmp = ice->cs8403_spdif_stream_bits;
-		if (tmp & 0x10)		/* consumer */
-			tmp &= (tmp & 0x01) ? ~0x06 : ~0x60;
-		switch (substream->runtime->rate) {
-		case 32000: tmp |= (tmp & 0x01) ? 0x02 : 0x00; break;
-		case 44100: tmp |= (tmp & 0x01) ? 0x06 : 0x40; break;
-		case 48000: tmp |= (tmp & 0x01) ? 0x04 : 0x20; break;
-		default: tmp |= (tmp & 0x01) ? 0x06 : 0x40; break;
-		}
-		change = ice->cs8403_spdif_stream_bits != tmp;
-		ice->cs8403_spdif_stream_bits = tmp;
-		spin_unlock_irqrestore(&ice->reg_lock, flags);
-		if (change)
-			snd_ctl_notify(ice->card, SNDRV_CTL_EVENT_MASK_VALUE, &ice->spdif_stream_ctl->id);
-		snd_ice1712_ews_cs8404_spdif_write(ice, tmp);
-		return 0;
-	}
-
-	spin_unlock_irqrestore(&ice->reg_lock, flags);
-	return 0;
-}
-
-static int snd_ice1712_capture_pro_prepare(snd_pcm_substream_t * substream)
-{
-	unsigned long flags;
-	ice1712_t *ice = snd_pcm_substream_chip(substream);
-
-	ice->capture_pro_size = snd_pcm_lib_buffer_bytes(substream);
-	snd_ice1712_set_pro_rate(ice, substream);
-	spin_lock_irqsave(&ice->reg_lock, flags);
-	outl(substream->runtime->dma_addr, ICEMT(ice, CAPTURE_ADDR));
-	outw((ice->capture_pro_size >> 2) - 1, ICEMT(ice, CAPTURE_SIZE));
-	outw((snd_pcm_lib_period_bytes(substream) >> 2) - 1, ICEMT(ice, CAPTURE_COUNT));
-	spin_unlock_irqrestore(&ice->reg_lock, flags);
-	return 0;
-}
-
-static snd_pcm_uframes_t snd_ice1712_playback_pro_pointer(snd_pcm_substream_t * substream)
-{
-	ice1712_t *ice = snd_pcm_substream_chip(substream);
-	size_t ptr;
-
-	if (!(inl(ICEMT(ice, PLAYBACK_CONTROL)) & ICE1712_PLAYBACK_START))
-		return 0;
-	ptr = ice->playback_pro_size - (inw(ICEMT(ice, PLAYBACK_SIZE)) << 2);
-	return bytes_to_frames(substream->runtime, ptr);
-}
-
-static snd_pcm_uframes_t snd_ice1712_capture_pro_pointer(snd_pcm_substream_t * substream)
-{
-	ice1712_t *ice = snd_pcm_substream_chip(substream);
-	size_t ptr;
-
-	if (!(inl(ICEMT(ice, PLAYBACK_CONTROL)) & ICE1712_CAPTURE_START_SHADOW))
-		return 0;
-	ptr = ice->capture_pro_size - (inw(ICEMT(ice, CAPTURE_SIZE)) << 2);
-	return bytes_to_frames(substream->runtime, ptr);
-}
-
-static snd_pcm_hardware_t snd_ice1712_playback_pro =
-{
-	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
-				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
-				 SNDRV_PCM_INFO_MMAP_VALID |
-				 SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_SYNC_START),
-	formats:		SNDRV_PCM_FMTBIT_S32_LE,
-	rates:			SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_96000,
-	rate_min:		4000,
-	rate_max:		96000,
-	channels_min:		10,
-	channels_max:		10,
-	buffer_bytes_max:	(256*1024),
-	period_bytes_min:	10 * 4 * 2,
-	period_bytes_max:	131040,
-	periods_min:		1,
-	periods_max:		1024,
-	fifo_size:		0,
-};
-
-static snd_pcm_hardware_t snd_ice1712_capture_pro =
-{
-	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
-				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
-				 SNDRV_PCM_INFO_MMAP_VALID |
-				 SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_SYNC_START),
-	formats:		SNDRV_PCM_FMTBIT_S32_LE,
-	rates:			SNDRV_PCM_RATE_KNOT | SNDRV_PCM_RATE_8000_96000,
-	rate_min:		4000,
-	rate_max:		96000,
-	channels_min:		12,
-	channels_max:		12,
-	buffer_bytes_max:	(256*1024),
-	period_bytes_min:	12 * 4 * 2,
-	period_bytes_max:	131040,
-	periods_min:		1,
-	periods_max:		1024,
-	fifo_size:		0,
-};
-
-static int snd_ice1712_playback_pro_open(snd_pcm_substream_t * substream)
-{
-	snd_pcm_runtime_t *runtime = substream->runtime;
-	ice1712_t *ice = snd_pcm_substream_chip(substream);
-
-	ice->playback_pro_substream = substream;
-	runtime->hw = snd_ice1712_playback_pro;
-	snd_pcm_set_sync(substream);
-	snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
-	snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates);
-
-	ice->cs8403_spdif_stream_bits = ice->cs8403_spdif_bits;
-	if (ice->cs8427)
-		snd_cs8427_iec958_active(ice->cs8427, 1);
-
-	return 0;
-}
-
-static int snd_ice1712_capture_pro_open(snd_pcm_substream_t * substream)
-{
-	ice1712_t *ice = snd_pcm_substream_chip(substream);
-	snd_pcm_runtime_t *runtime = substream->runtime;
-
-	ice->capture_pro_substream = substream;
-	runtime->hw = snd_ice1712_capture_pro;
-	snd_pcm_set_sync(substream);
-	snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
-	snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates);
-	return 0;
-}
-
-static int snd_ice1712_playback_pro_close(snd_pcm_substream_t * substream)
-{
-	ice1712_t *ice = snd_pcm_substream_chip(substream);
-
-	ice->playback_pro_substream = NULL;
-	if (ice->cs8427)
-		snd_cs8427_iec958_active(ice->cs8427, 0);
-
-	return 0;
-}
-
-static int snd_ice1712_capture_pro_close(snd_pcm_substream_t * substream)
-{
-	ice1712_t *ice = snd_pcm_substream_chip(substream);
-
-	ice->capture_pro_substream = NULL;
-	return 0;
-}
-
-static void snd_ice1712_pcm_profi_free(snd_pcm_t *pcm)
-{
-	ice1712_t *ice = snd_magic_cast(ice1712_t, pcm->private_data, return);
-	ice->pcm_pro = NULL;
-	snd_pcm_lib_preallocate_free_for_all(pcm);
-}
-
-static snd_pcm_ops_t snd_ice1712_playback_pro_ops = {
-	open:		snd_ice1712_playback_pro_open,
-	close:		snd_ice1712_playback_pro_close,
-	ioctl:		snd_pcm_lib_ioctl,
-	hw_params:	snd_ice1712_hw_params,
-	hw_free:	snd_ice1712_hw_free,
-	prepare:	snd_ice1712_playback_pro_prepare,
-	trigger:	snd_ice1712_pro_trigger,
-	pointer:	snd_ice1712_playback_pro_pointer,
-};
-
-static snd_pcm_ops_t snd_ice1712_capture_pro_ops = {
-	open:		snd_ice1712_capture_pro_open,
-	close:		snd_ice1712_capture_pro_close,
-	ioctl:		snd_pcm_lib_ioctl,
-	hw_params:	snd_ice1712_hw_params,
-	hw_free:	snd_ice1712_hw_free,
-	prepare:	snd_ice1712_capture_pro_prepare,
-	trigger:	snd_ice1712_pro_trigger,
-	pointer:	snd_ice1712_capture_pro_pointer,
-};
-
-static int __devinit snd_ice1712_pcm_profi(ice1712_t * ice, int device, snd_pcm_t ** rpcm)
-{
-	snd_pcm_t *pcm;
-	int err;
-
-	if (rpcm)
-		*rpcm = NULL;
-	err = snd_pcm_new(ice->card, "ICE1712 multi", device, 1, 1, &pcm);
-	if (err < 0)
-		return err;
-
-	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_ice1712_playback_pro_ops);
-	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_ice1712_capture_pro_ops);
-
-	pcm->private_data = ice;
-	pcm->private_free = snd_ice1712_pcm_profi_free;
-	pcm->info_flags = 0;
-	strcpy(pcm->name, "ICE1712 multi");
-
-	snd_pcm_lib_preallocate_pci_pages_for_all(ice->pci, pcm, 256*1024, 256*1024);
-
-	ice->pcm_pro = pcm;
-	if (rpcm)
-		*rpcm = pcm;
-	
-	if (ice->cs8427) {
-		/* assign channels to iec958 */
-		err = snd_cs8427_iec958_build(ice->cs8427,
-					      pcm->streams[0].substream,
-					      pcm->streams[1].substream);
-		if (err < 0)
-			return err;
-	}
-
-	if ((err = snd_ice1712_build_pro_mixer(ice)) < 0)
-		return err;
-	return 0;
-}
-
-/*
- *  Mixer section
- */
-
-static void snd_ice1712_update_volume(ice1712_t *ice, int index)
-{
-	unsigned int vol = ice->pro_volumes[index];
-	unsigned short val = 0;
-
-	val |= (vol & 0x8000) == 0 ? (96 - (vol & 0x7f)) : 0x7f;
-	val |= ((vol & 0x80000000) == 0 ? (96 - ((vol >> 16) & 0x7f)) : 0x7f) << 8;
-	outb(index, ICEMT(ice, MONITOR_INDEX));
-	outw(val, ICEMT(ice, MONITOR_VOLUME));
-}
-
-static int snd_ice1712_pro_mixer_switch_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
-{
-	uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
-	uinfo->count = 2;
-	uinfo->value.integer.min = 0;
-	uinfo->value.integer.max = 1;
-	return 0;
-}
-
-static int snd_ice1712_pro_mixer_switch_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
-{
-	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
-	unsigned long flags;
-	int index = kcontrol->private_value;
-	
-	spin_lock_irqsave(&ice->reg_lock, flags);
-	ucontrol->value.integer.value[0] = !((ice->pro_volumes[index] >> 15) & 1);
-	ucontrol->value.integer.value[1] = !((ice->pro_volumes[index] >> 31) & 1);
-	spin_unlock_irqrestore(&ice->reg_lock, flags);
-	return 0;
-}
-
-static int snd_ice1712_pro_mixer_switch_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
-{
-	unsigned long flags;
-	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
-	int index = kcontrol->private_value;
-	unsigned int nval, change;
-
-	nval = (ucontrol->value.integer.value[0] ? 0 : 0x00008000) |
-	       (ucontrol->value.integer.value[1] ? 0 : 0x80000000);
-	spin_lock_irqsave(&ice->reg_lock, flags);
-	nval |= ice->pro_volumes[index] & ~0x80008000;
-	change = nval != ice->pro_volumes[index];
-	ice->pro_volumes[index] = nval;
-	snd_ice1712_update_volume(ice, index);
-	spin_unlock_irqrestore(&ice->reg_lock, flags);
-	return change;
-}
-
-static int snd_ice1712_pro_mixer_volume_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
-{
-	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
-	uinfo->count = 2;
-	uinfo->value.integer.min = 0;
-	uinfo->value.integer.max = 96;
-	return 0;
-}
-
-static int snd_ice1712_pro_mixer_volume_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
-{
-	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
-	unsigned long flags;
-	int index = kcontrol->private_value;
-	
-	spin_lock_irqsave(&ice->reg_lock, flags);
-	ucontrol->value.integer.value[0] = (ice->pro_volumes[index] >> 0) & 127;
-	ucontrol->value.integer.value[1] = (ice->pro_volumes[index] >> 16) & 127;
-	spin_unlock_irqrestore(&ice->reg_lock, flags);
-	return 0;
-}
-
-static int snd_ice1712_pro_mixer_volume_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
-{
-	unsigned long flags;
-	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
-	int index = kcontrol->private_value;
-	unsigned int nval, change;
-
-	nval = (ucontrol->value.integer.value[0] & 127) |
-	       ((ucontrol->value.integer.value[1] & 127) << 16);
-	spin_lock_irqsave(&ice->reg_lock, flags);
-	nval |= ice->pro_volumes[index] & ~0x007f007f;
-	change = nval != ice->pro_volumes[index];
-	ice->pro_volumes[index] = nval;
-	snd_ice1712_update_volume(ice, index);
-	spin_unlock_irqrestore(&ice->reg_lock, flags);
-	return change;
-}
-
-static int snd_ice1712_ak4524_volume_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
-{
-	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
-	uinfo->count = 1;
-	uinfo->value.integer.min = 0;
-	uinfo->value.integer.max = 127;
-	return 0;
-}
-
-static int snd_ice1712_ak4524_volume_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
-{
-	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
-	int chip = kcontrol->private_value / 8;
-	int addr = kcontrol->private_value % 8;
-	ucontrol->value.integer.value[0] = ice->ak4524_images[chip][addr];
-	return 0;
-}
-
-static int snd_ice1712_ak4524_volume_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
-{
-	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
-	int chip = kcontrol->private_value / 8;
-	int addr = kcontrol->private_value % 8;
-	unsigned char nval = ucontrol->value.integer.value[0];
-	int change = ice->ak4524_images[chip][addr] != nval;
-	if (change)
-		snd_ice1712_ak4524_write(ice, chip, addr, nval);
-	return change;
-}
-
-static int snd_ice1712_ak4524_ipga_gain_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
-{
-	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
-	uinfo->count = 1;
-	uinfo->value.integer.min = 0;
-	uinfo->value.integer.max = 36;
-	return 0;
-}
-
-static int snd_ice1712_ak4524_ipga_gain_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
-{
-	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
-	int chip = kcontrol->private_value / 8;
-	int addr = kcontrol->private_value % 8;
-	ucontrol->value.integer.value[0] = ice->ak4524_ipga_gain[chip][addr-4] & 0x7f;
-	return 0;
-}
-
-static int snd_ice1712_ak4524_ipga_gain_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
-{
-	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
-	int chip = kcontrol->private_value / 8;
-	int addr = kcontrol->private_value % 8;
-	unsigned char nval = (ucontrol->value.integer.value[0] % 37) | 0x80;
-	int change = ice->ak4524_ipga_gain[chip][addr] != nval;
-	if (change)
-		snd_ice1712_ak4524_write(ice, chip, addr, nval);
-	return change;
-}
-
-static int snd_ice1712_ak4524_deemphasis_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
-{
-	static char *texts[4] = {
-		"44.1kHz", "Off", "48kHz", "32kHz",
-	};
-	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 = 3;
-	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
-	return 0;
-}
-
-static int snd_ice1712_ak4524_deemphasis_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t *ucontrol)
-{
-	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
-	int chip = kcontrol->id.index;
-	ucontrol->value.enumerated.item[0] = ice->ak4524_images[chip][3] & 3;
-	return 0;
-}
-
-static int snd_ice1712_ak4524_deemphasis_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
-{
-	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
-	int chip = kcontrol->id.index;
-	unsigned char nval = ucontrol->value.enumerated.item[0];
-	int change;
-	nval |= (nval & 3) | (ice->ak4524_images[chip][3] & ~3);
-	change = ice->ak4524_images[chip][3] != nval;
-	if (change)
-		snd_ice1712_ak4524_write(ice, chip, 3, nval);
-	return change;
-}
-
-static int __init snd_ice1712_build_pro_mixer(ice1712_t *ice)
-{
-	snd_card_t * card = ice->card;
-	snd_kcontrol_t ctl;
-	int idx, err;
-
-	/* PCM playback */
-	for (idx = 0; idx < 10; idx++) {
-		memset(&ctl, 0, sizeof(ctl));
-		strcpy(ctl.id.name, "Multi Playback Switch");
-		ctl.id.index = idx;
-		ctl.id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
-		ctl.info = snd_ice1712_pro_mixer_switch_info;
-		ctl.access = SNDRV_CTL_ELEM_ACCESS_READ|SNDRV_CTL_ELEM_ACCESS_WRITE;
-		ctl.get = snd_ice1712_pro_mixer_switch_get;
-		ctl.put = snd_ice1712_pro_mixer_switch_put;
-		ctl.private_value = idx;
-		ctl.private_data = ice;
-		if ((err = snd_ctl_add(card, snd_ctl_new(&ctl))) < 0)
-			return err;
-		memset(&ctl, 0, sizeof(ctl));
-		strcpy(ctl.id.name, "Multi Playback Volume");
-		ctl.id.index = idx;
-		ctl.id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
-		ctl.info = snd_ice1712_pro_mixer_volume_info;
-		ctl.access = SNDRV_CTL_ELEM_ACCESS_READ|SNDRV_CTL_ELEM_ACCESS_WRITE;
-		ctl.get = snd_ice1712_pro_mixer_volume_get;
-		ctl.put = snd_ice1712_pro_mixer_volume_put;
-		ctl.private_value = idx;
-		ctl.private_data = ice;
-		if ((err = snd_ctl_add(card, snd_ctl_new(&ctl))) < 0)
-			return err;
-	}
-
-	/* PCM capture */
-	for (idx = 0; idx < 10; idx++) {
-		memset(&ctl, 0, sizeof(ctl));
-		strcpy(ctl.id.name, "Multi Capture Switch");
-		ctl.id.index = idx;
-		ctl.id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
-		ctl.info = snd_ice1712_pro_mixer_switch_info;
-		ctl.access = SNDRV_CTL_ELEM_ACCESS_READ|SNDRV_CTL_ELEM_ACCESS_WRITE;
-		ctl.get = snd_ice1712_pro_mixer_switch_get;
-		ctl.put = snd_ice1712_pro_mixer_switch_put;
-		ctl.private_value = idx + 10;
-		ctl.private_data = ice;
-		if ((err = snd_ctl_add(card, snd_ctl_new(&ctl))) < 0)
-			return err;
-		memset(&ctl, 0, sizeof(ctl));
-		strcpy(ctl.id.name, "Multi Capture Volume");
-		ctl.id.index = idx;
-		ctl.id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
-		ctl.info = snd_ice1712_pro_mixer_volume_info;
-		ctl.access = SNDRV_CTL_ELEM_ACCESS_READ|SNDRV_CTL_ELEM_ACCESS_WRITE;
-		ctl.get = snd_ice1712_pro_mixer_volume_get;
-		ctl.put = snd_ice1712_pro_mixer_volume_put;
-		ctl.private_value = idx + 10;
-		ctl.private_data = ice;
-		if ((err = snd_ctl_add(card, snd_ctl_new(&ctl))) < 0)
-			return err;
-	}
-	
-	/* initialize volumes */
-	for (idx = 0; idx < 20; idx++) {
-		ice->pro_volumes[idx] = 0x80008000;	/* mute */
-		snd_ice1712_update_volume(ice, idx);
-	}
-	return 0;
-}
-
-static void snd_ice1712_ac97_init(ac97_t *ac97)
-{
-	// ice1712_t *ice = snd_magic_cast(ice1712_t, ac97->private_data, return);
-
-        /* disable center DAC/surround DAC/LFE DAC/MIC ADC */
-        snd_ac97_update_bits(ac97, AC97_EXTENDED_STATUS, 0xe800, 0xe800);
-}
-
-static void snd_ice1712_mixer_free_ac97(ac97_t *ac97)
-{
-	ice1712_t *ice = snd_magic_cast(ice1712_t, ac97->private_data, return);
-	ice->ac97 = NULL;
-}
-
-static int __devinit snd_ice1712_ac97_mixer(ice1712_t * ice)
-{
-	int err;
-
-	if (!(ice->eeprom.codec & ICE1712_CFG_NO_CON_AC97)) {
-		ac97_t ac97;
-		memset(&ac97, 0, sizeof(ac97));
-		ac97.write = snd_ice1712_ac97_write;
-		ac97.read = snd_ice1712_ac97_read;
-		ac97.init = snd_ice1712_ac97_init;
-		ac97.private_data = ice;
-		ac97.private_free = snd_ice1712_mixer_free_ac97;
-		if ((err = snd_ac97_mixer(ice->card, &ac97, &ice->ac97)) < 0) {
-			printk(KERN_WARNING "ice1712: cannot initialize ac97 for consumer, skipped\n");
-			// return err;
-		} else {
-			if ((err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_mixer_digmix_route_ac97, ice))) < 0)
-				return err;
-		}
-		return 0;
-	}
-	/* hmm.. can we have both consumer and pro ac97 mixers? */
-	if (! (ice->eeprom.aclink & ICE1712_CFG_PRO_I2S)) {
-		ac97_t ac97;
-		memset(&ac97, 0, sizeof(ac97));
-		ac97.write = snd_ice1712_pro_ac97_write;
-		ac97.read = snd_ice1712_pro_ac97_read;
-		ac97.init = snd_ice1712_ac97_init;
-		ac97.private_data = ice;
-		ac97.private_free = snd_ice1712_mixer_free_ac97;
-		if ((err = snd_ac97_mixer(ice->card, &ac97, &ice->ac97)) < 0) {
-			printk(KERN_WARNING "ice1712: cannot initialize pro ac97, skipped\n");
-			// return err;
-		}
-		return 0;
-	}
-	/* I2S mixer only */
-	strcat(ice->card->mixername, "ICE1712 - multitrack");
-	return 0;
-}
-
-/*
- *
- */
-
-static void snd_ice1712_proc_read(snd_info_entry_t *entry, 
-				  snd_info_buffer_t * buffer)
-{
-	ice1712_t *ice = snd_magic_cast(ice1712_t, entry->private_data, return);
-	unsigned int idx;
-
-	snd_iprintf(buffer, "ICE1712\n\n");
-	snd_iprintf(buffer, "EEPROM:\n");
-	snd_iprintf(buffer, "  Subvendor        : 0x%x\n", ice->eeprom.subvendor);
-	snd_iprintf(buffer, "  Size             : %i bytes\n", ice->eeprom.size);
-	snd_iprintf(buffer, "  Version          : %i\n", ice->eeprom.version);
-	snd_iprintf(buffer, "  Codec            : 0x%x\n", ice->eeprom.codec);
-	snd_iprintf(buffer, "  ACLink           : 0x%x\n", ice->eeprom.aclink);
-	snd_iprintf(buffer, "  I2S ID           : 0x%x\n", ice->eeprom.i2sID);
-	snd_iprintf(buffer, "  S/PDIF           : 0x%x\n", ice->eeprom.spdif);
-	snd_iprintf(buffer, "  GPIO mask        : 0x%x\n", ice->eeprom.gpiomask);
-	snd_iprintf(buffer, "  GPIO state       : 0x%x\n", ice->eeprom.gpiostate);
-	snd_iprintf(buffer, "  GPIO direction   : 0x%x\n", ice->eeprom.gpiodir);
-	snd_iprintf(buffer, "  AC'97 main       : 0x%x\n", ice->eeprom.ac97main);
-	snd_iprintf(buffer, "  AC'97 pcm        : 0x%x\n", ice->eeprom.ac97pcm);
-	snd_iprintf(buffer, "  AC'97 record     : 0x%x\n", ice->eeprom.ac97rec);
-	snd_iprintf(buffer, "  AC'97 record src : 0x%x\n", ice->eeprom.ac97recsrc);
-	for (idx = 0; idx < 4; idx++)
-		snd_iprintf(buffer, "  DAC ID #%i        : 0x%x\n", idx, ice->eeprom.dacID[idx]);
-	for (idx = 0; idx < 4; idx++)
-		snd_iprintf(buffer, "  ADC ID #%i        : 0x%x\n", idx, ice->eeprom.adcID[idx]);
-	for (idx = 0x1c; idx < ice->eeprom.size && idx < 0x1c + sizeof(ice->eeprom.extra); idx++)
-		snd_iprintf(buffer, "  Extra #%02i        : 0x%x\n", idx, ice->eeprom.extra[idx - 0x1c]);
-}
-
-static void __devinit snd_ice1712_proc_init(ice1712_t * ice)
-{
-	snd_info_entry_t *entry;
-
-	if ((entry = snd_info_create_card_entry(ice->card, "ice1712", ice->card->proc_root)) != NULL) {
-		entry->content = SNDRV_INFO_CONTENT_TEXT;
-		entry->private_data = ice;
-		entry->mode = S_IFREG | S_IRUGO | S_IWUSR;
-		entry->c.text.read_size = 2048;
-		entry->c.text.read = snd_ice1712_proc_read;
-		if (snd_info_register(entry) < 0) {
-			snd_info_free_entry(entry);
-			entry = NULL;
-		}
-	}
-	ice->proc_entry = entry;
-}
-
-static void snd_ice1712_proc_done(ice1712_t * ice)
-{
-	if (ice->proc_entry) {
-		snd_info_unregister(ice->proc_entry);
-		ice->proc_entry = NULL;
-	}
-}
-
-/*
- *
- */
-
-static int snd_ice1712_eeprom_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
-{
-	uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
-	uinfo->count = 32;
-	return 0;
-}
-
-static int snd_ice1712_eeprom_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
-{
-	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
-	
-	memcpy(ucontrol->value.bytes.data, &ice->eeprom, 32);
-	return 0;
-}
-
-static snd_kcontrol_new_t snd_ice1712_eeprom __devinitdata = {
-	iface: SNDRV_CTL_ELEM_IFACE_CARD,
-	name: "ICE1712 EEPROM",
-	access: SNDRV_CTL_ELEM_ACCESS_READ,
-	info: snd_ice1712_eeprom_info,
-	get: snd_ice1712_eeprom_get
-};
-
-/*
- */
-static int snd_ice1712_spdif_default_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_ice1712_spdif_default_get(snd_kcontrol_t * kcontrol,
-					 snd_ctl_elem_value_t * ucontrol)
-{
-	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
-	
-	switch (ice->eeprom.subvendor) {
-	case ICE1712_SUBDEVICE_DELTA1010:
-	case ICE1712_SUBDEVICE_DELTADIO2496:
-	case ICE1712_SUBDEVICE_DELTA66:
-		snd_cs8403_decode_spdif_bits(&ucontrol->value.iec958, ice->cs8403_spdif_bits);
-		break;
-	case ICE1712_SUBDEVICE_EWS88MT:
-	case ICE1712_SUBDEVICE_EWS88D:
-		snd_cs8404_decode_spdif_bits(&ucontrol->value.iec958, ice->cs8403_spdif_bits);
-		break;
-	}
-	return 0;
-}
-
-static int snd_ice1712_spdif_default_put(snd_kcontrol_t * kcontrol,
-					 snd_ctl_elem_value_t * ucontrol)
-{
-	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
-	unsigned long flags;
-	unsigned int val;
-	int change;
-
-	switch (ice->eeprom.subvendor) {
-	case ICE1712_SUBDEVICE_DELTA1010:
-	case ICE1712_SUBDEVICE_DELTADIO2496:
-	case ICE1712_SUBDEVICE_DELTA66:
-		val = snd_cs8403_encode_spdif_bits(&ucontrol->value.iec958);
-		spin_lock_irqsave(&ice->reg_lock, flags);
-		change = ice->cs8403_spdif_bits != val;
-		ice->cs8403_spdif_bits = val;
-		if (change && ice->playback_pro_substream == NULL) {
-			spin_unlock_irqrestore(&ice->reg_lock, flags);
-			snd_ice1712_delta_cs8403_spdif_write(ice, val);
-		} else {
-			spin_unlock_irqrestore(&ice->reg_lock, flags);
-		}
-		break;
-	case ICE1712_SUBDEVICE_EWS88MT:
-	case ICE1712_SUBDEVICE_EWS88D:
-		val = snd_cs8404_encode_spdif_bits(&ucontrol->value.iec958);
-		spin_lock_irqsave(&ice->reg_lock, flags);
-		change = ice->cs8403_spdif_bits != val;
-		ice->cs8403_spdif_bits = val;
-		if (change && ice->playback_pro_substream == NULL) {
-			spin_unlock_irqrestore(&ice->reg_lock, flags);
-			snd_ice1712_ews_cs8404_spdif_write(ice, val);
-		} else {
-			spin_unlock_irqrestore(&ice->reg_lock, flags);
-		}
-		break;
-	default:
-		change = 0;
-	}
-	return change;
-}
-
-static snd_kcontrol_new_t snd_ice1712_spdif_default __devinitdata =
-{
-	iface:		SNDRV_CTL_ELEM_IFACE_PCM,
-	name:           SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
-	info:		snd_ice1712_spdif_default_info,
-	get:		snd_ice1712_spdif_default_get,
-	put:		snd_ice1712_spdif_default_put
-};
-
-static int snd_ice1712_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_ice1712_spdif_maskc_get(snd_kcontrol_t * kcontrol,
-				       snd_ctl_elem_value_t * ucontrol)
-{
-	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
-
-	switch (ice->eeprom.subvendor) {
-	case ICE1712_SUBDEVICE_DELTA1010:
-	case ICE1712_SUBDEVICE_DELTADIO2496:
-	case ICE1712_SUBDEVICE_DELTA66:
-	case ICE1712_SUBDEVICE_EWS88MT:
-	case ICE1712_SUBDEVICE_EWS88D:
-		ucontrol->value.iec958.status[0] = IEC958_AES0_NONAUDIO |
-						     IEC958_AES0_PROFESSIONAL |
-						     IEC958_AES0_CON_NOT_COPYRIGHT |
-						     IEC958_AES0_CON_EMPHASIS;
-		ucontrol->value.iec958.status[1] = IEC958_AES1_CON_ORIGINAL |
-						     IEC958_AES1_CON_CATEGORY;
-		ucontrol->value.iec958.status[3] = IEC958_AES3_CON_FS;
-		break;
-	case ICE1712_SUBDEVICE_AUDIOPHILE:
-	case ICE1712_SUBDEVICE_EWX2496:
-		ucontrol->value.iec958.status[0] = 0xff;
-		ucontrol->value.iec958.status[1] = 0xff;
-		ucontrol->value.iec958.status[2] = 0xff;
-		ucontrol->value.iec958.status[3] = 0xff;
-		ucontrol->value.iec958.status[4] = 0xff;
-		break;
-	}
-	return 0;
-}
-
-static int snd_ice1712_spdif_maskp_get(snd_kcontrol_t * kcontrol,
-				       snd_ctl_elem_value_t * ucontrol)
-{
-	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
-
-	switch (ice->eeprom.subvendor) {
-	case ICE1712_SUBDEVICE_DELTA1010:
-	case ICE1712_SUBDEVICE_DELTADIO2496:
-	case ICE1712_SUBDEVICE_DELTA66:
-	case ICE1712_SUBDEVICE_EWS88MT:
-	case ICE1712_SUBDEVICE_EWS88D:
-		ucontrol->value.iec958.status[0] = IEC958_AES0_NONAUDIO |
-						     IEC958_AES0_PROFESSIONAL |
-						     IEC958_AES0_PRO_FS |
-						     IEC958_AES0_PRO_EMPHASIS;
-		ucontrol->value.iec958.status[1] = IEC958_AES1_PRO_MODE;
-		break;
-	case ICE1712_SUBDEVICE_AUDIOPHILE:
-	case ICE1712_SUBDEVICE_EWX2496:
-		ucontrol->value.iec958.status[0] = 0xff;
-		ucontrol->value.iec958.status[1] = 0xff;
-		ucontrol->value.iec958.status[2] = 0xff;
-		ucontrol->value.iec958.status[3] = 0xff;
-		ucontrol->value.iec958.status[4] = 0xff;
-		break;
-	}
-	return 0;
-}
-
-static snd_kcontrol_new_t snd_ice1712_spdif_maskc __devinitdata =
-{
-	access:		SNDRV_CTL_ELEM_ACCESS_READ,
-	iface:		SNDRV_CTL_ELEM_IFACE_MIXER,
-	name:           SNDRV_CTL_NAME_IEC958("",PLAYBACK,CON_MASK),
-	info:		snd_ice1712_spdif_mask_info,
-	get:		snd_ice1712_spdif_maskc_get,
-};
-
-static snd_kcontrol_new_t snd_ice1712_spdif_maskp __devinitdata =
-{
-	access:		SNDRV_CTL_ELEM_ACCESS_READ,
-	iface:		SNDRV_CTL_ELEM_IFACE_MIXER,
-	name:           SNDRV_CTL_NAME_IEC958("",PLAYBACK,PRO_MASK),
-	info:		snd_ice1712_spdif_mask_info,
-	get:		snd_ice1712_spdif_maskp_get,
-};
-
-static int snd_ice1712_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_ice1712_spdif_stream_get(snd_kcontrol_t * kcontrol,
-					snd_ctl_elem_value_t * ucontrol)
-{
-	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
-
-	switch (ice->eeprom.subvendor) {
-	case ICE1712_SUBDEVICE_DELTA1010:
-	case ICE1712_SUBDEVICE_DELTADIO2496:
-	case ICE1712_SUBDEVICE_DELTA66:
-		snd_cs8403_decode_spdif_bits(&ucontrol->value.iec958, ice->cs8403_spdif_stream_bits);
-		break;
-	case ICE1712_SUBDEVICE_EWS88MT:
-	case ICE1712_SUBDEVICE_EWS88D:
-		snd_cs8404_decode_spdif_bits(&ucontrol->value.iec958, ice->cs8403_spdif_stream_bits);
-		break;
-	}
-	return 0;
-}
-
-static int snd_ice1712_spdif_stream_put(snd_kcontrol_t * kcontrol,
-					snd_ctl_elem_value_t * ucontrol)
-{
-	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
-	unsigned long flags;
-	unsigned int val;
-	int change;
-
-	switch (ice->eeprom.subvendor) {
-	case ICE1712_SUBDEVICE_DELTA1010:
-	case ICE1712_SUBDEVICE_DELTADIO2496:
-	case ICE1712_SUBDEVICE_DELTA66:
-		val = snd_cs8403_encode_spdif_bits(&ucontrol->value.iec958);
-		spin_lock_irqsave(&ice->reg_lock, flags);
-		change = ice->cs8403_spdif_stream_bits != val;
-		ice->cs8403_spdif_stream_bits = val;
-		if (change && ice->playback_pro_substream != NULL) {
-			spin_unlock_irqrestore(&ice->reg_lock, flags);
-			snd_ice1712_delta_cs8403_spdif_write(ice, val);
-		} else {
-			spin_unlock_irqrestore(&ice->reg_lock, flags);
-		}
-		break;
-	case ICE1712_SUBDEVICE_EWS88MT:
-	case ICE1712_SUBDEVICE_EWS88D:
-		val = snd_cs8404_encode_spdif_bits(&ucontrol->value.iec958);
-		spin_lock_irqsave(&ice->reg_lock, flags);
-		change = ice->cs8403_spdif_stream_bits != val;
-		ice->cs8403_spdif_stream_bits = val;
-		if (change && ice->playback_pro_substream != NULL) {
-			spin_unlock_irqrestore(&ice->reg_lock, flags);
-			snd_ice1712_ews_cs8404_spdif_write(ice, val);
-		} else {
-			spin_unlock_irqrestore(&ice->reg_lock, flags);
-		}
-		break;
-	default:
-		change = 0;
-	}
-	return change;
-}
-
-static snd_kcontrol_new_t snd_ice1712_spdif_stream __devinitdata =
-{
-	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_ice1712_spdif_stream_info,
-	get:		snd_ice1712_spdif_stream_get,
-	put:		snd_ice1712_spdif_stream_put
-};
-
-#define ICE1712_GPIO(xiface, xname, xindex, mask, invert, xaccess) \
-{ iface: xiface, name: xname, access: xaccess, info: snd_ice1712_gpio_info, \
-  get: snd_ice1712_gpio_get, put: snd_ice1712_gpio_put, \
-  private_value: mask | (invert << 24) }
-
-static int snd_ice1712_gpio_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_ice1712_gpio_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
-{
-	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
-	unsigned char mask = kcontrol->private_value & 0xff;
-	int invert = (kcontrol->private_value & (1<<24)) ? 1 : 0;
-	unsigned char saved[2];
-	
-	save_gpio_status(ice, saved);
-	ucontrol->value.integer.value[0] = (snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA) & mask ? 1 : 0) ^ invert;
-	restore_gpio_status(ice, saved);
-	return 0;
-}
-
-static int snd_ice1712_gpio_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
-{
-	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
-	unsigned char mask = kcontrol->private_value & 0xff;
-	int invert = (kcontrol->private_value & (1<<24)) ? mask : 0;
-	unsigned char saved[2];
-	int val, nval;
-
-	if (kcontrol->private_value & (1 << 31))
-		return -EPERM;
-	nval = (ucontrol->value.integer.value[0] ? mask : 0) ^ invert;
-	save_gpio_status(ice, saved);
-	val = snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA);
-	nval |= val & ~mask;
-	snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, nval);
-	restore_gpio_status(ice, saved);
-	return val != nval;
-}
-
-static snd_kcontrol_new_t snd_ice1712_delta1010_wordclock_select __devinitdata =
-ICE1712_GPIO(SNDRV_CTL_ELEM_IFACE_PCM, "Word Clock Sync", 0, ICE1712_DELTA_WORD_CLOCK_SELECT, 1, 0);
-static snd_kcontrol_new_t snd_ice1712_delta1010_wordclock_status __devinitdata =
-ICE1712_GPIO(SNDRV_CTL_ELEM_IFACE_PCM, "Word Clock Status", 0, ICE1712_DELTA_WORD_CLOCK_STATUS, 1, SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE);
-static snd_kcontrol_new_t snd_ice1712_deltadio2496_spdif_in_select __devinitdata =
-ICE1712_GPIO(SNDRV_CTL_ELEM_IFACE_PCM, "IEC958 Input Optical", 0, ICE1712_DELTA_SPDIF_INPUT_SELECT, 0, 0);
-static snd_kcontrol_new_t snd_ice1712_delta_spdif_in_status __devinitdata =
-ICE1712_GPIO(SNDRV_CTL_ELEM_IFACE_PCM, "Delta IEC958 Input Status", 0, ICE1712_DELTA_SPDIF_IN_STAT, 1, SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE);
-
-static int snd_ice1712_pro_spdif_master_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_ice1712_pro_spdif_master_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
-{
-	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
-	unsigned long flags;
-	
-	spin_lock_irqsave(&ice->reg_lock, flags);
-	ucontrol->value.integer.value[0] = inb(ICEMT(ice, RATE)) & ICE1712_SPDIF_MASTER ? 1 : 0;
-	spin_unlock_irqrestore(&ice->reg_lock, flags);
-	return 0;
-}
-
-static int snd_ice1712_pro_spdif_master_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
-{
-	unsigned long flags;
-	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
-	int nval, change;
-
-	nval = ucontrol->value.integer.value[0] ? ICE1712_SPDIF_MASTER : 0;
-	spin_lock_irqsave(&ice->reg_lock, flags);
-	nval |= inb(ICEMT(ice, RATE)) & ~ICE1712_SPDIF_MASTER;
-	change = inb(ICEMT(ice, RATE)) != nval;
-	outb(nval, ICEMT(ice, RATE));
-	spin_unlock_irqrestore(&ice->reg_lock, flags);
-
-	if (ice->cs8427) {
-		/* change CS8427 clock source too */
-		snd_ice1712_cs8427_set_input_clock(ice, ucontrol->value.integer.value[0]);
-	}
-
-	return change;
-}
-
-static snd_kcontrol_new_t snd_ice1712_pro_spdif_master __devinitdata = {
-	iface: SNDRV_CTL_ELEM_IFACE_MIXER,
-	name: "Multi Track IEC958 Master",
-	info: snd_ice1712_pro_spdif_master_info,
-	get: snd_ice1712_pro_spdif_master_get,
-	put: snd_ice1712_pro_spdif_master_put
-};
-
-/*
- * routing
- */
-static int snd_ice1712_pro_route_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
-{
-	static char *texts[] = {
-		"PCM Out", /* 0 */
-		"H/W In 0", "H/W In 1", "H/W In 2", "H/W In 3", /* 1-4 */
-		"H/W In 4", "H/W In 5", "H/W In 6", "H/W In 7", /* 5-8 */
-		"IEC958 In L", "IEC958 In R", /* 9-10 */
-		"Digital Mixer", /* 11 - optional */
-	};
-	
-	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
-	uinfo->count = 1;
-	uinfo->value.enumerated.items = kcontrol->id.index < 2 ? 12 : 11;
-	if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
-		uinfo->value.enumerated.item = uinfo->value.enumerated.items - 1;
-	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
-	return 0;
-}
-
-static int snd_ice1712_pro_route_analog_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t *ucontrol)
-{
-	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
-	int idx = kcontrol->id.index;
-	unsigned int val, cval;
-	val = inw(ICEMT(ice, ROUTE_PSDOUT03));
-	val >>= ((idx % 2) * 8) + ((idx / 2) * 2);
-	val &= 3;
-	cval = inl(ICEMT(ice, ROUTE_CAPTURE));
-	cval >>= ((idx / 2) * 8) + ((idx % 2) * 4);
-	if (val == 1 && idx < 2)
-		ucontrol->value.enumerated.item[0] = 11;
-	else if (val == 2)
-		ucontrol->value.enumerated.item[0] = (cval & 7) + 1;
-	else if (val == 3)
-		ucontrol->value.enumerated.item[0] = ((cval >> 3) & 1) + 9;
-	else
-		ucontrol->value.enumerated.item[0] = 0;
-	return 0;
-}
-
-static int snd_ice1712_pro_route_analog_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t *ucontrol)
-{
-	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
-	int change, shift;
-	int idx = kcontrol->id.index;
-	unsigned int val, old_val, nval;
-	
-	/* update PSDOUT */
-	if (ucontrol->value.enumerated.item[0] >= 11)
-		nval = idx < 2 ? 1 : 0; /* dig mixer (or pcm) */
-	else if (ucontrol->value.enumerated.item[0] >= 9)
-		nval = 3; /* spdif in */
-	else if (ucontrol->value.enumerated.item[0] >= 1)
-		nval = 2; /* analog in */
-	else
-		nval = 0; /* pcm */
-	shift = ((idx % 2) * 8) + ((idx / 2) * 2);
-	val = old_val = inw(ICEMT(ice, ROUTE_PSDOUT03));
-	val &= ~(0x03 << shift);
-	val |= nval << shift;
-	change = val != old_val;
-	if (change)
-		outw(val, ICEMT(ice, ROUTE_PSDOUT03));
-	if (nval < 2) /* dig mixer of pcm */
-		return change;
-
-	/* update CAPTURE */
-	val = old_val = inl(ICEMT(ice, ROUTE_CAPTURE));
-	shift = ((idx / 2) * 8) + ((idx % 2) * 4);
-	if (nval == 2) { /* analog in */
-		nval = ucontrol->value.enumerated.item[0] - 1;
-		val &= ~(0x07 << shift);
-		val |= nval << shift;
-	} else { /* spdif in */
-		nval = (ucontrol->value.enumerated.item[0] - 9) << 3;
-		val &= ~(0x08 << shift);
-		val |= nval << shift;
-	}
-	if (val != old_val) {
-		change = 1;
-		outl(val, ICEMT(ice, ROUTE_CAPTURE));
-	}
-	return change;
-}
-
-static int snd_ice1712_pro_route_spdif_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t *ucontrol)
-{
-	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
-	int idx = kcontrol->id.index;
-	unsigned int val, cval;
-	val = inw(ICEMT(ice, ROUTE_SPDOUT));
-	cval = (val >> (idx * 4 + 8)) & 0x0f;
-	val = (val >> (idx * 2)) & 0x03;
-	if (val == 1)
-		ucontrol->value.enumerated.item[0] = 11;
-	else if (val == 2)
-		ucontrol->value.enumerated.item[0] = (cval & 7) + 1;
-	else if (val == 3)
-		ucontrol->value.enumerated.item[0] = ((cval >> 3) & 1) + 9;
-	else
-		ucontrol->value.enumerated.item[0] = 0;
-	return 0;
-}
-
-static int snd_ice1712_pro_route_spdif_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t *ucontrol)
-{
-	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
-	int change, shift;
-	int idx = kcontrol->id.index;
-	unsigned int val, old_val, nval;
-	
-	/* update SPDOUT */
-	val = old_val = inw(ICEMT(ice, ROUTE_SPDOUT));
-	if (ucontrol->value.enumerated.item[0] >= 11)
-		nval = 1;
-	else if (ucontrol->value.enumerated.item[0] >= 9)
-		nval = 3;
-	else if (ucontrol->value.enumerated.item[0] >= 1)
-		nval = 2;
-	else
-		nval = 0;
-	shift = idx * 2;
-	val &= ~(0x03 << shift);
-	val |= nval << shift;
-	shift = idx * 4 + 8;
-	if (nval == 2) {
-		nval = ucontrol->value.enumerated.item[0] - 1;
-		val &= ~(0x07 << shift);
-		val |= nval << shift;
-	} else if (nval == 3) {
-		nval = (ucontrol->value.enumerated.item[0] - 9) << 3;
-		val &= ~(0x08 << shift);
-		val |= nval << shift;
-	}
-	change = val != old_val;
-	if (change)
-		outw(val, ICEMT(ice, ROUTE_SPDOUT));
-	return change;
-}
-
-static snd_kcontrol_new_t snd_ice1712_mixer_pro_analog_route __devinitdata = {
-	iface: SNDRV_CTL_ELEM_IFACE_MIXER,
-	name: "H/W Playback Route",
-	info: snd_ice1712_pro_route_info,
-	get: snd_ice1712_pro_route_analog_get,
-	put: snd_ice1712_pro_route_analog_put,
-};
-
-static snd_kcontrol_new_t snd_ice1712_mixer_pro_spdif_route __devinitdata = {
-	iface: SNDRV_CTL_ELEM_IFACE_MIXER,
-	name: "IEC958 Playback Route",
-	info: snd_ice1712_pro_route_info,
-	get: snd_ice1712_pro_route_spdif_get,
-	put: snd_ice1712_pro_route_spdif_put,
-};
-
-
-static int snd_ice1712_pro_volume_rate_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
-{
-	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
-	uinfo->count = 1;
-	uinfo->value.integer.min = 0;
-	uinfo->value.integer.max = 255;
-	return 0;
-}
-
-static int snd_ice1712_pro_volume_rate_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
-{
-	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
-	unsigned long flags;
-	
-	spin_lock_irqsave(&ice->reg_lock, flags);
-	ucontrol->value.integer.value[0] = inb(ICEMT(ice, MONITOR_RATE));
-	spin_unlock_irqrestore(&ice->reg_lock, flags);
-	return 0;
-}
-
-static int snd_ice1712_pro_volume_rate_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
-{
-	unsigned long flags;
-	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
-	int change;
-
-	spin_lock_irqsave(&ice->reg_lock, flags);
-	change = inb(ICEMT(ice, MONITOR_RATE)) != ucontrol->value.integer.value[0];
-	outb(ucontrol->value.integer.value[0], ICEMT(ice, MONITOR_RATE));
-	spin_unlock_irqrestore(&ice->reg_lock, flags);
-	return change;
-}
-
-static snd_kcontrol_new_t snd_ice1712_mixer_pro_volume_rate __devinitdata = {
-	iface: SNDRV_CTL_ELEM_IFACE_MIXER,
-	name: "Multi Track Volume Rate",
-	info: snd_ice1712_pro_volume_rate_info,
-	get: snd_ice1712_pro_volume_rate_get,
-	put: snd_ice1712_pro_volume_rate_put
-};
-
-static int snd_ice1712_pro_peak_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
-{
-	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
-	uinfo->count = 22;
-	uinfo->value.integer.min = 0;
-	uinfo->value.integer.max = 255;
-	return 0;
-}
-
-static int snd_ice1712_pro_peak_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
-{
-	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
-	unsigned long flags;
-	int idx;
-	
-	spin_lock_irqsave(&ice->reg_lock, flags);
-	for (idx = 0; idx < 22; idx++) {
-		outb(idx, ICEMT(ice, MONITOR_PEAKINDEX));
-		ucontrol->value.integer.value[idx] = inb(ICEMT(ice, MONITOR_PEAKDATA));
-	}
-	spin_unlock_irqrestore(&ice->reg_lock, flags);
-	return 0;
-}
-
-static snd_kcontrol_new_t snd_ice1712_mixer_pro_peak __devinitdata = {
-	iface: SNDRV_CTL_ELEM_IFACE_MIXER,
-	name: "Multi Track Peak",
-	access: SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
-	info: snd_ice1712_pro_peak_info,
-	get: snd_ice1712_pro_peak_get
-};
-
-/*
- * EWX 24/96
- */
-
-static int snd_ice1712_ewx_io_sense_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo){
-
-	static char *texts[4] = {
-		"+4dBu", "-10dBV",
-	};
-	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
-	uinfo->count = 1;
-	uinfo->value.enumerated.items = 2;
-	if (uinfo->value.enumerated.item >= 2)
-		uinfo->value.enumerated.item = 1;
-	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
-	return 0;
-}
-
-static int snd_ice1712_ewx_io_sense_get(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
-{
-	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
-	unsigned char mask = kcontrol->private_value & 0xff;
-	unsigned char saved[2];
-	
-	save_gpio_status(ice, saved);
-	ucontrol->value.enumerated.item[0] = snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA) & mask ? 1 : 0;
-	restore_gpio_status(ice, saved);
-	return 0;
-}
-
-static int snd_ice1712_ewx_io_sense_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
-{
-	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
-	unsigned char mask = kcontrol->private_value & 0xff;
-	unsigned char saved[2];
-	int val, nval;
-
-	if (kcontrol->private_value & (1 << 31))
-		return -EPERM;
-	nval = ucontrol->value.enumerated.item[0] ? mask : 0;
-	save_gpio_status(ice, saved);
-	val = snd_ice1712_read(ice, ICE1712_IREG_GPIO_DATA);
-	nval |= val & ~mask;
-	snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, nval);
-	restore_gpio_status(ice, saved);
-	return val != nval;
-}
-
-static snd_kcontrol_new_t snd_ice1712_ewx_input_sense __devinitdata = {
-	iface: SNDRV_CTL_ELEM_IFACE_MIXER,
-	name: "Input Sensitivity Switch",
-	info: snd_ice1712_ewx_io_sense_info,
-	get: snd_ice1712_ewx_io_sense_get,
-	put: snd_ice1712_ewx_io_sense_put,
-	private_value: ICE1712_EWX2496_AIN_SEL,
-};
-
-static snd_kcontrol_new_t snd_ice1712_ewx_output_sense __devinitdata = {
-	iface: SNDRV_CTL_ELEM_IFACE_MIXER,
-	name: "Output Sensitivity Switch",
-	info: snd_ice1712_ewx_io_sense_info,
-	get: snd_ice1712_ewx_io_sense_get,
-	put: snd_ice1712_ewx_io_sense_put,
-	private_value: ICE1712_EWX2496_AOUT_SEL,
-};
-
-
-/*
- * EWS88MT
- */
-/* analog output sensitivity;; address 0x48 bit 6 */
-static int snd_ice1712_ews88mt_output_sense_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
-{
-	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
-	unsigned char data;
-
-	snd_i2c_lock(ice->i2c);
-	if (snd_i2c_readbytes(ice->pcf8574[1], &data, 1) != 1) {
-		snd_i2c_unlock(ice->i2c);
-		return -EREMOTE;
-	}
-	snd_i2c_unlock(ice->i2c);
-	ucontrol->value.enumerated.item[0] = data & ICE1712_EWS88MT_OUTPUT_SENSE ? 1 : 0; /* high = -10dBV, low = +4dBu */
-	return 0;
-}
-
-/* analog output sensitivity;; address 0x48 bit 6 */
-static int snd_ice1712_ews88mt_output_sense_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
-{
-	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
-	unsigned char data, ndata;
-
-	snd_i2c_lock(ice->i2c);
-	if (snd_i2c_readbytes(ice->pcf8574[1], &data, 1) != 1) {
-		snd_i2c_unlock(ice->i2c);
-		return -EREMOTE;
-	}
-	ndata = (data & ~ICE1712_EWS88MT_OUTPUT_SENSE) | (ucontrol->value.enumerated.item[0] ? ICE1712_EWS88MT_OUTPUT_SENSE : 0);
-	if (ndata != data && snd_i2c_sendbytes(ice->pcf8574[1], &ndata, 1) != 1) {
-		snd_i2c_unlock(ice->i2c);
-		return -EREMOTE;
-	}
-	snd_i2c_unlock(ice->i2c);
-	return ndata != data;
-}
-
-/* analog input sensitivity; address 0x46 */
-static int snd_ice1712_ews88mt_input_sense_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
-{
-	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
-	int channel = kcontrol->id.index;
-	unsigned char data;
-
-	snd_assert(channel >= 0 && channel <= 7, return 0);
-	snd_i2c_lock(ice->i2c);
-	if (snd_i2c_readbytes(ice->pcf8574[0], &data, 1) != 1) {
-		snd_i2c_unlock(ice->i2c);
-		return -EREMOTE;
-	}
-	/* reversed; high = +4dBu, low = -10dBV */
-	ucontrol->value.enumerated.item[0] = data & (1 << channel) ? 0 : 1;
-	return 0;
-}
-
-/* analog output sensitivity; address 0x46 */
-static int snd_ice1712_ews88mt_input_sense_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
-{
-	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
-	int channel = kcontrol->id.index;
-	unsigned char data, ndata;
-
-	snd_assert(channel >= 0 && channel <= 7, return 0);
-	snd_i2c_lock(ice->i2c);
-	if (snd_i2c_readbytes(ice->pcf8574[0], &data, 1) != 1) {
-		snd_i2c_unlock(ice->i2c);
-		return -EREMOTE;
-	}
-	ndata = (data & ~(1 << channel)) | (ucontrol->value.enumerated.item[0] ? 0 : (1 << channel));
-	if (ndata != data && snd_i2c_sendbytes(ice->pcf8574[0], &ndata, 1) != 1) {
-		snd_i2c_unlock(ice->i2c);
-		return -EREMOTE;
-	}
-	snd_i2c_unlock(ice->i2c);
-	return ndata != data;
-}
-
-static snd_kcontrol_new_t snd_ice1712_ews88mt_input_sense __devinitdata = {
-	iface: SNDRV_CTL_ELEM_IFACE_MIXER,
-	name: "Input Sensitivity Switch",
-	info: snd_ice1712_ewx_io_sense_info,
-	get: snd_ice1712_ews88mt_input_sense_get,
-	put: snd_ice1712_ews88mt_input_sense_put,
-};
-
-static snd_kcontrol_new_t snd_ice1712_ews88mt_output_sense __devinitdata = {
-	iface: SNDRV_CTL_ELEM_IFACE_MIXER,
-	name: "Output Sensitivity Switch",
-	info: snd_ice1712_ewx_io_sense_info,
-	get: snd_ice1712_ews88mt_output_sense_get,
-	put: snd_ice1712_ews88mt_output_sense_put,
-};
-
-
-/*
- * EWS88D controls
- */
-
-static int snd_ice1712_ews88d_control_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_ice1712_ews88d_control_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
-{
-	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
-	int shift = kcontrol->private_value & 0xff;
-	int invert = (kcontrol->private_value >> 8) & 1;
-	unsigned char data[2];
-	
-	snd_i2c_lock(ice->i2c);
-	if (snd_i2c_readbytes(ice->pcf8575, data, 2) != 2) {
-		snd_i2c_unlock(ice->i2c);
-		return -EREMOTE;
-	}
-	snd_i2c_unlock(ice->i2c);
-	data[0] = (data[shift >> 3] >> (shift & 7)) & 0x01;
-	if (invert)
-		data[0] ^= 0x01;
-	ucontrol->value.integer.value[0] = data[0];
-	return 0;
-}
-
-static int snd_ice1712_ews88d_control_put(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol)
-{
-	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
-	int shift = kcontrol->private_value & 0xff;
-	int invert = (kcontrol->private_value >> 8) & 1;
-	unsigned char data[2], ndata[2];
-	int change;
-
-	snd_i2c_lock(ice->i2c);
-	if (snd_i2c_readbytes(ice->pcf8575, data, 2) != 2) {
-		snd_i2c_unlock(ice->i2c);
-		return -EREMOTE;
-	}
-	ndata[shift >> 3] = data[shift >> 3] & ~(1 << (shift & 7));
-	if (invert) {
-		if (! ucontrol->value.integer.value[0])
-			ndata[shift >> 3] |= (1 << (shift & 7));
-	} else {
-		if (ucontrol->value.integer.value[0])
-			ndata[shift >> 3] |= (1 << (shift & 7));
-	}
-	change = (data[shift >> 3] != ndata[shift >> 3]);
-	if (change && snd_i2c_sendbytes(ice->pcf8575, data, 2) != 2) {
-		snd_i2c_unlock(ice->i2c);
-		return -EREMOTE;
-	}
-	snd_i2c_unlock(ice->i2c);
-	return change;
-}
-
-#define EWS88D_CONTROL(xiface, xname, xshift, xinvert, xaccess) \
-{ iface: xiface,\
-  name: xname,\
-  access: xaccess,\
-  info: snd_ice1712_ews88d_control_info,\
-  get: snd_ice1712_ews88d_control_get,\
-  put: snd_ice1712_ews88d_control_put,\
-  private_value: xshift | (xinvert << 8),\
-}
-
-static snd_kcontrol_new_t snd_ice1712_ews88d_controls[] __devinitdata = {
-	EWS88D_CONTROL(SNDRV_CTL_ELEM_IFACE_MIXER, "IEC958 Input Optical", 0, 1, 0), /* inverted */
-	EWS88D_CONTROL(SNDRV_CTL_ELEM_IFACE_MIXER, "ADAT Output Optical", 1, 0, 0),
-	EWS88D_CONTROL(SNDRV_CTL_ELEM_IFACE_MIXER, "ADAT External Master Clock", 2, 0, 0),
-	EWS88D_CONTROL(SNDRV_CTL_ELEM_IFACE_MIXER, "Enable ADAT", 3, 0, 0),
-	EWS88D_CONTROL(SNDRV_CTL_ELEM_IFACE_MIXER, "ADAT Through", 4, 1, 0),
-};
-
-
-/*
- * DMX 6Fire controls
- */
-
-#if 0 // XXX not working yet
-static int snd_ice1712_6fire_read_pca(ice1712_t *ice)
-{
-	unsigned char byte;
-	snd_i2c_lock(ice->i2c);
-	byte = 0; /* read port */
-	snd_i2c_sendbytes(ice->pcf8575, &byte, 1);
-	if (snd_i2c_readbytes(ice->pcf8575, &byte, 1) != 1) {
-		snd_i2c_unlock(ice->i2c);
-		return -EREMOTE;
-	}
-	snd_i2c_unlock(ice->i2c);
-	return byte;
-}
-
-static int snd_ice1712_6fire_write_pca(ice1712_t *ice, unsigned char data)
-{
-	unsigned char bytes[2];
-	snd_i2c_lock(ice->i2c);
-	bytes[0] = 1; /* write port */
-	bytes[1] = data;
-	if (snd_i2c_sendbytes(ice->pcf8575, bytes, 2) != 2) {
-		snd_i2c_unlock(ice->i2c);
-		return -EREMOTE;
-	}
-	snd_i2c_unlock(ice->i2c);
-	return 0;
-}
-
-static int snd_ice1712_6fire_control_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_ice1712_6fire_control_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
-{
-	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
-	int shift = kcontrol->private_value & 0xff;
-	int invert = (kcontrol->private_value >> 8) & 1;
-	int data;
-	
-	if ((data = snd_ice1712_6fire_read_pca(ice)) < 0)
-		return data;
-	data = (data >> shift) & 1;
-	if (invert)
-		data ^= 1;
-	ucontrol->value.integer.value[0] = data;
-	return 0;
-}
-
-static int snd_ice1712_6fire_control_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
-{
-	ice1712_t *ice = snd_kcontrol_chip(kcontrol);
-	int shift = kcontrol->private_value & 0xff;
-	int invert = (kcontrol->private_value >> 8) & 1;
-	int data, ndata;
-	
-	if ((data = snd_ice1712_6fire_read_pca(ice)) < 0)
-		return data;
-	ndata = data & ~(1 << shift);
-	if (ucontrol->value.integer.value[0])
-		ndata |= (1 << shift);
-	if (invert)
-		ndata ^= (1 << shift);
-	if (data != ndata) {
-		snd_ice1712_6fire_write_pca(ice, (unsigned char)ndata);
-		return 1;
-	}
-	return 0;
-}
-
-#define DMX6FIRE_CONTROL(xiface, xname, xshift, xinvert, xaccess) \
-{ iface: xiface,\
-  name: xname,\
-  access: xaccess,\
-  info: snd_ice1712_6fire_control_info,\
-  get: snd_ice1712_6fire_control_get,\
-  put: snd_ice1712_6fire_control_put,\
-  private_value: xshift | (xinvert << 8),\
-}
-
-static snd_kcontrol_new_t snd_ice1712_6fire_led __devinitdata =
-DMX6FIRE_CONTROL(SNDRV_CTL_ELEM_IFACE_MIXER, "Breakbox LED", 6, 0, 0);
-
-#endif // XXX not working yet
-
-
-/*
- *
- */
-
-static unsigned char __devinit snd_ice1712_read_i2c(ice1712_t *ice,
-						 unsigned char dev,
-						 unsigned char addr)
-{
-	long t = 0x10000;
-
-	outb(addr, ICEREG(ice, I2C_BYTE_ADDR));
-	outb(dev & ~ICE1712_I2C_WRITE, ICEREG(ice, I2C_DEV_ADDR));
-	while (t-- > 0 && (inb(ICEREG(ice, I2C_CTRL)) & ICE1712_I2C_BUSY)) ;
-	return inb(ICEREG(ice, I2C_DATA));
-}
-
-static int __devinit snd_ice1712_read_eeprom(ice1712_t *ice)
-{
-	int dev = 0xa0;		/* EEPROM device address */
-	unsigned int idx;
-
-	if ((inb(ICEREG(ice, I2C_CTRL)) & ICE1712_I2C_EEPROM) == 0) {
-		snd_printk("ICE1712 has not detected EEPROM\n");
-		return -EIO;
-	}
-	ice->eeprom.subvendor = (snd_ice1712_read_i2c(ice, dev, 0x00) << 0) |
-				(snd_ice1712_read_i2c(ice, dev, 0x01) << 8) | 
-				(snd_ice1712_read_i2c(ice, dev, 0x02) << 16) | 
-				(snd_ice1712_read_i2c(ice, dev, 0x03) << 24);
-	ice->eeprom.size = snd_ice1712_read_i2c(ice, dev, 0x04);
-	if (ice->eeprom.size < 28) {
-		snd_printk("invalid EEPROM (size = %i)\n", ice->eeprom.size);
-		return -EIO;
-	}
-	ice->eeprom.version = snd_ice1712_read_i2c(ice, dev, 0x05);
-	if (ice->eeprom.version != 1) {
-		snd_printk("invalid EEPROM version %i\n", ice->eeprom.version);
-		return -EIO;
-	}
-	ice->eeprom.codec = snd_ice1712_read_i2c(ice, dev, 0x06);
-	ice->eeprom.aclink = snd_ice1712_read_i2c(ice, dev, 0x07);
-	ice->eeprom.i2sID = snd_ice1712_read_i2c(ice, dev, 0x08);
-	ice->eeprom.spdif = snd_ice1712_read_i2c(ice, dev, 0x09);
-	ice->eeprom.gpiomask = snd_ice1712_read_i2c(ice, dev, 0x0a);
-	ice->eeprom.gpiostate = snd_ice1712_read_i2c(ice, dev, 0x0b);
-	ice->eeprom.gpiodir = snd_ice1712_read_i2c(ice, dev, 0x0c);
-	ice->eeprom.ac97main = (snd_ice1712_read_i2c(ice, dev, 0x0d) << 0) |
-			       (snd_ice1712_read_i2c(ice, dev, 0x0e) << 8);
-	ice->eeprom.ac97pcm = (snd_ice1712_read_i2c(ice, dev, 0x0f) << 0) |
-			      (snd_ice1712_read_i2c(ice, dev, 0x10) << 8);
-	ice->eeprom.ac97rec = (snd_ice1712_read_i2c(ice, dev, 0x11) << 0) |
-			      (snd_ice1712_read_i2c(ice, dev, 0x12) << 8);
-	ice->eeprom.ac97recsrc = snd_ice1712_read_i2c(ice, dev, 0x13) << 0;
-	for (idx = 0; idx < 4; idx++) {
-		ice->eeprom.dacID[idx] = snd_ice1712_read_i2c(ice, dev, 0x14 + idx);
-		ice->eeprom.adcID[idx] = snd_ice1712_read_i2c(ice, dev, 0x18 + idx);
-	}
-	for (idx = 0x1c; idx < ice->eeprom.size && idx < 0x1c + sizeof(ice->eeprom.extra); idx++)
-		ice->eeprom.extra[idx - 0x1c] = snd_ice1712_read_i2c(ice, dev, idx);
-	return 0;
-}
-
-static void __devinit snd_ice1712_ak4524_init(ice1712_t *ice)
-{
-	static unsigned char inits[] = {
-		0x00, 0x07, /* 0: all power up */
-		0x01, 0x00, /* 1: ADC/DAC reset */
-		0x02, 0x60, /* 2: 24bit I2S */
-		0x03, 0x19, /* 3: deemphasis off */
-		0x01, 0x03, /* 1: ADC/DAC enable */
-		0x04, 0x00, /* 4: ADC left muted */
-		0x05, 0x00, /* 5: ADC right muted */
-		0x04, 0x80, /* 4: ADC IPGA gain 0dB */
-		0x05, 0x80, /* 5: ADC IPGA gain 0dB */
-		0x06, 0x00, /* 6: DAC left muted */
-		0x07, 0x00, /* 7: DAC right muted */
-		0xff, 0xff
-	};
-	int chip, idx;
-	unsigned char *ptr, reg, data;
-
-	for (chip = idx = 0; chip < ice->num_dacs/2; chip++) {
-		ptr = inits;
-		while (*ptr != 0xff) {
-			reg = *ptr++;
-			data = *ptr++;
-			if (ice->ak4528) {
-				if (reg > 5)
-					continue;
-				if (reg >= 4 && (data & 0x80))
-					continue;
-			}
-			if (reg == 0x03 && ice->ak4528)
-				data = 0x0d;	/* deemphasis off, turn LR highpass filters on */
-			snd_ice1712_ak4524_write(ice, chip, reg, data);
-		}
-	}
-}
-
-static void __devinit snd_ice1712_stdsp24_gpio_write(ice1712_t *ice, unsigned char byte)
-{
-	byte |= ICE1712_STDSP24_CLOCK_BIT;
-	udelay(100);
-	snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, byte);
-	byte &= ~ICE1712_STDSP24_CLOCK_BIT;
-	udelay(100);
-	snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, byte);
-	byte |= ICE1712_STDSP24_CLOCK_BIT;
-	udelay(100);
-	snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, byte);
-}
-
-static void __devinit snd_ice1712_stdsp24_darear(ice1712_t *ice, int activate)
-{
-	down(&ice->gpio_mutex);
-	ICE1712_STDSP24_0_DAREAR(ice->hoontech_boxbits, activate);
-	snd_ice1712_stdsp24_gpio_write(ice, ice->hoontech_boxbits[0]);
-	up(&ice->gpio_mutex);
-}
-
-static void __devinit snd_ice1712_stdsp24_mute(ice1712_t *ice, int activate)
-{
-	down(&ice->gpio_mutex);
-	ICE1712_STDSP24_3_MUTE(ice->hoontech_boxbits, activate);
-	snd_ice1712_stdsp24_gpio_write(ice, ice->hoontech_boxbits[3]);
-	up(&ice->gpio_mutex);
-}
-
-static void __devinit snd_ice1712_stdsp24_insel(ice1712_t *ice, int activate)
-{
-	down(&ice->gpio_mutex);
-	ICE1712_STDSP24_3_INSEL(ice->hoontech_boxbits, activate);
-	snd_ice1712_stdsp24_gpio_write(ice, ice->hoontech_boxbits[3]);
-	up(&ice->gpio_mutex);
-}
-
-static void __devinit snd_ice1712_stdsp24_box_channel(ice1712_t *ice, int box, int chn, int activate)
-{
-	down(&ice->gpio_mutex);
-
-	/* select box */
-	ICE1712_STDSP24_0_BOX(ice->hoontech_boxbits, box);
-	snd_ice1712_stdsp24_gpio_write(ice, ice->hoontech_boxbits[0]);
-
-	/* prepare for write */
-	if (chn == 3)
-		ICE1712_STDSP24_2_CHN4(ice->hoontech_boxbits, 0);
-	ICE1712_STDSP24_2_MIDI1(ice->hoontech_boxbits, activate);
-	snd_ice1712_stdsp24_gpio_write(ice, ice->hoontech_boxbits[2]);
-
-	ICE1712_STDSP24_1_CHN1(ice->hoontech_boxbits, 1);
-	ICE1712_STDSP24_1_CHN2(ice->hoontech_boxbits, 1);
-	ICE1712_STDSP24_1_CHN3(ice->hoontech_boxbits, 1);
-	ICE1712_STDSP24_2_CHN4(ice->hoontech_boxbits, 1);
-	snd_ice1712_stdsp24_gpio_write(ice, ice->hoontech_boxbits[1]);
-	snd_ice1712_stdsp24_gpio_write(ice, ice->hoontech_boxbits[2]);
-	udelay(100);
-	if (chn == 3) {
-		ICE1712_STDSP24_2_CHN4(ice->hoontech_boxbits, 0);
-		snd_ice1712_stdsp24_gpio_write(ice, ice->hoontech_boxbits[2]);
-	} else {
-		switch (chn) {
-		case 0:	ICE1712_STDSP24_1_CHN1(ice->hoontech_boxbits, 0); break;
-		case 1:	ICE1712_STDSP24_1_CHN2(ice->hoontech_boxbits, 0); break;
-		case 2:	ICE1712_STDSP24_1_CHN3(ice->hoontech_boxbits, 0); break;
-		}
-		snd_ice1712_stdsp24_gpio_write(ice, ice->hoontech_boxbits[1]);
-	}
-	udelay(100);
-	ICE1712_STDSP24_1_CHN1(ice->hoontech_boxbits, 1);
-	ICE1712_STDSP24_1_CHN2(ice->hoontech_boxbits, 1);
-	ICE1712_STDSP24_1_CHN3(ice->hoontech_boxbits, 1);
-	ICE1712_STDSP24_2_CHN4(ice->hoontech_boxbits, 1);
-	snd_ice1712_stdsp24_gpio_write(ice, ice->hoontech_boxbits[1]);
-	snd_ice1712_stdsp24_gpio_write(ice, ice->hoontech_boxbits[2]);
-	udelay(100);
-
-	ICE1712_STDSP24_2_MIDI1(ice->hoontech_boxbits, 0);
-	snd_ice1712_stdsp24_gpio_write(ice, ice->hoontech_boxbits[2]);
-
-	up(&ice->gpio_mutex);
-}
-
-static void __devinit snd_ice1712_stdsp24_box_midi(ice1712_t *ice, int box, int master, int slave)
-{
-	down(&ice->gpio_mutex);
-
-	/* select box */
-	ICE1712_STDSP24_0_BOX(ice->hoontech_boxbits, box);
-	snd_ice1712_stdsp24_gpio_write(ice, ice->hoontech_boxbits[0]);
-
-	ICE1712_STDSP24_2_MIDIIN(ice->hoontech_boxbits, 1);
-	ICE1712_STDSP24_2_MIDI1(ice->hoontech_boxbits, master);
-	snd_ice1712_stdsp24_gpio_write(ice, ice->hoontech_boxbits[2]);
-
-	udelay(100);
-	
-	ICE1712_STDSP24_2_MIDIIN(ice->hoontech_boxbits, 0);
-	snd_ice1712_stdsp24_gpio_write(ice, ice->hoontech_boxbits[2]);
-	
-	udelay(100);
-	
-	ICE1712_STDSP24_2_MIDIIN(ice->hoontech_boxbits, 1);
-	snd_ice1712_stdsp24_gpio_write(ice, ice->hoontech_boxbits[2]);
-
-	udelay(100);
-
-	/* MIDI2 is direct */
-	ICE1712_STDSP24_3_MIDI2(ice->hoontech_boxbits, slave);
-	snd_ice1712_stdsp24_gpio_write(ice, ice->hoontech_boxbits[3]);
-
-	up(&ice->gpio_mutex);
-}
-
-static void __devinit snd_ice1712_stdsp24_init(ice1712_t *ice)
-{
-	int box, chn;
-
-	ice->hoontech_boxbits[0] = 
-	ice->hoontech_boxbits[1] = 
-	ice->hoontech_boxbits[2] = 
-	ice->hoontech_boxbits[3] = 0;	/* should be already */
-
-	ICE1712_STDSP24_SET_ADDR(ice->hoontech_boxbits, 0);
-	ICE1712_STDSP24_CLOCK(ice->hoontech_boxbits, 0, 1);
-	ICE1712_STDSP24_0_BOX(ice->hoontech_boxbits, 0);
-	ICE1712_STDSP24_0_DAREAR(ice->hoontech_boxbits, 0);
-
-	ICE1712_STDSP24_SET_ADDR(ice->hoontech_boxbits, 1);
-	ICE1712_STDSP24_CLOCK(ice->hoontech_boxbits, 1, 1);
-	ICE1712_STDSP24_1_CHN1(ice->hoontech_boxbits, 1);
-	ICE1712_STDSP24_1_CHN2(ice->hoontech_boxbits, 1);
-	ICE1712_STDSP24_1_CHN3(ice->hoontech_boxbits, 1);
-	
-	ICE1712_STDSP24_SET_ADDR(ice->hoontech_boxbits, 2);
-	ICE1712_STDSP24_CLOCK(ice->hoontech_boxbits, 2, 1);
-	ICE1712_STDSP24_2_CHN4(ice->hoontech_boxbits, 1);
-	ICE1712_STDSP24_2_MIDIIN(ice->hoontech_boxbits, 1);
-	ICE1712_STDSP24_2_MIDI1(ice->hoontech_boxbits, 0);
-
-	ICE1712_STDSP24_SET_ADDR(ice->hoontech_boxbits, 3);
-	ICE1712_STDSP24_CLOCK(ice->hoontech_boxbits, 3, 1);
-	ICE1712_STDSP24_3_MIDI2(ice->hoontech_boxbits, 0);
-	ICE1712_STDSP24_3_MUTE(ice->hoontech_boxbits, 1);
-	ICE1712_STDSP24_3_INSEL(ice->hoontech_boxbits, 0);
-
-	/* let's go - activate only functions in first box */
-	ice->hoontech_config = 0;
-			    /* ICE1712_STDSP24_MUTE |
-			       ICE1712_STDSP24_INSEL |
-			       ICE1712_STDSP24_DAREAR; */
-	ice->hoontech_boxconfig[0] = ICE1712_STDSP24_BOX_CHN1 |
-				     ICE1712_STDSP24_BOX_CHN2 |
-				     ICE1712_STDSP24_BOX_CHN3 |
-				     ICE1712_STDSP24_BOX_CHN4 |
-				     ICE1712_STDSP24_BOX_MIDI1 |
-				     ICE1712_STDSP24_BOX_MIDI2;
-	ice->hoontech_boxconfig[1] = 
-	ice->hoontech_boxconfig[2] = 
-	ice->hoontech_boxconfig[3] = 0;
-	snd_ice1712_stdsp24_darear(ice, (ice->hoontech_config & ICE1712_STDSP24_DAREAR) ? 1 : 0);
-	snd_ice1712_stdsp24_mute(ice, (ice->hoontech_config & ICE1712_STDSP24_MUTE) ? 1 : 0);
-	snd_ice1712_stdsp24_insel(ice, (ice->hoontech_config & ICE1712_STDSP24_INSEL) ? 1 : 0);
-	for (box = 0; box < 4; box++) {
-		for (chn = 0; chn < 4; chn++)
-			snd_ice1712_stdsp24_box_channel(ice, box, chn, (ice->hoontech_boxconfig[box] & (1 << chn)) ? 1 : 0);
-		snd_ice1712_stdsp24_box_midi(ice, box,
-				(ice->hoontech_boxconfig[box] & ICE1712_STDSP24_BOX_MIDI1) ? 1 : 0,
-				(ice->hoontech_boxconfig[box] & ICE1712_STDSP24_BOX_MIDI2) ? 1 : 0);
-	}
-}
-
-static int __devinit snd_ice1712_chip_init(ice1712_t *ice)
-{
-	int err, has_i2c = 0;
-
-	outb(ICE1712_RESET | ICE1712_NATIVE, ICEREG(ice, CONTROL));
-	udelay(200);
-	outb(ICE1712_NATIVE, ICEREG(ice, CONTROL));
-	udelay(200);
-	pci_write_config_byte(ice->pci, 0x60, ice->eeprom.codec);
-	pci_write_config_byte(ice->pci, 0x61, ice->eeprom.aclink);
-	pci_write_config_byte(ice->pci, 0x62, ice->eeprom.i2sID);
-	pci_write_config_byte(ice->pci, 0x63, ice->eeprom.spdif);
-	if (ice->eeprom.subvendor != ICE1712_SUBDEVICE_STDSP24) {
-		ice->gpio_write_mask = ice->eeprom.gpiomask;
-		ice->gpio_direction = ice->eeprom.gpiodir;
-		snd_ice1712_write(ice, ICE1712_IREG_GPIO_WRITE_MASK, ice->eeprom.gpiomask);
-		snd_ice1712_write(ice, ICE1712_IREG_GPIO_DIRECTION, ice->eeprom.gpiodir);
-		snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, ice->eeprom.gpiostate);
-	} else {
-		ice->gpio_write_mask = 0xc0;
-		ice->gpio_direction = 0xff;
-		snd_ice1712_write(ice, ICE1712_IREG_GPIO_WRITE_MASK, 0xc0);
-		snd_ice1712_write(ice, ICE1712_IREG_GPIO_DIRECTION, 0xff);
-		snd_ice1712_write(ice, ICE1712_IREG_GPIO_DATA, ICE1712_STDSP24_CLOCK_BIT);
-	}
-	snd_ice1712_write(ice, ICE1712_IREG_PRO_POWERDOWN, 0);
-	if (!(ice->eeprom.codec & ICE1712_CFG_NO_CON_AC97)) {
-		outb(ICE1712_AC97_WARM, ICEREG(ice, AC97_CMD));
-		udelay(100);
-		outb(0, ICEREG(ice, AC97_CMD));
-		udelay(200);
-		snd_ice1712_write(ice, ICE1712_IREG_CONSUMER_POWERDOWN, 0);
-	}
-
-	/* determine I2C, DACs and ADCs */
-	switch (ice->eeprom.subvendor) {
-	case ICE1712_SUBDEVICE_AUDIOPHILE:
-		ice->ak4528 = 1;
-		/* follow thru */
-	case ICE1712_SUBDEVICE_EWX2496:
-		has_i2c = 1;
-		ice->num_adcs = ice->num_dacs = ice->num_total_dacs = 2;
-		break;	
-	case ICE1712_SUBDEVICE_DELTA44:
-	case ICE1712_SUBDEVICE_DELTA66:
-		ice->num_adcs = ice->num_dacs = ice->num_total_dacs = 4;
-		if (ice->omni)
-			ice->num_total_dacs = 8;
-		break;
-	case ICE1712_SUBDEVICE_EWS88MT:
-		has_i2c = 1;
-		/* follow thru */
-	case ICE1712_SUBDEVICE_DELTA1010:
-	case ICE1712_SUBDEVICE_STDSP24:
-		ice->num_adcs = ice->num_dacs = ice->num_total_dacs = 8;
-		break;
-	case ICE1712_SUBDEVICE_EWS88D:
-		has_i2c = 1;
-		break;
-	case ICE1712_SUBDEVICE_DMX6FIRE:
-		has_i2c = 1;
-		ice->num_adcs = ice->num_dacs = ice->num_total_dacs = 6;
-		break;
-	}
-
-	if (has_i2c) {
-		if ((err = snd_i2c_bus_create(ice->card, "ICE1712 GPIO 1", NULL, &ice->i2c)) < 0) {
-			snd_printk("unable to create I2C bus\n");
-			return err;
-		}
-		ice->i2c->private_data = ice;
-		switch (ice->eeprom.subvendor) {
-		case ICE1712_SUBDEVICE_AUDIOPHILE:
-			ice->i2c->ops = &ap_cs8427_i2c_ops;
-			break;
-		case ICE1712_SUBDEVICE_EWX2496:
-		case ICE1712_SUBDEVICE_EWS88MT:
-		case ICE1712_SUBDEVICE_EWS88D:
-		case ICE1712_SUBDEVICE_DMX6FIRE:
-			ice->i2c->hw_ops.bit = &snd_ice1712_ewx_cs8427_bit_ops;
-			break;
-		}
-		switch (ice->eeprom.subvendor) {
-		case ICE1712_SUBDEVICE_AUDIOPHILE:
-		case ICE1712_SUBDEVICE_EWX2496:
-			if ((err = snd_cs8427_create(ice->i2c, CS8427_BASE_ADDR, &ice->cs8427)) < 0) {
-				snd_printk("CS8427 initialization failed\n");
-				return err;
-			}
-			break;
-		case ICE1712_SUBDEVICE_DMX6FIRE:
-#if 0 // XXX not working yet
-			if ((err = snd_i2c_device_create(ice->i2c, "PCF9554", 0x40>>1, &ice->pcf8575)) < 0)
-				return err;
-			if ((err = snd_cs8427_create(ice->i2c, 0x11, &ice->cs8427)) < 0) {
-				snd_printk("CS8427 initialization failed\n");
-				return err;
-			}
-#endif // XXX not working yet
-			break;
-		case ICE1712_SUBDEVICE_EWS88MT:
-			if ((err = snd_i2c_device_create(ice->i2c, "CS8404", ICE1712_EWS88MT_CS8404_ADDR, &ice->cs8404)) < 0)
-				return err;
-			if ((err = snd_i2c_device_create(ice->i2c, "PCF8574 (1st)", ICE1712_EWS88MT_INPUT_ADDR, &ice->pcf8574[0])) < 0)
-				return err;
-			if ((err = snd_i2c_device_create(ice->i2c, "PCF8574 (2nd)", ICE1712_EWS88MT_OUTPUT_ADDR, &ice->pcf8574[1])) < 0)
-				return err;
-			break;
-		case ICE1712_SUBDEVICE_EWS88D:
-			if ((err = snd_i2c_device_create(ice->i2c, "PCF8575", ICE1712_EWS88D_PCF_ADDR, &ice->pcf8575)) < 0)
-				return err;
-			break;
-		}
-	}
-	/* second stage of initialization, analog parts and others */
-	switch (ice->eeprom.subvendor) {
-	case ICE1712_SUBDEVICE_DELTA66:
-	case ICE1712_SUBDEVICE_DELTA44:
-	case ICE1712_SUBDEVICE_AUDIOPHILE:
-	case ICE1712_SUBDEVICE_EWX2496:
-	case ICE1712_SUBDEVICE_EWS88MT:
-	case ICE1712_SUBDEVICE_DMX6FIRE:
-		snd_ice1712_ak4524_init(ice);
-		break;
-	case ICE1712_SUBDEVICE_STDSP24:
-		snd_ice1712_stdsp24_init(ice);
-		break;
-	}
-	switch (ice->eeprom.subvendor) {
-	case ICE1712_SUBDEVICE_DELTA1010:
-	case ICE1712_SUBDEVICE_DELTADIO2496:
-	case ICE1712_SUBDEVICE_DELTA66:
-		/* Set spdif defaults */
-		snd_ice1712_delta_cs8403_spdif_write(ice, ice->cs8403_spdif_bits);
-		break;
-	}
-	switch (ice->eeprom.subvendor) {
-	case ICE1712_SUBDEVICE_EWS88MT:
-	case ICE1712_SUBDEVICE_EWS88D:
-		/* Set spdif defaults */
-		snd_ice1712_ews_cs8404_spdif_write(ice, ice->cs8403_spdif_bits);
-		break;
-	}
-	return 0;
-}
-
-static int __init snd_ice1712_build_controls(ice1712_t *ice)
-{
-	unsigned int idx;
-	snd_kcontrol_t *kctl;
-	int err;
-
-	err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_eeprom, ice));
-	if (err < 0)
-		return err;
-	err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_pro_spdif_master, ice));
-	if (err < 0)
-		return err;
-	for (idx = 0; idx < ice->num_total_dacs; idx++) {
-		kctl = snd_ctl_new1(&snd_ice1712_mixer_pro_analog_route, ice);
-		if (kctl == NULL)
-			return -ENOMEM;
-		kctl->id.index = idx;
-		err = snd_ctl_add(ice->card, kctl);
-		if (err < 0)
-			return err;
-	}
-	for (idx = 0; idx < 2; idx++) {
-		kctl = snd_ctl_new1(&snd_ice1712_mixer_pro_spdif_route, ice);
-		if (kctl == NULL)
-			return -ENOMEM;
-		kctl->id.index = idx;
-		err = snd_ctl_add(ice->card, kctl);
-		if (err < 0)
-			return err;
-	}
-	err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_mixer_pro_volume_rate, ice));
-	if (err < 0)
-		return err;
-	err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_mixer_pro_peak, ice));
-	if (err < 0)
-		return err;
-	switch (ice->eeprom.subvendor) {
-	case ICE1712_SUBDEVICE_DELTA1010:
-		err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_delta1010_wordclock_select, ice));
-		if (err < 0)
-			return err;
-		err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_delta1010_wordclock_status, ice));
-		if (err < 0)
-			return err;
-		break;
-	case ICE1712_SUBDEVICE_DELTADIO2496:
-		err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_deltadio2496_spdif_in_select, ice));
-		if (err < 0)
-			return err;
-		break;
-	}
-	switch (ice->eeprom.subvendor) {
-	case ICE1712_SUBDEVICE_DELTA1010:
-	case ICE1712_SUBDEVICE_DELTADIO2496:
-	case ICE1712_SUBDEVICE_DELTA66:
-	case ICE1712_SUBDEVICE_AUDIOPHILE:
-	case ICE1712_SUBDEVICE_EWX2496:
-	case ICE1712_SUBDEVICE_EWS88MT:
-	case ICE1712_SUBDEVICE_EWS88D:
-		snd_assert(ice->pcm_pro != NULL, return -EIO);
-		err = snd_ctl_add(ice->card, kctl = snd_ctl_new1(&snd_ice1712_spdif_default, ice));
-		if (err < 0)
-			return err;
-		kctl->id.device = ice->pcm_pro->device;
-		err = snd_ctl_add(ice->card, kctl = snd_ctl_new1(&snd_ice1712_spdif_maskc, ice));
-		if (err < 0)
-			return err;
-		kctl->id.device = ice->pcm_pro->device;
-		err = snd_ctl_add(ice->card, kctl = snd_ctl_new1(&snd_ice1712_spdif_maskp, ice));
-		if (err < 0)
-			return err;
-		kctl->id.device = ice->pcm_pro->device;
-		err = snd_ctl_add(ice->card, kctl = snd_ctl_new1(&snd_ice1712_spdif_stream, ice));
-		if (err < 0)
-			return err;
-		kctl->id.device = ice->pcm_pro->device;
-		ice->spdif_stream_ctl = kctl;
-		break;
-	}
-	switch (ice->eeprom.subvendor) {
-	case ICE1712_SUBDEVICE_DELTA1010:
-	case ICE1712_SUBDEVICE_DELTADIO2496:
-	case ICE1712_SUBDEVICE_DELTA66:
-		err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_delta_spdif_in_status, ice));
-		if (err < 0)
-			return err;
-		break;
-	}
-	switch (ice->eeprom.subvendor) {
-	case ICE1712_SUBDEVICE_EWX2496:
-	case ICE1712_SUBDEVICE_AUDIOPHILE:
-	case ICE1712_SUBDEVICE_DELTA44:
-	case ICE1712_SUBDEVICE_DELTA66:
-	case ICE1712_SUBDEVICE_EWS88MT:
-	case ICE1712_SUBDEVICE_DMX6FIRE:
-		for (idx = 0; idx < ice->num_dacs; ++idx) {
-			snd_kcontrol_t ctl;
-			memset(&ctl, 0, sizeof(ctl));
-			strcpy(ctl.id.name, "DAC Volume");
-			ctl.id.index = idx;
-			ctl.id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
-			ctl.info = snd_ice1712_ak4524_volume_info;
-			ctl.access = SNDRV_CTL_ELEM_ACCESS_READ|SNDRV_CTL_ELEM_ACCESS_WRITE;
-			ctl.get = snd_ice1712_ak4524_volume_get;
-			ctl.put = snd_ice1712_ak4524_volume_put;
-			if (ice->ak4528)
-				ctl.private_value = (idx / 2) * 8 + (idx % 2) + 4; /* register 4 & 5 */
-			else
-				ctl.private_value = (idx / 2) * 8 + (idx % 2) + 6; /* register 6 & 7 */
-			ctl.private_data = ice;
-			if ((err = snd_ctl_add(ice->card, snd_ctl_new(&ctl))) < 0)
-				return err;
-		}
-		for (idx = 0; idx < ice->num_adcs && !ice->ak4528; ++idx) {
-			snd_kcontrol_t ctl;
-			memset(&ctl, 0, sizeof(ctl));
-			strcpy(ctl.id.name, "ADC Volume");
-			ctl.id.index = idx;
-			ctl.id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
-			ctl.info = snd_ice1712_ak4524_volume_info;
-			ctl.access = SNDRV_CTL_ELEM_ACCESS_READ|SNDRV_CTL_ELEM_ACCESS_WRITE;
-			ctl.get = snd_ice1712_ak4524_volume_get;
-			ctl.put = snd_ice1712_ak4524_volume_put;
-			ctl.private_value = (idx / 2) * 8 + (idx % 2) + 4; /* register 4 & 5 */
-			ctl.private_data = ice;
-			if ((err = snd_ctl_add(ice->card, snd_ctl_new(&ctl))) < 0)
-				return err;
-			memset(&ctl, 0, sizeof(ctl));
-			strcpy(ctl.id.name, "IPGA Analog Capture Volume");
-			ctl.id.index = idx;
-			ctl.id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
-			ctl.info = snd_ice1712_ak4524_ipga_gain_info;
-			ctl.access = SNDRV_CTL_ELEM_ACCESS_READ|SNDRV_CTL_ELEM_ACCESS_WRITE;
-			ctl.get = snd_ice1712_ak4524_ipga_gain_get;
-			ctl.put = snd_ice1712_ak4524_ipga_gain_put;
-			ctl.private_value = (idx / 2) * 8 + (idx % 2) + 4; /* register 4 & 5 */
-			ctl.private_data = ice;
-			if ((err = snd_ctl_add(ice->card, snd_ctl_new(&ctl))) < 0)
-				return err;
-		}
-		for (idx = 0; idx < ice->num_dacs/2; idx++) {
-			snd_kcontrol_t ctl;
-			memset(&ctl, 0, sizeof(ctl));
-			strcpy(ctl.id.name, "Deemphasis");
-			ctl.id.index = idx;
-			ctl.id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
-			ctl.info = snd_ice1712_ak4524_deemphasis_info;
-			ctl.access = SNDRV_CTL_ELEM_ACCESS_READ|SNDRV_CTL_ELEM_ACCESS_WRITE;
-			ctl.get = snd_ice1712_ak4524_deemphasis_get;
-			ctl.put = snd_ice1712_ak4524_deemphasis_put;
-			ctl.private_data = ice;
-			if ((err = snd_ctl_add(ice->card, snd_ctl_new(&ctl))) < 0)
-				return err;
-		}
-		break;
-	}
-	switch (ice->eeprom.subvendor) {
-	case ICE1712_SUBDEVICE_EWX2496:
-		err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_ewx_input_sense, ice));
-		if (err < 0)
-			return err;
-		err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_ewx_output_sense, ice));
-		if (err < 0)
-			return err;
-		break;
-	case ICE1712_SUBDEVICE_EWS88MT:
-		for (idx = 0; idx < 8; idx++) {
-			kctl = snd_ctl_new1(&snd_ice1712_ews88mt_input_sense, ice);
-			kctl->id.index = idx;
-			err = snd_ctl_add(ice->card, kctl);
-			if (err < 0)
-				return err;
-		}
-		err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_ews88mt_output_sense, ice));
-		if (err < 0)
-			return err;
-		break;
-	case ICE1712_SUBDEVICE_EWS88D:
-		for (idx = 0; idx < sizeof(snd_ice1712_ews88d_controls)/sizeof(snd_ice1712_ews88d_controls[0]); idx++) {
-			err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_ews88d_controls[idx], ice));
-			if (err < 0)
-				return err;
-		}
-		break;
-	case ICE1712_SUBDEVICE_DMX6FIRE:
-#if 0 // XXX not working yet
-		err = snd_ctl_add(ice->card, snd_ctl_new1(&snd_ice1712_6fire_led, ice));
-		if (err < 0)
-			return err;
-#endif
-		break;
-	}
-
-	return 0;
-}
-
-static int snd_ice1712_free(ice1712_t *ice)
-{
-	if (ice->res_port == NULL)
-		goto __hw_end;
-	/* mask all interrupts */
-	outb(0xc0, ICEMT(ice, IRQ));
-	outb(0xff, ICEREG(ice, IRQMASK));
-	/* --- */
-      __hw_end:
-	snd_ice1712_proc_done(ice);
-	if (ice->irq >= 0) {
-		synchronize_irq(ice->irq);
-		free_irq(ice->irq, (void *) ice);
-	}
-	if (ice->res_port) {
-		release_resource(ice->res_port);
-		kfree_nocheck(ice->res_port);
-	}
-	if (ice->res_ddma_port) {
-		release_resource(ice->res_ddma_port);
-		kfree_nocheck(ice->res_ddma_port);
-	}
-	if (ice->res_dmapath_port) {
-		release_resource(ice->res_dmapath_port);
-		kfree_nocheck(ice->res_dmapath_port);
-	}
-	if (ice->res_profi_port) {
-		release_resource(ice->res_profi_port);
-		kfree_nocheck(ice->res_profi_port);
-	}
-	snd_magic_kfree(ice);
-	return 0;
-}
-
-static int snd_ice1712_dev_free(snd_device_t *device)
-{
-	ice1712_t *ice = snd_magic_cast(ice1712_t, device->device_data, return -ENXIO);
-	return snd_ice1712_free(ice);
-}
-
-static int __devinit snd_ice1712_create(snd_card_t * card,
-				     struct pci_dev *pci,
-				     int omni,
-				     ice1712_t ** r_ice1712)
-{
-	ice1712_t *ice;
-	int err;
-	static snd_device_ops_t ops = {
-		dev_free:	snd_ice1712_dev_free,
-	};
-
-	*r_ice1712 = NULL;
-
-        /* enable PCI device */
-	if ((err = pci_enable_device(pci)) < 0)
-		return err;
-	/* check, if we can restrict PCI DMA transfers to 28 bits */
-	if (!pci_dma_supported(pci, 0x0fffffff)) {
-		snd_printk("architecture does not support 28bit PCI busmaster DMA\n");
-		return -ENXIO;
-	}
-	pci_set_dma_mask(pci, 0x0fffffff);
-
-	ice = snd_magic_kcalloc(ice1712_t, 0, GFP_KERNEL);
-	if (ice == NULL)
-		return -ENOMEM;
-	ice->omni = omni ? 1 : 0;
-	spin_lock_init(&ice->reg_lock);
-	init_MUTEX(&ice->gpio_mutex);
-	ice->cs8403_spdif_bits =
-	ice->cs8403_spdif_stream_bits = (0x01 |	/* consumer format */
-					 0x10 |	/* no emphasis */
-					 0x20);	/* PCM encoder/decoder */
-	ice->card = card;
-	ice->pci = pci;
-	ice->irq = -1;
-	ice->port = pci_resource_start(pci, 0);
-	ice->ddma_port = pci_resource_start(pci, 1);
-	ice->dmapath_port = pci_resource_start(pci, 2);
-	ice->profi_port = pci_resource_start(pci, 3);
-	pci_set_master(pci);
-	pci_write_config_word(ice->pci, 0x40, 0x807f);
-	pci_write_config_word(ice->pci, 0x42, 0x0006);
-	snd_ice1712_proc_init(ice);
-	synchronize_irq(pci->irq);
-
-	if ((ice->res_port = request_region(ice->port, 32, "ICE1712 - Controller")) == NULL) {
-		snd_ice1712_free(ice);
-		snd_printk("unable to grab ports 0x%lx-0x%lx\n", ice->port, ice->port + 32 - 1);
-		return -EIO;
-	}
-	if ((ice->res_ddma_port = request_region(ice->ddma_port, 16, "ICE1712 - DDMA")) == NULL) {
-		snd_ice1712_free(ice);
-		snd_printk("unable to grab ports 0x%lx-0x%lx\n", ice->ddma_port, ice->ddma_port + 16 - 1);
-		return -EIO;
-	}
-	if ((ice->res_dmapath_port = request_region(ice->dmapath_port, 16, "ICE1712 - DMA path")) == NULL) {
-		snd_ice1712_free(ice);
-		snd_printk("unable to grab ports 0x%lx-0x%lx\n", ice->dmapath_port, ice->dmapath_port + 16 - 1);
-		return -EIO;
-	}
-	if ((ice->res_profi_port = request_region(ice->profi_port, 64, "ICE1712 - Professional")) == NULL) {
-		snd_ice1712_free(ice);
-		snd_printk("unable to grab ports 0x%lx-0x%lx\n", ice->profi_port, ice->profi_port + 16 - 1);
-		return -EIO;
-	}
-	if (request_irq(pci->irq, snd_ice1712_interrupt, SA_INTERRUPT|SA_SHIRQ, "ICE1712", (void *) ice)) {
-		snd_ice1712_free(ice);
-		snd_printk("unable to grab IRQ %d\n", pci->irq);
-		return -EIO;
-	}
-	ice->irq = pci->irq;
-
-	if (snd_ice1712_read_eeprom(ice) < 0) {
-		snd_ice1712_free(ice);
-		return -EIO;
-	}
-	if (snd_ice1712_chip_init(ice) < 0) {
-		snd_ice1712_free(ice);
-		return -EIO;
-	}
-
-	/* unmask used interrupts */
-	outb((ice->eeprom.codec & ICE1712_CFG_2xMPU401) == 0 ? ICE1712_IRQ_MPU2 : 0 |
-	     (ice->eeprom.codec & ICE1712_CFG_NO_CON_AC97) ? ICE1712_IRQ_PBKDS | ICE1712_IRQ_CONCAP | ICE1712_IRQ_CONPBK : 0,
-	     ICEREG(ice, IRQMASK));
-	outb(0x00, ICEMT(ice, IRQ));
-
-	if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, ice, &ops)) < 0) {
-		snd_ice1712_free(ice);
-		return err;
-	}
-
-	*r_ice1712 = ice;
-	return 0;
-}
-
-static int __devinit snd_ice1712_probe(struct pci_dev *pci,
-				       const struct pci_device_id *id)
-{
-	static int dev;
-	snd_card_t *card;
-	ice1712_t *ice;
-	int pcm_dev = 0, err;
-
-	if (dev >= SNDRV_CARDS)
-		return -ENODEV;
-	if (!snd_enable[dev]) {
-		dev++;
-		return -ENOENT;
-	}
-
-	card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, 0);
-	if (card == NULL)
-		return -ENOMEM;
-
-	if ((err = snd_ice1712_create(card, pci, snd_omni[dev], &ice)) < 0) {
-		snd_card_free(card);
-		return err;
-	}
-
-	if ((err = snd_ice1712_pcm_profi(ice, pcm_dev++, NULL)) < 0) {
-		snd_card_free(card);
-		return err;
-	}
-	
-	if (!(ice->eeprom.codec & ICE1712_CFG_NO_CON_AC97))
-		if ((err = snd_ice1712_pcm(ice, pcm_dev++, NULL)) < 0) {
-			snd_card_free(card);
-			return err;
-		}
-
-	if ((err = snd_ice1712_ac97_mixer(ice)) < 0) {
-		snd_card_free(card);
-		return err;
-	}
-
-	if ((err = snd_ice1712_build_controls(ice)) < 0) {
-		snd_card_free(card);
-		return err;
-	}
-
-	if (!(ice->eeprom.codec & ICE1712_CFG_NO_CON_AC97))
-		if ((err = snd_ice1712_pcm_ds(ice, pcm_dev++, NULL)) < 0) {
-			snd_card_free(card);
-			return err;
-		}
-
-	strcpy(card->driver, "ICE1712");
-	strcpy(card->shortname, "ICEnsemble ICE1712");
-	
-	switch (ice->eeprom.subvendor) {
-	case ICE1712_SUBDEVICE_STDSP24:
-		strcpy(card->shortname, "Hoontech SoundTrack Audio DSP24");
-		break;
-	case ICE1712_SUBDEVICE_DELTA1010:
-		strcpy(card->shortname, "M Audio Delta 1010");
-		break;
-	case ICE1712_SUBDEVICE_DELTADIO2496:
-		strcpy(card->shortname, "M Audio Delta DiO 2496");
-		goto __no_mpu401;
-	case ICE1712_SUBDEVICE_DELTA66:
-		strcpy(card->shortname, "M Audio Delta 66");
-		goto __no_mpu401;
-	case ICE1712_SUBDEVICE_DELTA44:
-		strcpy(card->shortname, "M Audio Delta 44");
-		goto __no_mpu401;
-	case ICE1712_SUBDEVICE_AUDIOPHILE:
-		strcpy(card->shortname, "M Audio Audiophile 24/96");
-		break;
-	case ICE1712_SUBDEVICE_DELTA1010LT:
-		strcpy(card->shortname, "M Audio Delta 1010LT");
-		break;
-	case ICE1712_SUBDEVICE_EWX2496:
-		strcpy(card->shortname, "TerraTec EWX 24/96");
-		break;
-	case ICE1712_SUBDEVICE_EWS88MT:
-		strcpy(card->shortname, "TerraTec EWS 88MT");
-		break;
-	case ICE1712_SUBDEVICE_EWS88D:
-		strcpy(card->shortname, "TerraTec EWS 88D");
-		break;
-	case ICE1712_SUBDEVICE_DMX6FIRE:
-		strcpy(card->shortname, "TerraTec DMX 6Fire");
-		break;
-	}
-
-	if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_ICE1712,
-				       ICEREG(ice, MPU1_CTRL), 1,
-				       ice->irq, 0,
-				       &ice->rmidi[0])) < 0) {
-		snd_card_free(card);
-		return err;
-	}
-
-	if (ice->eeprom.codec & ICE1712_CFG_2xMPU401)
-		if ((err = snd_mpu401_uart_new(card, 1, MPU401_HW_ICE1712,
-					       ICEREG(ice, MPU2_CTRL), 1,
-					       ice->irq, 0,
-					       &ice->rmidi[1])) < 0) {
-			snd_card_free(card);
-			return err;
-		}
-
-      __no_mpu401:
-	sprintf(card->longname, "%s at 0x%lx, irq %i",
-		card->shortname, ice->port, ice->irq);
-
-	if ((err = snd_card_register(card)) < 0) {
-		snd_card_free(card);
-		return err;
-	}
-	pci_set_drvdata(pci, card);
-	dev++;
-	return 0;
-}
-
-static void __devexit snd_ice1712_remove(struct pci_dev *pci)
-{
-	snd_card_free(pci_get_drvdata(pci));
-	pci_set_drvdata(pci, NULL);
-}
-
-static struct pci_driver driver = {
-	name: "ICE1712",
-	id_table: snd_ice1712_ids,
-	probe: snd_ice1712_probe,
-	remove: __devexit_p(snd_ice1712_remove),
-};
-
-static int __init alsa_card_ice1712_init(void)
-{
-	int err;
-
-	if ((err = pci_module_init(&driver)) < 0) {
-#ifdef MODULE
-		printk(KERN_ERR "ICE1712 soundcard not found or device busy\n");
-#endif
-		return err;
-	}
-	return 0;
-}
-
-static void __exit alsa_card_ice1712_exit(void)
-{
-	pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_ice1712_init)
-module_exit(alsa_card_ice1712_exit)
-
-#ifndef MODULE
-
-/* format is: snd-ice1712=snd_enable,snd_index,snd_id */
-
-static int __init alsa_card_ice1712_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-ice1712=", alsa_card_ice1712_setup);
-
-#endif /* ifndef MODULE */
diff -Nru a/sound/pci/intel8x0.c b/sound/pci/intel8x0.c
--- a/sound/pci/intel8x0.c	Mon Oct  7 11:25:35 2002
+++ b/sound/pci/intel8x0.c	Mon Oct  7 11:25:35 2002
@@ -22,6 +22,7 @@
  *   You should have received a copy of the GNU General Public License
  *   along with this program; if not, write to the Free Software
  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+
  *
  */      
 
@@ -41,7 +42,7 @@
 #include <sound/initval.h>
 
 MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
-MODULE_DESCRIPTION("Intel 82801AA,82901AB,i810,i820,i830,i840,MX440; SiS 7012");
+MODULE_DESCRIPTION("Intel 82801AA,82901AB,i810,i820,i830,i840,i845,MX440; SiS 7012; Ali 5455");
 MODULE_LICENSE("GPL");
 MODULE_CLASSES("{sound}");
 MODULE_DEVICES("{{Intel,82801AA-ICH},"
@@ -53,7 +54,8 @@
 		"{SiS,SI7012},"
 		"{NVidia,NForce Audio},"
 		"{AMD,AMD768},"
-		"{AMD,AMD8111}}");
+		"{AMD,AMD8111},"
+	        "{ALI,M5455}}");
 
 #define SUPPORT_JOYSTICK 1
 #define SUPPORT_MIDI 1
@@ -126,50 +128,58 @@
 #define PCI_DEVICE_ID_NVIDIA_MCP_AUDIO	0x01b1
 #endif
 
-#define DEVICE_INTEL	0
-#define DEVICE_SIS	1
+enum { DEVICE_INTEL, DEVICE_INTEL_ICH4, DEVICE_SIS, DEVICE_ALI };
+
+#define ICHREG(x) ICH_REG_##x
+
+#define DEFINE_REGSET(name,base) \
+enum { \
+	ICH_REG_##name##_BDBAR	= base + 0x0,	/* dword - buffer descriptor list base address */ \
+	ICH_REG_##name##_CIV	= base + 0x04,	/* byte - current index value */ \
+	ICH_REG_##name##_LVI	= base + 0x05,	/* byte - last valid index */ \
+	ICH_REG_##name##_SR	= base + 0x06,	/* byte - status register */ \
+	ICH_REG_##name##_PICB	= base + 0x08,	/* word - position in current buffer */ \
+	ICH_REG_##name##_PIV	= base + 0x0a,	/* byte - prefetched index value */ \
+	ICH_REG_##name##_CR	= base + 0x0b,	/* byte - control register */ \
+};
+
+/* busmaster blocks */
+DEFINE_REGSET(OFF, 0);		/* offset */
+DEFINE_REGSET(PI, 0x00);	/* PCM in */
+DEFINE_REGSET(PO, 0x10);	/* PCM out */
+DEFINE_REGSET(MC, 0x20);	/* Mic in */
+
+/* ICH4 busmater blocks */
+DEFINE_REGSET(MC2, 0x40);	/* Mic in 2 */
+DEFINE_REGSET(PI2, 0x50);	/* PCM in 2 */
+DEFINE_REGSET(SP, 0x60);	/* SPDIF out */
+
+/* values for each busmaster block */
+
+/* LVI */
+#define ICH_REG_LVI_MASK		0x1f
+
+/* SR */
+#define ICH_FIFOE			0x10	/* FIFO error */
+#define ICH_BCIS			0x08	/* buffer completion interrupt status */
+#define ICH_LVBCI			0x04	/* last valid buffer completion interrupt */
+#define ICH_CELV			0x02	/* current equals last valid */
+#define ICH_DCH				0x01	/* DMA controller halted */
+
+/* PIV */
+#define ICH_REG_PIV_MASK		0x1f	/* mask */
+
+/* CR */
+#define ICH_IOCE			0x10	/* interrupt on completion enable */
+#define ICH_FEIE			0x08	/* fifo error interrupt enable */
+#define ICH_LVBIE			0x04	/* last valid buffer interrupt enable */
+#define ICH_RESETREGS			0x02	/* reset busmaster registers */
+#define ICH_STARTBM			0x01	/* start busmaster operation */
 
-#define ICHREG(ice, x) ((ice)->bmport + ICH_REG_##x)
-#define ICHREG2(ice, x) ((ice)->bmport + x)
 
-/* capture block */
-#define ICH_REG_PI_BDBAR		0x00	/* dword - buffer descriptor list base address */
-#define ICH_REG_PI_CIV			0x04	/* byte - current index value */
-#define ICH_REG_PI_LVI			0x05	/* byte - last valid index */
-#define   ICH_REG_LVI_MASK		0x1f
-#define ICH_REG_PI_SR			0x06	/* byte - status register */
-#define   ICH_FIFOE			0x10	/* FIFO error */
-#define   ICH_BCIS			0x08	/* buffer completion interrupt status */
-#define   ICH_LVBCI			0x04	/* last valid buffer completion interrupt */
-#define   ICH_CELV			0x02	/* current equals last valid */
-#define   ICH_DCH			0x01	/* DMA controller halted */
-#define ICH_REG_PI_PICB			0x08	/* word - position in current buffer */
-#define ICH_REG_PI_PIV			0x0a	/* byte - prefetched index value */
-#define   ICH_REG_PIV_MASK		0x1f	/* mask */
-#define ICH_REG_PI_CR			0x0b	/* byte - control register */
-#define   ICH_IOCE			0x10	/* interrupt on completion enable */
-#define   ICH_FEIE			0x08	/* fifo error interrupt enable */
-#define   ICH_LVBIE			0x04	/* last valid buffer interrupt enable */
-#define   ICH_RESETREGS			0x02	/* reset busmaster registers */
-#define   ICH_STARTBM			0x01	/* start busmaster operation */
-/* playback block */
-#define ICH_REG_PO_BDBAR		0x10	/* dword - buffer descriptor list base address */
-#define ICH_REG_PO_CIV			0x14	/* byte - current index value */
-#define ICH_REG_PO_LVI			0x15	/* byte - last valid command */
-#define ICH_REG_PO_SR			0x16	/* byte - status register */
-#define ICH_REG_PO_PICB			0x18	/* word - position in current buffer */
-#define ICH_REG_PO_PIV			0x1a	/* byte - prefetched index value */
-#define ICH_REG_PO_CR			0x1b	/* byte - control register */
-/* mic capture block */
-#define ICH_REG_MC_BDBAR		0x20	/* dword - buffer descriptor list base address */
-#define ICH_REG_MC_CIV			0x24	/* byte - current index value */
-#define ICH_REG_MC_LVI			0x25	/* byte - last valid command */
-#define ICH_REG_MC_SR			0x26	/* byte - status register */
-#define ICH_REG_MC_PICB			0x28	/* word - position in current buffer */
-#define ICH_REG_MC_PIV			0x2a	/* byte - prefetched index value */
-#define ICH_REG_MC_CR			0x2b	/* byte - control register */
 /* global block */
 #define ICH_REG_GLOB_CNT		0x2c	/* dword - global control */
+#define   ICH_PCM_20BIT		0x00400000	/* 20-bit samples (ICH4) */
 #define   ICH_PCM_246_MASK	0x00300000	/* 6 channels (not all chips) */
 #define   ICH_PCM_6		0x00200000	/* 6 channels (not all chips) */
 #define   ICH_PCM_4		0x00100000	/* 4 channels (not all chips) */
@@ -181,16 +191,24 @@
 #define   ICH_AC97COLD		0x00000002	/* AC'97 cold reset */
 #define   ICH_GIE		0x00000001	/* GPI interrupt enable */
 #define ICH_REG_GLOB_STA		0x30	/* dword - global status */
+#define   ICH_TRI		0x20000000	/* ICH4: tertiary (AC_SDIN2) resume interrupt */
+#define   ICH_TCR		0x10000000	/* ICH4: tertiary (AC_SDIN2) codec ready */
+#define   ICH_BCS		0x08000000	/* ICH4: bit clock stopped */
+#define   ICH_SPINT		0x04000000	/* ICH4: S/PDIF interrupt */
+#define   ICH_P2INT		0x02000000	/* ICH4: PCM2-In interrupt */
+#define   ICH_M2INT		0x01000000	/* ICH4: Mic2-In interrupt */
+#define   ICH_SAMPLE_CAP	0x00c00000	/* ICH4: sample capability bits (RO) */
+#define   ICH_MULTICHAN_CAP	0x00300000	/* ICH4: multi-channel capability bits (RO) */
 #define   ICH_MD3		0x00020000	/* modem power down semaphore */
 #define   ICH_AD3		0x00010000	/* audio power down semaphore */
 #define   ICH_RCS		0x00008000	/* read completion status */
 #define   ICH_BIT3		0x00004000	/* bit 3 slot 12 */
 #define   ICH_BIT2		0x00002000	/* bit 2 slot 12 */
 #define   ICH_BIT1		0x00001000	/* bit 1 slot 12 */
-#define   ICH_SRI		0x00000800	/* secondary resume interrupt */
-#define   ICH_PRI		0x00000400	/* primary resume interrupt */
-#define   ICH_SCR		0x00000200	/* secondary codec ready */
-#define   ICH_PCR		0x00000100	/* primary codec ready */
+#define   ICH_SRI		0x00000800	/* secondary (AC_SDIN1) resume interrupt */
+#define   ICH_PRI		0x00000400	/* primary (AC_SDIN0) resume interrupt */
+#define   ICH_SCR		0x00000200	/* secondary (AC_SDIN1) codec ready */
+#define   ICH_PCR		0x00000100	/* primary (AC_SDIN0) codec ready */
 #define   ICH_MCINT		0x00000080	/* MIC capture interrupt */
 #define   ICH_POINT		0x00000040	/* playback interrupt */
 #define   ICH_PIINT		0x00000020	/* capture interrupt */
@@ -198,16 +216,79 @@
 #define   ICH_MIINT		0x00000002	/* modem capture interrupt */
 #define   ICH_GSCI		0x00000001	/* GPI status change interrupt */
 #define ICH_REG_ACC_SEMA		0x34	/* byte - codec write semaphore */
-#define   ICH_CAS			0x01	/* codec access semaphore */
+#define   ICH_CAS		0x01		/* codec access semaphore */
+#define ICH_REG_SDM		0x80
+#define   ICH_DI2L_MASK		0x000000c0	/* PCM In 2, Mic In 2 data in line */
+#define   ICH_DI2L_SHIFT	6
+#define   ICH_DI1L_MASK		0x00000030	/* PCM In 1, Mic In 1 data in line */
+#define   ICH_DI1L_SHIFT	4
+#define   ICH_SE		0x00000008	/* steer enable */
+#define   ICH_LDI_MASK		0x00000003	/* last codec read data input */
 
 #define ICH_MAX_FRAGS		32		/* max hw frags */
 
+
+/*
+ * registers for Ali5455
+ */
+
+/* ALi 5455 busmaster blocks */
+DEFINE_REGSET(AL_PI, 0x40);	/* ALi PCM in */
+DEFINE_REGSET(AL_PO, 0x50);	/* Ali PCM out */
+DEFINE_REGSET(AL_MC, 0x60);	/* Ali Mic in */
+DEFINE_REGSET(AL_CDC_SPO, 0x70);	/* Ali Codec SPDIF out */
+DEFINE_REGSET(AL_CLR_SPI, 0xa0);	/* Ali Controller SPDIF in */
+DEFINE_REGSET(AL_CLR_SPO, 0xb0);	/* Ali Controller SPDIF out */
+
+enum {
+	ICH_REG_ALI_SCR = 0x00,		/* System Control Register */
+	ICH_REG_ALI_SSR = 0x04,		/* System Status Register  */
+	ICH_REG_ALI_DMACR = 0x08,	/* DMA Control Register    */
+	ICH_REG_ALI_FIFOCR1 = 0x0c,	/* FIFO Control Register 1  */
+	ICH_REG_ALI_INTERFACECR = 0x10,	/* Interface Control Register */
+	ICH_REG_ALI_INTERRUPTCR = 0x14,	/* Interrupt control Register */
+	ICH_REG_ALI_INTERRUPTSR = 0x18,	/* Interrupt  Status Register */
+	ICH_REG_ALI_FIFOCR2 = 0x1c,	/* FIFO Control Register 2   */
+	ICH_REG_ALI_CPR = 0x20,		/* Command Port Register     */
+	ICH_REG_ALI_SPR = 0x24,		/* Status Port Register      */
+	ICH_REG_ALI_FIFOCR3 = 0x2c,	/* FIFO Control Register 3  */
+	ICH_REG_ALI_TTSR = 0x30,	/* Transmit Tag Slot Register */
+	ICH_REG_ALI_RTSR = 0x34,	/* Receive Tag Slot  Register */
+	ICH_REG_ALI_CSPSR = 0x38,	/* Command/Status Port Status Register */
+	ICH_REG_ALI_CAS = 0x3c,		/* Codec Write Semaphore Register */
+	ICH_REG_ALI_SPDIFCSR = 0xf8,	/* spdif channel status register  */
+	ICH_REG_ALI_SPDIFICS = 0xfc	/* spdif interface control/status  */
+};
+
+/* interrupts for the whole chip by interrupt status register finish */
+ 
+#define ALI_INT_SPDIFOUT	(1<<23)	/* controller spdif out INTERRUPT */
+#define ALI_INT_SPDIFIN		(1<<22)
+#define ALI_INT_CODECSPDIFOUT	(1<<19)
+#define ALI_INT_MICIN		(1<<18)
+#define ALI_INT_PCMOUT		(1<<17)
+#define ALI_INT_PCMIN		(1<<16)
+#define ALI_INT_CPRAIS		(1<<7)
+#define ALI_INT_SPRAIS		(1<<5)
+#define ALI_INT_GPIO		(1<<1)
+#define ALI_INT_MASK		(ALI_INT_SPDIFOUT|ALI_INT_CODECSPDIFOUT|ALI_INT_MICIN|ALI_INT_PCMOUT|ALI_INT_PCMIN)
+
+#define ALI_PCM_CH4		0x100
+#define ALI_PCM_CH6		0x200
+#define ALI_PCM_MASK		(ALI_PCM_CH4 | ALI_PCM_CH6)
+
 /*
  *  
  */
 
+enum { ICHD_PCMIN, ICHD_PCMOUT, ICHD_MIC, ICHD_MIC2, ICHD_PCM2IN, ICHD_SPBAR, ICHD_LAST = ICHD_SPBAR };
+enum { ALID_PCMIN, ALID_PCMOUT, ALID_MIC, ALID_AC97SPDIFOUT, ALID_SPDIFIN, ALID_SPDIFOUT, ALID_LAST = ALID_SPDIFOUT };
+
+#define get_ichdev(substream) (ichdev_t *)(substream->runtime->private_data)
+
 typedef struct {
-	unsigned long reg_offset;
+	unsigned int ichd;			/* ich device number */
+	unsigned long reg_offset;		/* offset to bmaddr */
 	u32 *bdbar;				/* CPU address (32bit) */
 	unsigned int bdbar_addr;		/* PCI bus address (32bit) */
 	snd_pcm_substream_t *substream;
@@ -221,11 +302,20 @@
         int lvi_frag;
 	int ack;
 	int ack_reload;
+	unsigned int ack_bit;
+	unsigned int roff_sr;
+	unsigned int roff_picb;
+	unsigned int int_sta_mask;		/* interrupt status mask */
+	unsigned int ali_slot;			/* ALI DMA slot */
+	ac97_t *ac97;
+	unsigned short ac97_rate_regs[3];
+	int ac97_rates_idx;
 #ifdef CONFIG_PM
 	unsigned char civ_saved;
 	unsigned char piv_saved;
 	unsigned short picb_saved;
 #endif
+	snd_info_entry_t *proc_entry;
 } ichdev_t;
 
 typedef struct _snd_intel8x0 intel8x0_t;
@@ -236,32 +326,36 @@
 	char ac97_name[32];
 	char ctrl_name[32];
 
-	unsigned long dma_playback_size;
-	unsigned long dma_capture_size;
-	unsigned long dma_mic_size;
-
 	int irq;
 
-	unsigned long port;
-	struct resource *res_port;
-	unsigned long bmport;
-	struct resource *res_bmport;
+	unsigned int mmio;
+	unsigned long addr;
+	unsigned long remap_addr;
+	struct resource *res;
+	unsigned int bm_mmio;
+	unsigned long bmaddr;
+	unsigned long remap_bmaddr;
+	struct resource *res_bm;
 
 	struct pci_dev *pci;
 	snd_card_t *card;
 
 	snd_pcm_t *pcm;
 	snd_pcm_t *pcm_mic;
-	ichdev_t playback;
-	ichdev_t capture;
-	ichdev_t capture_mic;
+	snd_pcm_t *pcm_mic2;
+	snd_pcm_t *pcm2;
+	snd_pcm_t *pcm_spdif;
+	snd_pcm_t *pcm_ac97spdif;
+	ichdev_t ichd[6];
 
 	int multi4: 1,
-	    multi6: 1;
-	int in_ac97_init: 1;
+	    multi6: 1,
+	    smp20bit: 1;
+	int in_ac97_init: 1,
+	    in_sdin_init: 1;
 
-	ac97_t *ac97;
-	ac97_t *ac97sec;
+	ac97_t *ac97[3];
+	unsigned int ac97_sdin[3];
 
 	snd_rawmidi_t *rmidi;
 
@@ -269,16 +363,12 @@
 	spinlock_t ac97_lock;
 	snd_info_entry_t *proc_entry;
 	
+	u32 bdbars_count;
 	u32 *bdbars;
 	dma_addr_t bdbars_addr;
+	u32 int_sta_reg;		/* interrupt status register */
+	u32 int_sta_mask;		/* interrupt status mask */
 	
-	unsigned int reg_pi_sr;
-	unsigned int reg_pi_picb;
-	unsigned int reg_po_sr;
-	unsigned int reg_po_picb;
-	unsigned int reg_mc_sr;
-	unsigned int reg_mc_picb;	
-
 #ifdef CONFIG_PM
 	int in_suspend;
 #endif
@@ -289,32 +379,131 @@
 	{ 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, 0x24c5, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL_ICH4 }, /* 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 */
 	{ 0x1022, 0x746d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL },	/* AMD8111 */
 	{ 0x1022, 0x7445, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_INTEL },	/* AMD768 */
+	{ 0x10b9, 0x5455, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_ALI },   /* Ali5455 */
 	{ 0, }
 };
 
 MODULE_DEVICE_TABLE(pci, snd_intel8x0_ids);
 
 /*
+ *  Lowlevel I/O - busmaster
+ */
+
+static u8 igetbyte(intel8x0_t *chip, u32 offset)
+{
+	if (chip->bm_mmio)
+		return readb(chip->remap_bmaddr + offset);
+	else
+		return inb(chip->bmaddr + offset);
+}
+
+static u16 igetword(intel8x0_t *chip, u32 offset)
+{
+	if (chip->bm_mmio)
+		return readw(chip->remap_bmaddr + offset);
+	else
+		return inw(chip->bmaddr + offset);
+}
+
+static u32 igetdword(intel8x0_t *chip, u32 offset)
+{
+	if (chip->bm_mmio)
+		return readl(chip->remap_bmaddr + offset);
+	else
+		return inl(chip->bmaddr + offset);
+}
+
+static void iputbyte(intel8x0_t *chip, u32 offset, u8 val)
+{
+	if (chip->bm_mmio)
+		writeb(val, chip->remap_bmaddr + offset);
+	else
+		outb(val, chip->bmaddr + offset);
+}
+
+static void iputword(intel8x0_t *chip, u32 offset, u16 val)
+{
+	if (chip->bm_mmio)
+		writew(val, chip->remap_bmaddr + offset);
+	else
+		outw(val, chip->bmaddr + offset);
+}
+
+static void iputdword(intel8x0_t *chip, u32 offset, u32 val)
+{
+	if (chip->bm_mmio)
+		writel(val, chip->remap_bmaddr + offset);
+	else
+		outl(val, chip->bmaddr + offset);
+}
+
+/*
+ *  Lowlevel I/O - AC'97 registers
+ */
+
+static u16 iagetword(intel8x0_t *chip, u32 offset)
+{
+	if (chip->mmio)
+		return readw(chip->remap_addr + offset);
+	else
+		return inw(chip->addr + offset);
+}
+
+static void iaputword(intel8x0_t *chip, u32 offset, u16 val)
+{
+	if (chip->mmio)
+		writew(val, chip->remap_addr + offset);
+	else
+		outw(val, chip->addr + offset);
+}
+
+/*
  *  Basic I/O
  */
+
+/*
+ * access to AC97 codec via normal i/o (for ICH and SIS7012)
+ */
 static int snd_intel8x0_codec_semaphore(intel8x0_t *chip, unsigned int codec)
 {
 	int time;
+	
+	if (codec > 2)
+		return -EIO;
+	if (chip->in_sdin_init) {
+		/* we don't know the ready bit assignment at the moment */
+		/* so we check any */
+		codec = ICH_PCR | ICH_SCR | ICH_TCR;
+	} else {
+		if (chip->device_type == DEVICE_INTEL_ICH4) {
+			switch (chip->ac97_sdin[codec]) {
+			case 0: codec = ICH_PCR; break;
+			case 1: codec = ICH_SCR; break;
+			case 2: codec = ICH_TCR; break;
+			}
+		} else {
+			switch (codec) {
+			case 0: codec = ICH_PCR; break;
+			case 1: codec = ICH_SCR; break;
+			case 2: codec = ICH_TCR; break;
+			}
+		}
+	}
 
 	/* codec ready ? */
-	if ((inl(ICHREG(chip, GLOB_STA)) & (codec ? ICH_SCR : ICH_PCR)) == 0)
+	if ((igetdword(chip, ICHREG(GLOB_STA)) & codec) == 0)
 		return -EIO;
 
 	/* Anyone holding a semaphore for 1 msec should be shot... */
 	time = 100;
       	do {
-      		if (!(inb(ICHREG(chip, ACC_SEMA)) & ICH_CAS))
+      		if (!(igetbyte(chip, ICHREG(ACC_SEMA)) & ICH_CAS))
       			return 0;
 		udelay(10);
 	} while (time--);
@@ -323,8 +512,8 @@
 	 * reset the semaphore. So even if you don't get the semaphore, still
 	 * continue the access. We don't need the semaphore anyway. */
 	snd_printk("codec_semaphore: semaphore is not ready [0x%x][0x%x]\n",
-			inb(ICHREG(chip, ACC_SEMA)), inl(ICHREG(chip, GLOB_STA)));
-	inw(chip->port);	/* clear semaphore flag */
+			igetbyte(chip, ICHREG(ACC_SEMA)), igetdword(chip, ICHREG(GLOB_STA)));
+	iagetword(chip, 0);	/* clear semaphore flag */
 	/* I don't care about the semaphore */
 	return -EBUSY;
 }
@@ -340,7 +529,7 @@
 		if (! chip->in_ac97_init)
 			snd_printk("codec_write %d: semaphore is not ready for register 0x%x\n", ac97->num, reg);
 	}
-	outw(val, chip->port + reg + ac97->num * 0x80);
+	iaputword(chip, reg + ac97->num * 0x80, val);
 	spin_unlock(&chip->ac97_lock);
 }
 
@@ -357,10 +546,10 @@
 			snd_printk("codec_read %d: semaphore is not ready for register 0x%x\n", ac97->num, reg);
 		res = 0xffff;
 	} else {
-		res = inw(chip->port + reg + ac97->num * 0x80);
-		if ((tmp = inl(ICHREG(chip, GLOB_STA))) & ICH_RCS) {
+		res = iagetword(chip, reg + ac97->num * 0x80);
+		if ((tmp = igetdword(chip, ICHREG(GLOB_STA))) & ICH_RCS) {
 			/* reset RCS and preserve other R/WC bits */
-			outl(tmp & ~(ICH_SRI|ICH_PRI|ICH_GSCI), ICHREG(chip, GLOB_STA));
+			iputdword(chip, ICHREG(GLOB_STA), tmp & ~(ICH_SRI|ICH_PRI|ICH_TRI|ICH_GSCI));
 			if (! chip->in_ac97_init)
 				snd_printk("codec_read %d: read timeout for register 0x%x\n", ac97->num, reg);
 			res = 0xffff;
@@ -370,46 +559,85 @@
 	return res;
 }
 
-static int snd_intel8x0_trigger(intel8x0_t *chip, ichdev_t *ichdev, int cmd)
+/*
+ * access to AC97 for Ali5455
+ */
+static int snd_intel8x0_ali_codec_ready(intel8x0_t *chip, int mask)
 {
-	unsigned char val = 0;
-	unsigned long port = chip->bmport + ichdev->reg_offset;
-	
-	switch (cmd) {
-	case SNDRV_PCM_TRIGGER_START:
-	case SNDRV_PCM_TRIGGER_RESUME:
-		val = ICH_IOCE | ICH_STARTBM;
-		break;
-	case SNDRV_PCM_TRIGGER_STOP:
-	case SNDRV_PCM_TRIGGER_SUSPEND:
-		val = 0;
-		break;
-	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
-		val = ICH_IOCE;
-		break;
-	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
-		val = ICH_IOCE | ICH_STARTBM;
-		break;
-	default:
-		return -EINVAL;
+	int count = 0;
+	for (count = 0; count < 0x7f; count++) {
+		int val = igetbyte(chip, ICHREG(ALI_CSPSR));
+		if (val & mask)
+			return 0;
 	}
-	outb(val, port + ICH_REG_PI_CR);
-	if (cmd == SNDRV_PCM_TRIGGER_STOP) {
-		/* reset whole DMA things */
-		while (!(inb(port + chip->reg_pi_sr) & ICH_DCH)) ;
-		outb(ICH_RESETREGS, port + ICH_REG_PI_CR);
+	snd_printd(KERN_WARNING "intel8x0: AC97 codec ready timeout.\n");
+	return -EBUSY;
+}
+
+static int snd_intel8x0_ali_codec_semaphore(intel8x0_t *chip)
+{
+	int time = 100;
+	do {
+		if (igetdword(chip, ICHREG(ALI_CAS)) & 0x80000000)
+			return snd_intel8x0_ali_codec_ready(chip, 0x08);
+		udelay(1);
+	} while (time--);
+	snd_printk(KERN_WARNING "intel8x0: AC97 codec semaphore timeout.\n");
+	return -EBUSY;
+}
+
+static unsigned short snd_intel8x0_ali_codec_read(ac97_t *ac97, unsigned short reg)
+{
+	intel8x0_t *chip = snd_magic_cast(intel8x0_t, ac97->private_data, return ~0);
+	unsigned short data, reg2;
+
+	spin_lock(&chip->ac97_lock);
+	if (snd_intel8x0_ali_codec_semaphore(chip))
+		goto __err;
+	reg |= 0x0080;
+	iputword(chip, ICHREG(ALI_CPR) + 2, reg | 0x0080);
+	if (snd_intel8x0_ali_codec_ready(chip, 0x02))
+		goto __err;
+	data = igetword(chip, ICHREG(ALI_SPR));
+	reg2 = igetword(chip, ICHREG(ALI_SPR) + 2);
+	if (reg != reg2) {
+		snd_printd(KERN_WARNING "intel8x0: AC97 read not completed?\n");
+		goto __err;
 	}
-	return 0;
+	spin_unlock(&chip->ac97_lock);
+	return data;
+ __err:
+	spin_unlock(&chip->ac97_lock);
+	return 0xffff;
+}
+
+static void snd_intel8x0_ali_codec_write(ac97_t *ac97, unsigned short reg, unsigned short val)
+{
+	intel8x0_t *chip = snd_magic_cast(intel8x0_t, ac97->private_data, return);
+
+	spin_lock(&chip->ac97_lock);
+	if (snd_intel8x0_ali_codec_semaphore(chip)) {
+		spin_unlock(&chip->ac97_lock);
+		return;
+	}
+	iputword(chip, ICHREG(ALI_CPR), val);
+	iputbyte(chip, ICHREG(ALI_CPR) + 2, reg);
+	snd_intel8x0_ali_codec_ready(chip, 0x01);
+	spin_unlock(&chip->ac97_lock);
 }
 
+
+/*
+ * DMA I/O
+ */
 static void snd_intel8x0_setup_periods(intel8x0_t *chip, ichdev_t *ichdev) 
 {
 	int idx;
 	u32 *bdbar = ichdev->bdbar;
-	unsigned long port = chip->bmport + ichdev->reg_offset;
+	unsigned long port = ichdev->reg_offset;
 	int shiftlen = (chip->device_type == DEVICE_SIS) ? 0 : 1;
 
-	outl(ichdev->bdbar_addr, port + ICH_REG_PI_BDBAR);
+	iputdword(chip, port + ICH_REG_OFF_BDBAR, ichdev->bdbar_addr);
 	if (ichdev->size == ichdev->fragsize) {
 		ichdev->ack_reload = ichdev->ack = 2;
 		ichdev->fragsize1 = ichdev->fragsize >> 1;
@@ -433,7 +661,7 @@
 		}
 		ichdev->frags = ichdev->size / ichdev->fragsize;
 	}
-	outb(ichdev->lvi = ICH_REG_LVI_MASK, port + ICH_REG_PI_LVI);
+	iputbyte(chip, port + ICH_REG_OFF_LVI, ichdev->lvi = ICH_REG_LVI_MASK);
 	ichdev->lvi_frag = ICH_REG_LVI_MASK % ichdev->frags;
 	ichdev->position = 0;
 #if 0
@@ -441,7 +669,7 @@
 			ichdev->lvi_frag, ichdev->frags, ichdev->fragsize, ichdev->fragsize1);
 #endif
 	/* clear interrupts */
-	outb(ICH_FIFOE | ICH_BCIS | ICH_LVBCI, port + chip->reg_pi_sr);
+	iputbyte(chip, port + ichdev->roff_sr, ICH_FIFOE | ICH_BCIS | ICH_LVBCI);
 }
 
 /*
@@ -450,7 +678,7 @@
 
 static inline void snd_intel8x0_update(intel8x0_t *chip, ichdev_t *ichdev)
 {
-	unsigned long port = chip->bmport + ichdev->reg_offset;
+	unsigned long port = ichdev->reg_offset;
 	int ack = 0;
 
 	spin_lock(&chip->reg_lock);
@@ -458,80 +686,116 @@
 	ichdev->position %= ichdev->size;
 	ichdev->lvi++;
 	ichdev->lvi &= ICH_REG_LVI_MASK;
-	outb(ichdev->lvi, port + ICH_REG_PI_LVI);
+	iputbyte(chip, port + ICH_REG_OFF_LVI, ichdev->lvi);
 	ichdev->lvi_frag++;
 	ichdev->lvi_frag %= ichdev->frags;
-	ichdev->bdbar[ichdev->lvi * 2] = ichdev->physbuf + ichdev->lvi_frag * ichdev->fragsize1;
-	// printk("new: bdbar[%i] = 0x%x [0x%x], prefetch = %i, all = 0x%x, 0x%x\n", ichdev->lvi * 2, ichdev->bdbar[ichdev->lvi * 2], ichdev->bdbar[ichdev->lvi * 2 + 1], inb(ICH_REG_PI_PIV + port), inl(port + 4), inb(port + ICH_REG_PI_CR));
+	ichdev->bdbar[ichdev->lvi * 2] = cpu_to_le32(ichdev->physbuf + ichdev->lvi_frag * ichdev->fragsize1);
+	// printk("new: bdbar[%i] = 0x%x [0x%x], prefetch = %i, all = 0x%x, 0x%x\n", ichdev->lvi * 2, ichdev->bdbar[ichdev->lvi * 2], ichdev->bdbar[ichdev->lvi * 2 + 1], inb(ICH_REG_OFF_PIV + port), inl(port + 4), inb(port + ICH_REG_OFF_CR));
 	if ((ack = (--ichdev->ack == 0)) != 0)
 		ichdev->ack = ichdev->ack_reload;
 	spin_unlock(&chip->reg_lock);
 	if (ack && ichdev->substream)
 		snd_pcm_period_elapsed(ichdev->substream);
-	outb(ICH_FIFOE | ICH_BCIS | ICH_LVBCI, port + chip->reg_pi_sr);
+	iputbyte(chip, port + ichdev->roff_sr, ICH_FIFOE | ICH_BCIS | ICH_LVBCI);
 }
 
 static void snd_intel8x0_interrupt(int irq, void *dev_id, struct pt_regs *regs)
 {
 	intel8x0_t *chip = snd_magic_cast(intel8x0_t, dev_id, return);
+	ichdev_t *ichdev;
 	unsigned int status;
+	int i;
 
 	spin_lock(&chip->reg_lock);
-	status = inl(ICHREG(chip, GLOB_STA));
-	if ((status & (ICH_MCINT | ICH_POINT | ICH_PIINT)) == 0) {
+	status = igetdword(chip, chip->int_sta_reg);
+	if ((status & chip->int_sta_mask) == 0) {
 		spin_unlock(&chip->reg_lock);
 		return;
 	}
 	/* ack first */
-	outl(status & (ICH_MCINT | ICH_POINT | ICH_PIINT), ICHREG(chip, GLOB_STA));
+	iputdword(chip, chip->int_sta_reg, status & ~chip->int_sta_mask);
 	spin_unlock(&chip->reg_lock);
 
-	if (status & ICH_POINT)
-		snd_intel8x0_update(chip, &chip->playback);
-	if (status & ICH_PIINT)
-		snd_intel8x0_update(chip, &chip->capture);
-	if (status & ICH_MCINT)
-		snd_intel8x0_update(chip, &chip->capture_mic);
+	for (i = 0; i < chip->bdbars_count; i++) {
+		ichdev = &chip->ichd[i];
+		if (status & ichdev->int_sta_mask)
+			snd_intel8x0_update(chip, ichdev);
+	}
 }
 
 /*
  *  PCM part
  */
 
-static int snd_intel8x0_playback_ioctl(snd_pcm_substream_t * substream,
-				       unsigned int cmd,
-				       void *arg)
-{
-	int result;
-	result = snd_pcm_lib_ioctl(substream, cmd, arg);
-	if (result < 0)
-		return result;
-	return 0;
-}
-
-static int snd_intel8x0_capture_ioctl(snd_pcm_substream_t * substream,
-				      unsigned int cmd,
-				      void *arg)
-{
-	int result;
-	result = snd_pcm_lib_ioctl(substream, cmd, arg);
-	if (result < 0)
-		return result;
-	return 0;
-}
-
-static int snd_intel8x0_playback_trigger(snd_pcm_substream_t *substream, int cmd)
+static int snd_intel8x0_pcm_trigger(snd_pcm_substream_t *substream, int cmd)
 {
 	intel8x0_t *chip = snd_pcm_substream_chip(substream);
+	ichdev_t *ichdev = get_ichdev(substream);
+	unsigned char val = 0;
+	unsigned long port = ichdev->reg_offset;
 
-	return snd_intel8x0_trigger(chip, &chip->playback, cmd);
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+		val = ICH_IOCE | ICH_STARTBM;
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+		val = 0;
+		break;
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		val = ICH_IOCE;
+		break;
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		val = ICH_IOCE | ICH_STARTBM;
+		break;
+	default:
+		return -EINVAL;
+	}
+	iputbyte(chip, port + ICH_REG_OFF_CR, val);
+	if (cmd == SNDRV_PCM_TRIGGER_STOP) {
+		/* reset whole DMA things */
+		while (!(igetbyte(chip, port + ichdev->roff_sr) & ICH_DCH)) ;
+		iputbyte(chip, port + ICH_REG_OFF_CR, ICH_RESETREGS);
+	}
+	return 0;
 }
 
-static int snd_intel8x0_capture_trigger(snd_pcm_substream_t *substream, int cmd)
+static int snd_intel8x0_ali_trigger(snd_pcm_substream_t *substream, int cmd)
 {
 	intel8x0_t *chip = snd_pcm_substream_chip(substream);
+	ichdev_t *ichdev = get_ichdev(substream);
+	unsigned long port = ichdev->reg_offset;
 
-	return snd_intel8x0_trigger(chip, &chip->capture, cmd);
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+		iputbyte(chip, port + ICH_REG_OFF_CR, ICH_IOCE);
+		iputbyte(chip, ICHREG(ALI_DMACR), 1 << ichdev->ali_slot);
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+		iputbyte(chip, ICHREG(ALI_DMACR), 1 << (ichdev->ali_slot + 8));
+		iputbyte(chip, port + ICH_REG_OFF_CR, 0);
+		/* reset whole DMA things */
+		while (!(igetbyte(chip, port + ICH_REG_OFF_CR)))
+			;
+		iputbyte(chip, port + ICH_REG_OFF_CR, ICH_RESETREGS);
+		/* clear interrupts */
+		iputbyte(chip, port + ICH_REG_OFF_SR, igetbyte(chip, port + ICH_REG_OFF_SR) | 0x1e);
+		iputdword(chip, ICHREG(ALI_INTERRUPTSR),
+			  igetdword(chip, ICHREG(ALI_INTERRUPTSR)) & (1 << (ichdev->ali_slot + 8)));
+		break;
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		iputbyte(chip, port + ICH_REG_OFF_CR, 0);
+		break;
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		iputbyte(chip, ICHREG(ALI_DMACR), 1 << ichdev->ali_slot);
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
 }
 
 static int snd_intel8x0_hw_params(snd_pcm_substream_t * substream,
@@ -547,110 +811,70 @@
 
 static void snd_intel8x0_setup_multi_channels(intel8x0_t *chip, int channels)
 {
-	unsigned int cnt = inl(ICHREG(chip, GLOB_CNT)) & ~ICH_PCM_246_MASK;
+	unsigned int cnt = igetdword(chip, ICHREG(GLOB_CNT)) & ~ICH_PCM_246_MASK;
 	if (chip->multi4 && channels == 4)
 		cnt |= ICH_PCM_4;
 	else if (chip->multi6 && channels == 6)
 		cnt |= ICH_PCM_6;
-	outl(cnt, ICHREG(chip, GLOB_CNT));
-}
-
-static int snd_intel8x0_playback_prepare(snd_pcm_substream_t * substream)
-{
-	intel8x0_t *chip = snd_pcm_substream_chip(substream);
-	snd_pcm_runtime_t *runtime = substream->runtime;
-
-	chip->playback.physbuf = runtime->dma_addr;
-	chip->playback.size = snd_pcm_lib_buffer_bytes(substream);
-	chip->playback.fragsize = snd_pcm_lib_period_bytes(substream);
-	spin_lock(&chip->reg_lock);
-	snd_intel8x0_setup_multi_channels(chip, runtime->channels);
-	spin_unlock(&chip->reg_lock);
-	snd_ac97_set_rate(chip->ac97, AC97_PCM_FRONT_DAC_RATE, runtime->rate);
-	snd_intel8x0_setup_periods(chip, &chip->playback);
-	return 0;
+	iputdword(chip, ICHREG(GLOB_CNT), cnt);
 }
 
-static int snd_intel8x0_capture_prepare(snd_pcm_substream_t * substream)
+static int snd_intel8x0_pcm_prepare(snd_pcm_substream_t * substream)
 {
 	intel8x0_t *chip = snd_pcm_substream_chip(substream);
 	snd_pcm_runtime_t *runtime = substream->runtime;
+	ichdev_t *ichdev = get_ichdev(substream);
+	int i;
 
-	chip->capture.physbuf = runtime->dma_addr;
-	chip->capture.size = snd_pcm_lib_buffer_bytes(substream);
-	chip->capture.fragsize = snd_pcm_lib_period_bytes(substream);
-	snd_ac97_set_rate(chip->ac97, AC97_PCM_LR_ADC_RATE, runtime->rate);
-	snd_intel8x0_setup_periods(chip, &chip->capture);
+	ichdev->physbuf = runtime->dma_addr;
+	ichdev->size = snd_pcm_lib_buffer_bytes(substream);
+	ichdev->fragsize = snd_pcm_lib_period_bytes(substream);
+	if (ichdev->ichd == ICHD_PCMOUT && chip->device_type != DEVICE_ALI) {
+		spin_lock(&chip->reg_lock);
+		snd_intel8x0_setup_multi_channels(chip, runtime->channels);
+		spin_unlock(&chip->reg_lock);
+	}
+	for (i = 0; i < 3; i++)
+		if (ichdev->ac97_rate_regs[i])
+			snd_ac97_set_rate(ichdev->ac97, ichdev->ac97_rate_regs[i], runtime->rate);
+	snd_intel8x0_setup_periods(chip, ichdev);
 	return 0;
 }
 
-static snd_pcm_uframes_t snd_intel8x0_playback_pointer(snd_pcm_substream_t * substream)
-{
-	intel8x0_t *chip = snd_pcm_substream_chip(substream);
-	size_t ptr;
-
-	ptr = chip->playback.fragsize1;
-	if (chip->device_type == DEVICE_SIS)
-		ptr -= inw(ICHREG2(chip,chip->reg_po_picb));
-	else
-		ptr -= inw(ICHREG2(chip,chip->reg_po_picb)) << 1;
-	ptr += chip->playback.position;
-	return bytes_to_frames(substream->runtime, ptr);
-}
-
-static snd_pcm_uframes_t snd_intel8x0_capture_pointer(snd_pcm_substream_t * substream)
+static snd_pcm_uframes_t snd_intel8x0_pcm_pointer(snd_pcm_substream_t * substream)
 {
 	intel8x0_t *chip = snd_pcm_substream_chip(substream);
+	ichdev_t *ichdev = get_ichdev(substream);
 	size_t ptr;
 
-	ptr = chip->capture.fragsize1;
+	ptr = ichdev->fragsize1;
 	if (chip->device_type == DEVICE_SIS)
-		ptr -= inw(ICHREG2(chip,chip->reg_pi_picb));
+		ptr -= igetword(chip, ichdev->reg_offset + ichdev->roff_picb);
 	else
-		ptr -= inw(ICHREG2(chip,chip->reg_pi_picb)) << 1;
-	ptr += chip->capture.position;
+		ptr -= igetword(chip, ichdev->reg_offset + ichdev->roff_picb) << 1;
+	ptr += ichdev->position;
 	return bytes_to_frames(substream->runtime, ptr);
 }
 
-static snd_pcm_hardware_t snd_intel8x0_playback =
+static snd_pcm_hardware_t snd_intel8x0_stream =
 {
-	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
 				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
 				 SNDRV_PCM_INFO_MMAP_VALID |
 				 SNDRV_PCM_INFO_PAUSE |
 				 SNDRV_PCM_INFO_RESUME),
-	formats:		SNDRV_PCM_FMTBIT_S16_LE,
-	rates:			0,
-	rate_min:		8000,
-	rate_max:		48000,
-	channels_min:		2,
-	channels_max:		2,
-	buffer_bytes_max:	128 * 1024,
-	period_bytes_min:	32,
-	period_bytes_max:	128 * 1024,
-	periods_min:		1,
-	periods_max:		1024,
-	fifo_size:		0,
-};
-
-static snd_pcm_hardware_t snd_intel8x0_capture =
-{
-	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
-				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
-				 SNDRV_PCM_INFO_MMAP_VALID |
-				 SNDRV_PCM_INFO_RESUME),
-	formats:		SNDRV_PCM_FMTBIT_S16_LE,
-	rates:			0,
-	rate_min:		8000,
-	rate_max:		48000,
-	channels_min:		2,
-	channels_max:		2,
-	buffer_bytes_max:	128 * 1024,
-	period_bytes_min:	32,
-	period_bytes_max:	128 * 1024,
-	periods_min:		1,
-	periods_max:		1024,
-	fifo_size:		0,
+	.formats =		SNDRV_PCM_FMTBIT_S16_LE,
+	.rates =		SNDRV_PCM_RATE_48000,
+	.rate_min =		8000,
+	.rate_max =		48000,
+	.channels_min =		2,
+	.channels_max =		2,
+	.buffer_bytes_max =	128 * 1024,
+	.period_bytes_min =	32,
+	.period_bytes_max =	128 * 1024,
+	.periods_min =		1,
+	.periods_max =		1024,
+	.fifo_size =		0,
 };
 
 static unsigned int channels4[] = {
@@ -660,9 +884,9 @@
 #define CHANNELS4 sizeof(channels4) / sizeof(channels4[0])
 
 static snd_pcm_hw_constraint_list_t hw_constraints_channels4 = {
-	count: CHANNELS4,
-	list: channels4,
-	mask: 0,
+	.count = CHANNELS4,
+	.list = channels4,
+	.mask = 0,
 };
 
 static unsigned int channels6[] = {
@@ -672,20 +896,21 @@
 #define CHANNELS6 sizeof(channels6) / sizeof(channels6[0])
 
 static snd_pcm_hw_constraint_list_t hw_constraints_channels6 = {
-	count: CHANNELS6,
-	list: channels6,
-	mask: 0,
+	.count = CHANNELS6,
+	.list = channels6,
+	.mask = 0,
 };
 
-static int snd_intel8x0_playback_open(snd_pcm_substream_t * substream)
+static int snd_intel8x0_pcm_open(snd_pcm_substream_t * substream, ichdev_t *ichdev)
 {
 	intel8x0_t *chip = snd_pcm_substream_chip(substream);
 	snd_pcm_runtime_t *runtime = substream->runtime;
 	int err;
 
-	chip->playback.substream = substream;
-	runtime->hw = snd_intel8x0_playback;
-	runtime->hw.rates = chip->ac97->rates_front_dac;
+	ichdev->substream = substream;
+	runtime->hw = snd_intel8x0_stream;
+	if (ichdev->ac97_rates_idx >= 0)
+		runtime->hw.rates = ichdev->ac97->rates[ichdev->ac97_rates_idx];
 	if (!(runtime->hw.rates & SNDRV_PCM_RATE_8000))
 		runtime->hw.rate_min = 48000;
 	if (chip->device_type == DEVICE_SIS) {
@@ -694,6 +919,17 @@
 	}
 	if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
 		return err;
+	runtime->private_data = ichdev;
+	return 0;
+}
+
+static int snd_intel8x0_playback_open(snd_pcm_substream_t * substream)
+{
+	intel8x0_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	int err;
+
+	err = snd_intel8x0_pcm_open(substream, &chip->ichd[ICHD_PCMOUT]);
 	if (chip->multi6) {
 		runtime->hw.channels_max = 6;
 		snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, &hw_constraints_channels6);
@@ -704,66 +940,264 @@
 	return 0;
 }
 
+static int snd_intel8x0_playback_close(snd_pcm_substream_t * substream)
+{
+	intel8x0_t *chip = snd_pcm_substream_chip(substream);
+
+	chip->ichd[ICHD_PCMOUT].substream = NULL;
+	return 0;
+}
+
 static int snd_intel8x0_capture_open(snd_pcm_substream_t * substream)
 {
 	intel8x0_t *chip = snd_pcm_substream_chip(substream);
-	snd_pcm_runtime_t *runtime = substream->runtime;
-	int err;
 
-	chip->capture.substream = substream;
-	runtime->hw = snd_intel8x0_capture;
-	runtime->hw.rates = chip->ac97->rates_adc;
-	if (!(runtime->hw.rates & SNDRV_PCM_RATE_8000))
-		runtime->hw.rate_min = 48000;
-	if (chip->device_type == DEVICE_SIS) {
-		runtime->hw.buffer_bytes_max = 64*1024;
-		runtime->hw.period_bytes_max = 64*1024;
-	}
-	if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
-		return err;
+	return snd_intel8x0_pcm_open(substream, &chip->ichd[ICHD_PCMIN]);
+}
+
+static int snd_intel8x0_capture_close(snd_pcm_substream_t * substream)
+{
+	intel8x0_t *chip = snd_pcm_substream_chip(substream);
+
+	chip->ichd[ICHD_PCMIN].substream = NULL;
 	return 0;
 }
 
-static int snd_intel8x0_playback_close(snd_pcm_substream_t * substream)
+static int snd_intel8x0_mic_open(snd_pcm_substream_t * substream)
 {
 	intel8x0_t *chip = snd_pcm_substream_chip(substream);
 
-	chip->playback.substream = NULL;
-	/* disable DAC power */
-	snd_ac97_update_bits(chip->ac97, AC97_POWERDOWN, 0x0200, 0x0200);
+	return snd_intel8x0_pcm_open(substream, &chip->ichd[ICHD_MIC]);
+}
+
+static int snd_intel8x0_mic_close(snd_pcm_substream_t * substream)
+{
+	intel8x0_t *chip = snd_pcm_substream_chip(substream);
+
+	chip->ichd[ICHD_MIC].substream = NULL;
 	return 0;
 }
 
-static int snd_intel8x0_capture_close(snd_pcm_substream_t * substream)
+static int snd_intel8x0_mic2_open(snd_pcm_substream_t * substream)
+{
+	intel8x0_t *chip = snd_pcm_substream_chip(substream);
+
+	return snd_intel8x0_pcm_open(substream, &chip->ichd[ICHD_MIC2]);
+}
+
+static int snd_intel8x0_mic2_close(snd_pcm_substream_t * substream)
+{
+	intel8x0_t *chip = snd_pcm_substream_chip(substream);
+
+	chip->ichd[ICHD_MIC2].substream = NULL;
+	return 0;
+}
+
+static int snd_intel8x0_capture2_open(snd_pcm_substream_t * substream)
+{
+	intel8x0_t *chip = snd_pcm_substream_chip(substream);
+
+	return snd_intel8x0_pcm_open(substream, &chip->ichd[ICHD_PCM2IN]);
+}
+
+static int snd_intel8x0_capture2_close(snd_pcm_substream_t * substream)
+{
+	intel8x0_t *chip = snd_pcm_substream_chip(substream);
+
+	chip->ichd[ICHD_PCM2IN].substream = NULL;
+	return 0;
+}
+
+static int snd_intel8x0_spdif_open(snd_pcm_substream_t * substream)
+{
+	intel8x0_t *chip = snd_pcm_substream_chip(substream);
+
+	return snd_intel8x0_pcm_open(substream, &chip->ichd[ICHD_SPBAR]);
+}
+
+static int snd_intel8x0_spdif_close(snd_pcm_substream_t * substream)
+{
+	intel8x0_t *chip = snd_pcm_substream_chip(substream);
+
+	chip->ichd[ICHD_SPBAR].substream = NULL;
+	return 0;
+}
+
+static int snd_intel8x0_ali_ac97spdifout_open(snd_pcm_substream_t * substream)
+{
+	intel8x0_t *chip = snd_pcm_substream_chip(substream);
+
+	return snd_intel8x0_pcm_open(substream, &chip->ichd[ALID_AC97SPDIFOUT]);
+}
+
+static int snd_intel8x0_ali_ac97spdifout_close(snd_pcm_substream_t * substream)
+{
+	intel8x0_t *chip = snd_pcm_substream_chip(substream);
+
+	chip->ichd[ALID_AC97SPDIFOUT].substream = NULL;
+	return 0;
+}
+
+static int snd_intel8x0_ali_spdifin_open(snd_pcm_substream_t * substream)
+{
+	intel8x0_t *chip = snd_pcm_substream_chip(substream);
+
+	return snd_intel8x0_pcm_open(substream, &chip->ichd[ALID_SPDIFIN]);
+}
+
+static int snd_intel8x0_ali_spdifin_close(snd_pcm_substream_t * substream)
+{
+	intel8x0_t *chip = snd_pcm_substream_chip(substream);
+
+	chip->ichd[ALID_SPDIFIN].substream = NULL;
+	return 0;
+}
+
+static int snd_intel8x0_ali_spdifout_open(snd_pcm_substream_t * substream)
+{
+	intel8x0_t *chip = snd_pcm_substream_chip(substream);
+
+	return snd_intel8x0_pcm_open(substream, &chip->ichd[ALID_SPDIFOUT]);
+}
+
+static int snd_intel8x0_ali_spdifout_close(snd_pcm_substream_t * substream)
 {
 	intel8x0_t *chip = snd_pcm_substream_chip(substream);
 
-	chip->capture.substream = NULL;
-	/* disable ADC power */
-	snd_ac97_update_bits(chip->ac97, AC97_POWERDOWN, 0x0100, 0x0100);
+	chip->ichd[ALID_SPDIFOUT].substream = NULL;
 	return 0;
 }
 
 static snd_pcm_ops_t snd_intel8x0_playback_ops = {
-	open:		snd_intel8x0_playback_open,
-	close:		snd_intel8x0_playback_close,
-	ioctl:		snd_intel8x0_playback_ioctl,
-	hw_params:	snd_intel8x0_hw_params,
-	hw_free:	snd_intel8x0_hw_free,
-	prepare:	snd_intel8x0_playback_prepare,
-	trigger:	snd_intel8x0_playback_trigger,
-	pointer:	snd_intel8x0_playback_pointer,
+	.open =		snd_intel8x0_playback_open,
+	.close =	snd_intel8x0_playback_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	snd_intel8x0_hw_params,
+	.hw_free =	snd_intel8x0_hw_free,
+	.prepare =	snd_intel8x0_pcm_prepare,
+	.trigger =	snd_intel8x0_pcm_trigger,
+	.pointer =	snd_intel8x0_pcm_pointer,
 };
 
 static snd_pcm_ops_t snd_intel8x0_capture_ops = {
-	open:		snd_intel8x0_capture_open,
-	close:		snd_intel8x0_capture_close,
-	ioctl:		snd_intel8x0_capture_ioctl,
-	hw_params:	snd_intel8x0_hw_params,
-	hw_free:	snd_intel8x0_hw_free,
-	prepare:	snd_intel8x0_capture_prepare,
-	trigger:	snd_intel8x0_capture_trigger,
-	pointer:	snd_intel8x0_capture_pointer,
+	.open =		snd_intel8x0_capture_open,
+	.close =	snd_intel8x0_capture_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	snd_intel8x0_hw_params,
+	.hw_free =	snd_intel8x0_hw_free,
+	.prepare =	snd_intel8x0_pcm_prepare,
+	.trigger =	snd_intel8x0_pcm_trigger,
+	.pointer =	snd_intel8x0_pcm_pointer,
+};
+
+static snd_pcm_ops_t snd_intel8x0_capture_mic_ops = {
+	.open =		snd_intel8x0_mic_open,
+	.close =	snd_intel8x0_mic_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	snd_intel8x0_hw_params,
+	.hw_free =	snd_intel8x0_hw_free,
+	.prepare =	snd_intel8x0_pcm_prepare,
+	.trigger =	snd_intel8x0_pcm_trigger,
+	.pointer =	snd_intel8x0_pcm_pointer,
+};
+
+static snd_pcm_ops_t snd_intel8x0_capture_mic2_ops = {
+	.open =		snd_intel8x0_mic2_open,
+	.close =	snd_intel8x0_mic2_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	snd_intel8x0_hw_params,
+	.hw_free =	snd_intel8x0_hw_free,
+	.prepare =	snd_intel8x0_pcm_prepare,
+	.trigger =	snd_intel8x0_pcm_trigger,
+	.pointer =	snd_intel8x0_pcm_pointer,
+};
+
+static snd_pcm_ops_t snd_intel8x0_capture2_ops = {
+	.open =		snd_intel8x0_capture2_open,
+	.close =	snd_intel8x0_capture2_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	snd_intel8x0_hw_params,
+	.hw_free =	snd_intel8x0_hw_free,
+	.prepare =	snd_intel8x0_pcm_prepare,
+	.trigger =	snd_intel8x0_pcm_trigger,
+	.pointer =	snd_intel8x0_pcm_pointer,
+};
+
+static snd_pcm_ops_t snd_intel8x0_spdif_ops = {
+	.open =		snd_intel8x0_spdif_open,
+	.close =	snd_intel8x0_spdif_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	snd_intel8x0_hw_params,
+	.hw_free =	snd_intel8x0_hw_free,
+	.prepare =	snd_intel8x0_pcm_prepare,
+	.trigger =	snd_intel8x0_pcm_trigger,
+	.pointer =	snd_intel8x0_pcm_pointer,
+};
+
+static snd_pcm_ops_t snd_intel8x0_ali_playback_ops = {
+	.open =		snd_intel8x0_playback_open,
+	.close =	snd_intel8x0_playback_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	snd_intel8x0_hw_params,
+	.hw_free =	snd_intel8x0_hw_free,
+	.prepare =	snd_intel8x0_pcm_prepare,
+	.trigger =	snd_intel8x0_ali_trigger,
+	.pointer =	snd_intel8x0_pcm_pointer,
+};
+
+static snd_pcm_ops_t snd_intel8x0_ali_capture_ops = {
+	.open =		snd_intel8x0_capture_open,
+	.close =	snd_intel8x0_capture_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	snd_intel8x0_hw_params,
+	.hw_free =	snd_intel8x0_hw_free,
+	.prepare =	snd_intel8x0_pcm_prepare,
+	.trigger =	snd_intel8x0_ali_trigger,
+	.pointer =	snd_intel8x0_pcm_pointer,
+};
+
+static snd_pcm_ops_t snd_intel8x0_ali_capture_mic_ops = {
+	.open =		snd_intel8x0_mic_open,
+	.close =	snd_intel8x0_mic_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	snd_intel8x0_hw_params,
+	.hw_free =	snd_intel8x0_hw_free,
+	.prepare =	snd_intel8x0_pcm_prepare,
+	.trigger =	snd_intel8x0_ali_trigger,
+	.pointer =	snd_intel8x0_pcm_pointer,
+};
+
+static snd_pcm_ops_t snd_intel8x0_ali_ac97spdifout_ops = {
+	.open =		snd_intel8x0_ali_ac97spdifout_open,
+	.close =	snd_intel8x0_ali_ac97spdifout_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	snd_intel8x0_hw_params,
+	.hw_free =	snd_intel8x0_hw_free,
+	.prepare =	snd_intel8x0_pcm_prepare,
+	.trigger =	snd_intel8x0_ali_trigger,
+	.pointer =	snd_intel8x0_pcm_pointer,
+};
+
+static snd_pcm_ops_t snd_intel8x0_ali_spdifin_ops = {
+	.open =		snd_intel8x0_ali_spdifin_open,
+	.close =	snd_intel8x0_ali_spdifin_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	snd_intel8x0_hw_params,
+	.hw_free =	snd_intel8x0_hw_free,
+	.prepare =	snd_intel8x0_pcm_prepare,
+	.trigger =	snd_intel8x0_pcm_trigger,
+	.pointer =	snd_intel8x0_pcm_pointer,
+};
+
+static snd_pcm_ops_t snd_intel8x0_ali_spdifout_ops = {
+	.open =		snd_intel8x0_ali_spdifout_open,
+	.close =	snd_intel8x0_ali_spdifout_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	snd_intel8x0_hw_params,
+	.hw_free =	snd_intel8x0_hw_free,
+	.prepare =	snd_intel8x0_pcm_prepare,
+	.trigger =	snd_intel8x0_pcm_trigger,
+	.pointer =	snd_intel8x0_pcm_pointer,
 };
 
 static void snd_intel8x0_pcm_free(snd_pcm_t *pcm)
@@ -784,8 +1218,13 @@
 	if (err < 0)
 		return err;
 
-	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_intel8x0_playback_ops);
-	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_intel8x0_capture_ops);
+	if (chip->device_type == DEVICE_ALI) {
+		snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_intel8x0_ali_playback_ops);
+		snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_intel8x0_ali_capture_ops);
+	} else {
+		snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_intel8x0_playback_ops);
+		snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_intel8x0_capture_ops);
+	}
 
 	pcm->private_data = chip;
 	pcm->private_free = snd_intel8x0_pcm_free;
@@ -804,198 +1243,446 @@
  *  PCM code - MIC
  */
 
-static int snd_intel8x0_capture_mic_ioctl(snd_pcm_substream_t * substream,
-					  unsigned int cmd,
-					  void *arg)
+static void snd_intel8x0_pcm_mic_free(snd_pcm_t *pcm)
 {
-	int result;
-	result = snd_pcm_lib_ioctl(substream, cmd, arg);
-	if (result < 0)
-		return result;
-	return 0;
+	intel8x0_t *chip = snd_magic_cast(intel8x0_t, pcm->private_data, return);
+	chip->pcm_mic = NULL;
 }
 
-static int snd_intel8x0_capture_mic_trigger(snd_pcm_substream_t * substream,
-					    int cmd)
+static int __devinit snd_intel8x0_pcm_mic(intel8x0_t *chip, int device, snd_pcm_t ** rpcm)
 {
-	intel8x0_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_t *pcm;
+	int err;
 
-	return snd_intel8x0_trigger(chip, &chip->capture_mic, cmd);
-}
+	if (rpcm)
+		*rpcm = NULL;
+	err = snd_pcm_new(chip->card, "Intel ICH - MIC ADC", device, 0, 1, &pcm);
+	if (err < 0)
+		return err;
 
-static int snd_intel8x0_capture_mic_prepare(snd_pcm_substream_t * substream)
-{
-	intel8x0_t *chip = snd_pcm_substream_chip(substream);
-	snd_pcm_runtime_t *runtime = substream->runtime;
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
+			chip->device_type == DEVICE_ALI ?
+				&snd_intel8x0_ali_capture_mic_ops :
+				&snd_intel8x0_capture_mic_ops);
+
+	pcm->private_data = chip;
+	pcm->private_free = snd_intel8x0_pcm_mic_free;
+	pcm->info_flags = 0;
+	sprintf(pcm->name, "%s - MIC ADC", chip->card->shortname);
 
-	chip->capture_mic.physbuf = runtime->dma_addr;
-	chip->capture_mic.size = snd_pcm_lib_buffer_bytes(substream);
-	chip->capture_mic.fragsize = snd_pcm_lib_period_bytes(substream);
-	snd_ac97_set_rate(chip->ac97, AC97_PCM_MIC_ADC_RATE, runtime->rate);
-	snd_intel8x0_setup_periods(chip, &chip->capture_mic);
+	chip->pcm_mic = pcm;	
+	if (rpcm)
+		*rpcm = pcm;
 	return 0;
 }
 
-static snd_pcm_uframes_t snd_intel8x0_capture_mic_pointer(snd_pcm_substream_t * substream)
+/*
+ *  PCM code - MIC2
+ */
+
+static void snd_intel8x0_pcm_mic2_free(snd_pcm_t *pcm)
 {
-	intel8x0_t *chip = snd_pcm_substream_chip(substream);
-	size_t ptr;
+	intel8x0_t *chip = snd_magic_cast(intel8x0_t, pcm->private_data, return);
+	chip->pcm_mic2 = NULL;
+}
 
-	ptr = chip->capture_mic.fragsize1;
-	if (chip->device_type == DEVICE_SIS)
-		ptr -= inw(ICHREG2(chip,chip->reg_mc_picb));
-	else
-		ptr -= inw(ICHREG2(chip,chip->reg_mc_picb)) << 1;
-	ptr += chip->capture_mic.position;
-	return bytes_to_frames(substream->runtime, ptr);
+static int __devinit snd_intel8x0_pcm_mic2(intel8x0_t *chip, int device, snd_pcm_t ** rpcm)
+{
+	snd_pcm_t *pcm;
+	int err;
+
+	if (rpcm)
+		*rpcm = NULL;
+	err = snd_pcm_new(chip->card, "Intel ICH - MIC2 ADC", device, 0, 1, &pcm);
+	if (err < 0)
+		return err;
+
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_intel8x0_capture_mic2_ops);
+
+	pcm->private_data = chip;
+	pcm->private_free = snd_intel8x0_pcm_mic2_free;
+	pcm->info_flags = 0;
+	sprintf(pcm->name, "%s - MIC2 ADC", chip->card->shortname);
+
+	chip->pcm_mic2 = pcm;	
+	if (rpcm)
+		*rpcm = pcm;
+	return 0;
 }
 
-static snd_pcm_hardware_t snd_intel8x0_capture_mic =
+/*
+ *  PCM code - capture2
+ */
+
+static void snd_intel8x0_pcm_capture2_free(snd_pcm_t *pcm)
 {
-	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
-				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
-				 SNDRV_PCM_INFO_MMAP_VALID |
-				 SNDRV_PCM_INFO_PAUSE),
-	formats:		SNDRV_PCM_FMTBIT_S16_LE,
-	rates:			0,
-	rate_min:		8000,
-	rate_max:		48000,
-	channels_min:		1,
-	channels_max:		1,
-	buffer_bytes_max:	128 * 1024,
-	period_bytes_min:	32,
-	period_bytes_max:	128 * 1024,
-	periods_min:		1,
-	periods_max:		1024,
-	fifo_size:		0,
-};
+	intel8x0_t *chip = snd_magic_cast(intel8x0_t, pcm->private_data, return);
+	chip->pcm2 = NULL;
+}
 
-static int snd_intel8x0_capture_mic_open(snd_pcm_substream_t * substream)
+static int __devinit snd_intel8x0_pcm_capture2(intel8x0_t *chip, int device, snd_pcm_t ** rpcm)
 {
-	intel8x0_t *chip = snd_pcm_substream_chip(substream);
-	snd_pcm_runtime_t *runtime = substream->runtime;
+	snd_pcm_t *pcm;
+	int err;
 
-	chip->capture_mic.substream = substream;
-	runtime->hw = snd_intel8x0_capture_mic;
-	runtime->hw.rates = chip->ac97->rates_mic_adc;
-	if (!(runtime->hw.rates & SNDRV_PCM_RATE_8000))
-		runtime->hw.rate_min = 48000;
-	if (chip->device_type == DEVICE_SIS) {
-		runtime->hw.buffer_bytes_max = 64*1024;
-		runtime->hw.period_bytes_max = 64*1024;
-	}
+	if (rpcm)
+		*rpcm = NULL;
+	err = snd_pcm_new(chip->card, "Intel ICH - ADC2", device, 0, 1, &pcm);
+	if (err < 0)
+		return err;
+
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_intel8x0_capture2_ops);
+
+	pcm->private_data = chip;
+	pcm->private_free = snd_intel8x0_pcm_capture2_free;
+	pcm->info_flags = 0;
+	sprintf(pcm->name, "%s - ADC2", chip->card->shortname);
+
+	chip->pcm2 = pcm;	
+	if (rpcm)
+		*rpcm = pcm;
 	return 0;
 }
 
-static int snd_intel8x0_capture_mic_close(snd_pcm_substream_t * substream)
+/*
+ *  PCM code - S/PDIF
+ */
+
+static void snd_intel8x0_pcm_spdif_free(snd_pcm_t *pcm)
 {
-	intel8x0_t *chip = snd_pcm_substream_chip(substream);
+	intel8x0_t *chip = snd_magic_cast(intel8x0_t, pcm->private_data, return);
+	chip->pcm_spdif = NULL;
+}
+
+static int __devinit snd_intel8x0_pcm_spdif(intel8x0_t *chip, int device, snd_pcm_t ** rpcm)
+{
+	snd_pcm_t *pcm;
+	int err;
+
+	if (rpcm)
+		*rpcm = NULL;
+	err = snd_pcm_new(chip->card, "Intel ICH - IEC958", device, 1, 0, &pcm);
+	if (err < 0)
+		return err;
+
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_intel8x0_spdif_ops);
 
-	chip->capture_mic.substream = NULL;
-	/* disable ADC power */
-	snd_ac97_update_bits(chip->ac97, AC97_EXTENDED_STATUS, 0x4000, 0x4000);
+	pcm->private_data = chip;
+	pcm->private_free = snd_intel8x0_pcm_spdif_free;
+	pcm->info_flags = 0;
+	sprintf(pcm->name, "%s - IEC958", chip->card->shortname);
+
+	chip->pcm_spdif = pcm;	
+	if (rpcm)
+		*rpcm = pcm;
 	return 0;
 }
 
-static snd_pcm_ops_t snd_intel8x0_capture_mic_ops = {
-	open:		snd_intel8x0_capture_mic_open,
-	close:		snd_intel8x0_capture_mic_close,
-	ioctl:		snd_intel8x0_capture_mic_ioctl,
-	hw_params:	snd_intel8x0_hw_params,
-	hw_free:	snd_intel8x0_hw_free,
-	prepare:	snd_intel8x0_capture_mic_prepare,
-	trigger:	snd_intel8x0_capture_mic_trigger,
-	pointer:	snd_intel8x0_capture_mic_pointer,
-};
+/*
+ *  PCM code - ALI S/PDIF
+ */
 
-static void snd_intel8x0_pcm_mic_free(snd_pcm_t *pcm)
+static void snd_intel8x0_ali_spdif_free(snd_pcm_t *pcm)
 {
 	intel8x0_t *chip = snd_magic_cast(intel8x0_t, pcm->private_data, return);
-	chip->pcm_mic = NULL;
+	chip->pcm_spdif = NULL;
 }
 
-static int __devinit snd_intel8x0_pcm_mic(intel8x0_t *chip, int device, snd_pcm_t ** rpcm)
+static int __devinit snd_intel8x0_ali_spdif(intel8x0_t *chip, int device, snd_pcm_t ** rpcm)
 {
 	snd_pcm_t *pcm;
 	int err;
 
 	if (rpcm)
 		*rpcm = NULL;
-	err = snd_pcm_new(chip->card, "Intel ICH - MIC ADC", device, 1, 1, &pcm);
+	err = snd_pcm_new(chip->card, "Intel ICH - IEC958", device, 1, 1, &pcm);
 	if (err < 0)
 		return err;
 
-	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_intel8x0_capture_mic_ops);
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_intel8x0_ali_spdifout_ops);
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_intel8x0_ali_spdifin_ops);
 
 	pcm->private_data = chip;
-	pcm->private_free = snd_intel8x0_pcm_mic_free;
+	pcm->private_free = snd_intel8x0_ali_spdif_free;
 	pcm->info_flags = 0;
-	sprintf(pcm->name, "%s - MIC ADC", chip->card->shortname);
+	sprintf(pcm->name, "%s - IEC958", chip->card->shortname);
 
-	chip->pcm_mic = pcm;	
+	chip->pcm_spdif = pcm;	
 	if (rpcm)
 		*rpcm = pcm;
 	return 0;
 }
 
 /*
- *  Mixer part
+ *  PCM code - ALI AC'97 S/PDIF
  */
 
-static void snd_intel8x0_codec_init(ac97_t *ac97)
+static void snd_intel8x0_ali_ac97spdif_free(snd_pcm_t *pcm)
 {
-	// intel8x0_t *chip = snd_magic_cast(intel8x0_t, ac97->private_data, return);
+	intel8x0_t *chip = snd_magic_cast(intel8x0_t, pcm->private_data, return);
+	chip->pcm_ac97spdif = NULL;
+}
 
-	/* disable DAC & ADC power */
-	snd_ac97_update_bits(ac97, AC97_POWERDOWN, 0x0300, 0x0300);
-	/* disable center DAC/surround DAC/LFE DAC/MIC ADC */
-	snd_ac97_update_bits(ac97, AC97_EXTENDED_STATUS, 0xe800, 0xe800);
+static int __devinit snd_intel8x0_ali_ac97spdif(intel8x0_t *chip, int device, snd_pcm_t ** rpcm)
+{
+	snd_pcm_t *pcm;
+	int err;
+
+	if (rpcm)
+		*rpcm = NULL;
+	err = snd_pcm_new(chip->card, "ALI - AC97 IEC958", device, 1, 0, &pcm);
+	if (err < 0)
+		return err;
+
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_intel8x0_ali_ac97spdifout_ops);
+
+	pcm->private_data = chip;
+	pcm->private_free = snd_intel8x0_ali_ac97spdif_free;
+	pcm->info_flags = 0;
+	sprintf(pcm->name, "%s - AC97 IEC958", chip->card->shortname);
+
+	chip->pcm_ac97spdif = pcm;	
+	if (rpcm)
+		*rpcm = pcm;
+	return 0;
 }
 
+/*
+ *  Mixer part
+ */
+
 static void snd_intel8x0_mixer_free_ac97(ac97_t *ac97)
 {
 	intel8x0_t *chip = snd_magic_cast(intel8x0_t, ac97->private_data, return);
-	if (ac97->num == 0) {
-		chip->ac97 = NULL;
-	} else {
-		chip->ac97sec = NULL;
-	}
+	chip->ac97[ac97->num] = NULL;
 }
 
+static struct _ac97_rate_regs {
+	unsigned int ichd;
+	unsigned short regs[3];
+	short rates_idx;
+} ac97_rate_regs[] = {
+	{ ICHD_PCMOUT, { AC97_PCM_FRONT_DAC_RATE, AC97_PCM_SURR_DAC_RATE, AC97_PCM_LFE_DAC_RATE }, AC97_RATES_FRONT_DAC },
+	{ ICHD_PCMIN, { AC97_PCM_LR_ADC_RATE, 0, 0 }, AC97_RATES_ADC },
+	{ ICHD_MIC, { AC97_PCM_MIC_ADC_RATE, 0, 0 }, AC97_RATES_MIC_ADC },
+	{ ICHD_MIC2, { AC97_PCM_MIC_ADC_RATE, 0, 0 }, AC97_RATES_MIC_ADC },
+	{ ICHD_PCM2IN, { AC97_PCM_LR_ADC_RATE, 0, 0 }, AC97_RATES_ADC },
+	{ ICHD_SPBAR, { AC97_SPDIF, 0, 0 }, AC97_RATES_SPDIF },
+};
+
+static struct _ac97_ali_rate_regs {
+	unsigned int ichd;
+	unsigned short regs[3];
+	short rates_idx;
+} ac97_ali_rate_regs[] = {
+	{ ALID_PCMOUT, { AC97_PCM_FRONT_DAC_RATE, AC97_PCM_SURR_DAC_RATE, AC97_PCM_LFE_DAC_RATE }, AC97_RATES_FRONT_DAC },
+	{ ALID_PCMIN, { AC97_PCM_LR_ADC_RATE, 0, 0 }, AC97_RATES_ADC },
+	{ ALID_MIC, { AC97_PCM_MIC_ADC_RATE, 0, 0 }, AC97_RATES_MIC_ADC },
+	{ ALID_AC97SPDIFOUT, { AC97_SPDIF, 0, 0 }, AC97_RATES_SPDIF },
+	{ ALID_SPDIFOUT, { 0, 0, 0 }, -1 },
+	{ ALID_SPDIFIN, { 0, 0, 0 }, -1 },
+};
+
+
 static int __devinit snd_intel8x0_mixer(intel8x0_t *chip, int ac97_clock)
 {
-	ac97_t ac97;
-	int err;
-
+	ac97_t ac97, *x97;
+	ichdev_t *ichdev;
+	int err, i, channels = 2, codecs;
+	unsigned int glob_sta = 0;
+
+	for (i = 0; i <= ICHD_LAST; i++) {
+		if (chip->device_type != DEVICE_ALI) {
+			struct _ac97_rate_regs *aregs;
+			aregs = &ac97_rate_regs[i];
+			ichdev = &chip->ichd[aregs->ichd];
+			ichdev->ac97_rate_regs[0] = aregs->regs[0];
+			ichdev->ac97_rate_regs[1] = aregs->regs[1];
+			ichdev->ac97_rate_regs[2] = aregs->regs[2];
+			ichdev->ac97_rates_idx = aregs->rates_idx;
+		} else {
+			struct _ac97_ali_rate_regs *aregs;
+			aregs = &ac97_ali_rate_regs[i];
+			ichdev = &chip->ichd[aregs->ichd];
+			ichdev->ac97_rate_regs[0] = aregs->regs[0];
+			ichdev->ac97_rate_regs[1] = aregs->regs[1];
+			ichdev->ac97_rate_regs[2] = aregs->regs[2];
+			ichdev->ac97_rates_idx = aregs->rates_idx;
+		}
+	}
 	chip->in_ac97_init = 1;
 	memset(&ac97, 0, sizeof(ac97));
-	ac97.write = snd_intel8x0_codec_write;
-	ac97.read = snd_intel8x0_codec_read;
-	ac97.init = snd_intel8x0_codec_init;
 	ac97.private_data = chip;
 	ac97.private_free = snd_intel8x0_mixer_free_ac97;
 	if (ac97_clock >= 8000 && ac97_clock <= 48000)
 		ac97.clock = ac97_clock;
 	else
 		ac97.clock = 48000;
-	if ((err = snd_ac97_mixer(chip->card, &ac97, &chip->ac97)) < 0)
+	if (chip->device_type != DEVICE_ALI) {
+		glob_sta = igetdword(chip, ICHREG(GLOB_STA));
+		ac97.write = snd_intel8x0_codec_write;
+		ac97.read = snd_intel8x0_codec_read;
+		if (glob_sta & ICH_PCM_6)
+			channels = 6;
+		else if (glob_sta & ICH_PCM_4)
+			channels = 4;
+		if (chip->device_type == DEVICE_INTEL_ICH4) {
+			codecs = 0;
+			if (glob_sta & ICH_PCR)
+				codecs++;
+			if (glob_sta & ICH_SCR)
+				codecs++;
+			if (glob_sta & ICH_TCR)
+				codecs++;
+			chip->in_sdin_init = 1;
+			for (i = 0; i < codecs; i++) {
+				ac97.num = i;
+				snd_intel8x0_codec_read(&ac97, 0);
+				chip->ac97_sdin[i] = igetbyte(chip, ICHREG(SDM)) & ICH_LDI_MASK;
+			}
+			ac97.num = 0;
+			chip->in_sdin_init = 0;
+		} else {
+			codecs = glob_sta & ICH_SCR ? 2 : 1;
+		}
+	} else {
+		ac97.write = snd_intel8x0_ali_codec_write;
+		ac97.read = snd_intel8x0_ali_codec_read;
+		channels = 6;
+		codecs = 1;
+		/* detect the secondary codec */
+		for (i = 0; i < 100; i++) {
+			unsigned int reg = igetdword(chip, ICHREG(ALI_RTSR));
+			if (reg & 0x40) {
+				codecs = 2;
+				break;
+			}
+			iputdword(chip, ICHREG(ALI_RTSR), reg | 0x40);
+			udelay(1);
+		}
+	}
+	if ((err = snd_ac97_mixer(chip->card, &ac97, &x97)) < 0)
 		return err;
-#if 0 /* it seems that SDIN signals are mixed together (at least for AD CNR boards) */
-	if (inl(ICHREG(chip, GLOB_STA)) & ICH_SCR) {
-		ac97.num = 1;
-		ac97.addr = 1;
-		snd_ac97_mixer(chip->card, &ac97, &chip->ac97sec);
+	chip->ac97[0] = x97;
+	chip->ichd[ICHD_PCMOUT].ac97 = x97;
+	chip->ichd[ICHD_PCMIN].ac97 = x97;
+	if (x97->ext_id & AC97_EI_VRM)
+		chip->ichd[ICHD_MIC].ac97 = x97;
+	if (x97->ext_id & AC97_EI_SPDIF) {
+		if (chip->device_type != DEVICE_ALI)
+			chip->ichd[ICHD_SPBAR].ac97 = x97;
+		else
+			chip->ichd[ALID_AC97SPDIFOUT].ac97 = x97;
+	}
+	/* make sure, that we have DACs at right slot for rev2.2 */
+	if (ac97_is_rev22(x97))
+		snd_ac97_update_bits(x97, AC97_EXTENDED_ID, AC97_EI_DACS_SLOT_MASK, 0);
+	/* AnalogDevices CNR boards uses special codec chaining */
+	/* skip standard test method for secondary codecs in this case */
+	if (x97->flags & AC97_AD_MULTI) {
+		codecs = 1;
+		goto __skip_secondary;
+	}
+	if (codecs < 2)
+		goto __skip_secondary;
+	for (i = 1; i < codecs; i++) {
+		ac97.num = i;
+		if ((err = snd_ac97_mixer(chip->card, &ac97, &x97)) < 0) {
+			snd_printk("Unable to initialize codec #%i [device = %i, GLOB_STA = 0x%x]\n", i, chip->device_type, glob_sta);
+			codecs = i;
+			break;
+		}
+		chip->ac97[i] = x97;
+		switch (chip->device_type) {
+		case DEVICE_INTEL_ICH4:
+			if (chip->ichd[ICHD_PCM2IN].ac97 == NULL)
+				chip->ichd[ICHD_PCM2IN].ac97 = x97;
+			if (x97->ext_id & AC97_EI_VRM) {
+				if (chip->ichd[ICHD_MIC].ac97 == NULL)
+					chip->ichd[ICHD_MIC].ac97 = x97;
+				else if (chip->ichd[ICHD_MIC2].ac97 == NULL &&
+					 chip->ichd[ICHD_PCM2IN].ac97 == x97)
+					chip->ichd[ICHD_MIC2].ac97 = x97;
+			}
+			if (x97->ext_id & AC97_EI_SPDIF) {
+				if (chip->ichd[ICHD_SPBAR].ac97 == NULL)
+					chip->ichd[ICHD_SPBAR].ac97 = x97;
+			}
+			break;
+		default:
+			if (x97->ext_id & AC97_EI_VRM) {
+				if (chip->ichd[ICHD_MIC].ac97 == NULL)
+					chip->ichd[ICHD_MIC].ac97 = x97;
+			}
+			break;
+		}
 	}
-#endif
-	if ((inl(ICHREG(chip, GLOB_STA)) & (ICH_PCM_4|ICH_PCM_6)) != (ICH_PCM_4|ICH_PCM_6))
-		return 0;
-	if ((chip->ac97->scaps & AC97_SCAP_SURROUND_DAC) ||
-	    (chip->ac97sec && (chip->ac97sec->scaps & AC97_SCAP_SURROUND_DAC))) {
-		chip->multi4 = 1;
-		if ((chip->ac97->scaps & AC97_SCAP_CENTER_LFE_DAC) ||
-		    (chip->ac97sec && (chip->ac97sec->scaps & AC97_SCAP_CENTER_LFE_DAC)))
+	
+      __skip_secondary:
+	if (chip->device_type == DEVICE_INTEL_ICH4) {
+		u8 tmp = igetbyte(chip, ICHREG(SDM));
+		tmp &= ~(ICH_DI2L_MASK|ICH_DI1L_MASK);
+		if (chip->ichd[ICHD_PCM2IN].ac97) {
+			tmp |= ICH_SE;	/* steer enable for multiple SDINs */
+			tmp |= chip->ac97_sdin[0] << ICH_DI1L_SHIFT;
+			tmp |= chip->ac97_sdin[chip->ichd[ICHD_PCM2IN].ac97->num] << ICH_DI2L_SHIFT;
+		} else {
+			tmp &= ~ICH_SE;
+		}
+		iputbyte(chip, ICHREG(SDM), tmp);
+	}
+      	for (i = 0; i < 3; i++) {
+		if ((x97 = chip->ac97[i]) == NULL)
+			continue;
+		if (x97->scaps & AC97_SCAP_SURROUND_DAC)
+			chip->multi4 = 1;
+	}
+      	for (i = 0; i < 3 && chip->multi4; i++) {
+		if ((x97 = chip->ac97[i]) == NULL)
+			continue;
+		if (x97->scaps & AC97_SCAP_CENTER_LFE_DAC)
 			chip->multi6 = 1;
 	}
+	if (codecs > 1) {
+		/* assign right slots for rev2.2 codecs */
+		i = 1;
+		if (chip->multi4)
+			goto __6ch;
+		for ( ; i < codecs; i++) {
+			if (ac97_is_rev22(x97 = chip->ac97[i])) {
+				snd_ac97_update_bits(x97, AC97_EXTENDED_ID, AC97_EI_DACS_SLOT_MASK, 1);
+				chip->multi4 = 1;
+				break;
+			}
+		}
+	      __6ch:
+		for ( ; i < codecs && chip->multi4; i++) {
+			if (ac97_is_rev22(x97 = chip->ac97[i])) {
+				snd_ac97_update_bits(x97, AC97_EXTENDED_ID, AC97_EI_DACS_SLOT_MASK, 2);
+				chip->multi6 = 1;
+				break;
+			}
+		}
+		/* ok, some older codecs might support only AMAP */
+		if (!chip->multi4) {
+			for (i = 1; i < codecs; i++) {
+				if (ac97_can_amap(x97 = chip->ac97[i])) {
+					if (x97->addr == 1) {
+						chip->multi4 = 1;
+						break;
+					}
+				}
+			}
+			for ( ; i < codecs && chip->multi4; i++) {
+				if (ac97_can_amap(x97 = chip->ac97[i])) {
+					if (x97->addr == 2) {
+						chip->multi6 = 1;
+						break;
+					}
+				}
+			}
+		}
+	}
 	chip->in_ac97_init = 0;
 	return 0;
 }
@@ -1005,116 +1692,190 @@
  *
  */
 
-static int snd_intel8x0_chip_init(intel8x0_t *chip)
+static void do_ali_reset(intel8x0_t *chip)
+{
+	iputdword(chip, ICHREG(ALI_SCR), 0x8000000);
+	iputdword(chip, ICHREG(ALI_FIFOCR1), 0x83838383);
+	iputdword(chip, ICHREG(ALI_FIFOCR2), 0x83838383);
+	iputdword(chip, ICHREG(ALI_INTERFACECR), 0x04080002); /* no spdif? */
+	iputdword(chip, ICHREG(ALI_INTERRUPTCR), 0x00000000);
+	iputdword(chip, ICHREG(ALI_INTERRUPTSR), 0x00000000);
+}
+
+static void do_delay(intel8x0_t *chip)
+{
+#ifdef CONFIG_PM
+	if (chip->in_suspend) {
+		mdelay((1000 + HZ - 1) / HZ);
+		return;
+	}
+#endif
+	set_current_state(TASK_UNINTERRUPTIBLE);
+	schedule_timeout(1);
+}
+
+static int snd_intel8x0_ich_chip_init(intel8x0_t *chip)
 {
 	signed long end_time;
-	unsigned int cnt;
+	unsigned int cnt, status, nstatus;
 	
 	/* put logic to right state */
 	/* first clear status bits */
-	cnt = inl(ICHREG(chip, GLOB_STA));
-	outl(cnt & (ICH_RCS | ICH_MCINT | ICH_POINT | ICH_PIINT), ICHREG(chip, GLOB_STA));
+	cnt = igetdword(chip, ICHREG(GLOB_STA));
+	iputdword(chip, ICHREG(GLOB_STA), cnt & (ICH_RCS | ICH_MCINT | ICH_POINT | ICH_PIINT));
 
 	/* ACLink on, 2 channels */
-	cnt = inl(ICHREG(chip, GLOB_CNT));
+	cnt = igetdword(chip, ICHREG(GLOB_CNT));
 	cnt &= ~(ICH_ACLINK | ICH_PCM_246_MASK);
 	/* finish cold or do warm reset */
 	cnt |= (cnt & ICH_AC97COLD) == 0 ? ICH_AC97COLD : ICH_AC97WARM;
-	outl(cnt, ICHREG(chip, GLOB_CNT));
+	iputdword(chip, ICHREG(GLOB_CNT), cnt);
 	end_time = (jiffies + (HZ / 4)) + 1;
 	do {
-		if ((inl(ICHREG(chip, GLOB_CNT)) & ICH_AC97WARM) == 0)
+		if ((igetdword(chip, ICHREG(GLOB_CNT)) & ICH_AC97WARM) == 0)
 			goto __ok;
-#ifdef CONFIG_PM
-		if (chip->in_suspend) {
-			mdelay(10);
-			continue;
-		}
-#endif
-		set_current_state(TASK_UNINTERRUPTIBLE);
-		schedule_timeout(1);
-	} while (end_time - (signed long)jiffies >= 0);
-	snd_printk("AC'97 warm reset still in progress? [0x%x]\n", inl(ICHREG(chip, GLOB_CNT)));
+		do_delay(chip);
+	} while (time_after_eq(end_time, jiffies));
+	snd_printk("AC'97 warm reset still in progress? [0x%x]\n", igetdword(chip, ICHREG(GLOB_CNT)));
 	return -EIO;
 
       __ok:
-	/* wait for primary codec ready status.
+	/* wait for any codec ready status.
 	 * Once it becomes ready it should remain ready
 	 * as long as we do not disable the ac97 link.
  	 */
 	end_time = jiffies + HZ;
 	do {
-		if (inl(ICHREG(chip, GLOB_STA)) & ICH_PCR)
+		status = igetdword(chip, ICHREG(GLOB_STA)) & (ICH_PCR | ICH_SCR | ICH_TCR);
+		if (status)
 			goto __ok1;
-#ifdef CONFIG_PM
-		if (chip->in_suspend) {
-			mdelay(10);
-			continue;
-		}
-#endif
-		set_current_state(TASK_UNINTERRUPTIBLE);
-		schedule_timeout(1);
-	} while (end_time - (signed long)jiffies >= 0);
-	snd_printk("codec_ready: primary codec is not ready [0x%x]\n", inl(ICHREG(chip, GLOB_STA)));
+		do_delay(chip);
+	} while (time_after_eq(end_time, jiffies));
+	snd_printk(KERN_ERR "codec_ready: codec is not ready [0x%x]\n", igetdword(chip, ICHREG(GLOB_STA)));
 	return -EIO;
 
       __ok1:
-	/* wait for secondary codec ready status. No secondary codec? , ok */
-	/* the end_time variable is not initialized again */
+      	if (status == (ICH_PCR | ICH_SCR | ICH_TCR))
+      		goto __ok3;
+	/* wait for other codecs ready status. No secondary codecs? , ok */
+	end_time = jiffies + HZ / 4;
 	do {
-		if (inl(ICHREG(chip, GLOB_STA)) & ICH_SCR)
-			break;
-#ifdef CONFIG_PM
-		if (chip->in_suspend) {
-			mdelay(10);
-			continue;
+		nstatus = igetdword(chip, ICHREG(GLOB_STA)) & (ICH_PCR | ICH_SCR | ICH_TCR);
+		if (nstatus != status) {
+			status = nstatus;
+			goto __ok2;
 		}
-#endif
-		set_current_state(TASK_UNINTERRUPTIBLE);
-		schedule_timeout(1);
-	} while (end_time - (signed long)jiffies >= 0);
+		do_delay(chip);
+	} while (time_after_eq(end_time, jiffies));
+
+      __ok2:
+      	if (status == (ICH_PCR | ICH_SCR | ICH_TCR))
+      		goto __ok3;
+	/* wait for other codecs ready status. No other secondary codecs? , ok */
+	/* end_time is not initialized here */
+	do {
+		nstatus = igetdword(chip, ICHREG(GLOB_STA)) & (ICH_PCR | ICH_SCR | ICH_TCR);
+		if (nstatus != status) {
+			status = nstatus;
+			goto __ok2;
+		}
+		do_delay(chip);
+	} while (time_after_eq(end_time, jiffies));
 
-	inw(chip->port);	/* clear semaphore flag */
+      __ok3:      
+      	return 0;
+}
+
+static int snd_intel8x0_ali_chip_init(intel8x0_t *chip)
+{
+	u32 reg;
+	int i = 0;
+
+	reg = igetdword(chip, ICHREG(ALI_SCR));
+	if ((reg & 2) == 0)	/* Cold required */
+		reg |= 2;
+	else
+		reg |= 1;	/* Warm */
+	reg &= ~0x80000000;	/* ACLink on */
+	iputdword(chip, ICHREG(ALI_SCR), reg);
+
+	for (i = 0; i < HZ / 2; i++) {
+		if (! (igetdword(chip, ICHREG(ALI_INTERRUPTSR)) & ALI_INT_GPIO))
+			goto __ok;
+		do_delay(chip);
+	}
+	snd_printk(KERN_ERR "AC'97 reset failed.\n");
+	return -EIO;
+
+ __ok:
+	for (i = 0; i < HZ / 2; i++) {
+		reg = igetdword(chip, ICHREG(ALI_RTSR));
+		if (reg & 0x80) /* primary codec */
+			break;
+		iputdword(chip, ICHREG(ALI_RTSR), reg | 0x80);
+		do_delay(chip);
+	}
+
+	do_ali_reset(chip);
+	return 0;
+}
+
+static int snd_intel8x0_chip_init(intel8x0_t *chip)
+{
+	int i, err;
+	
+	if (chip->device_type != DEVICE_ALI)
+		err = snd_intel8x0_ich_chip_init(chip);
+	else
+		err = snd_intel8x0_ali_chip_init(chip);
+	if (err < 0)
+		return err;
+
+	iagetword(chip, 0);	/* clear semaphore flag */
 
 	/* disable interrupts */
-	outb(0x00, ICHREG(chip, PI_CR));
-	outb(0x00, ICHREG(chip, PO_CR));
-	outb(0x00, ICHREG(chip, MC_CR));
+	for (i = 0; i < chip->bdbars_count; i++)
+		iputbyte(chip, ICH_REG_OFF_CR + chip->ichd[i].reg_offset, 0x00);
 	/* reset channels */
-	outb(ICH_RESETREGS, ICHREG(chip, PI_CR));
-	outb(ICH_RESETREGS, ICHREG(chip, PO_CR));
-	outb(ICH_RESETREGS, ICHREG(chip, MC_CR));
+	for (i = 0; i < chip->bdbars_count; i++)
+		iputbyte(chip, ICH_REG_OFF_CR + chip->ichd[i].reg_offset, ICH_RESETREGS);
 	/* initialize Buffer Descriptor Lists */
-	outl(chip->playback.bdbar_addr, ICHREG(chip, PO_BDBAR));
-	outl(chip->capture.bdbar_addr, ICHREG(chip, PI_BDBAR));
-	outl(chip->capture_mic.bdbar_addr, ICHREG(chip, MC_BDBAR));
+	for (i = 0; i < chip->bdbars_count; i++)
+		iputdword(chip, ICH_REG_OFF_BDBAR + chip->ichd[i].reg_offset, chip->ichd[i].bdbar_addr);
 	return 0;
 }
 
+static void snd_intel8x0_proc_done(intel8x0_t * chip);
+
 static int snd_intel8x0_free(intel8x0_t *chip)
 {
+	int i;
+
 	if (chip->irq < 0)
 		goto __hw_end;
 	/* disable interrupts */
-	outb(0x00, ICHREG(chip, PI_CR));
-	outb(0x00, ICHREG(chip, PO_CR));
-	outb(0x00, ICHREG(chip, MC_CR));
+	for (i = 0; i < chip->bdbars_count; i++)
+		iputbyte(chip, ICH_REG_OFF_CR + chip->ichd[i].reg_offset, 0x00);
 	/* reset channels */
-	outb(ICH_RESETREGS, ICHREG(chip, PI_CR));
-	outb(ICH_RESETREGS, ICHREG(chip, PO_CR));
-	outb(ICH_RESETREGS, ICHREG(chip, MC_CR));
+	for (i = 0; i < chip->bdbars_count; i++)
+		iputbyte(chip, ICH_REG_OFF_CR + chip->ichd[i].reg_offset, ICH_RESETREGS);
 	/* --- */
 	synchronize_irq(chip->irq);
       __hw_end:
+	snd_intel8x0_proc_done(chip);
 	if (chip->bdbars)
-		snd_free_pci_pages(chip->pci, 3 * sizeof(u32) * ICH_MAX_FRAGS * 2, chip->bdbars, chip->bdbars_addr);
-	if (chip->res_port) {
-		release_resource(chip->res_port);
-		kfree_nocheck(chip->res_port);
-	}
-	if (chip->res_bmport) {
-		release_resource(chip->res_bmport);
-		kfree_nocheck(chip->res_bmport);
+		snd_free_pci_pages(chip->pci, chip->bdbars_count * sizeof(u32) * ICH_MAX_FRAGS * 2, chip->bdbars, chip->bdbars_addr);
+	if (chip->remap_addr)
+		iounmap((void *) chip->remap_addr);
+	if (chip->remap_bmaddr)
+		iounmap((void *) chip->remap_bmaddr);
+	if (chip->res) {
+		release_resource(chip->res);
+		kfree_nocheck(chip->res);
+	}
+	if (chip->res_bm) {
+		release_resource(chip->res_bm);
+		kfree_nocheck(chip->res_bm);
 	}
 	if (chip->irq >= 0)
 		free_irq(chip->irq, (void *)chip);
@@ -1146,6 +1907,7 @@
 static void intel8x0_resume(intel8x0_t *chip)
 {
 	snd_card_t *card = chip->card;
+	int i;
 
 	snd_power_lock(card);
 	if (card->power_state == SNDRV_CTL_POWER_D0)
@@ -1153,7 +1915,9 @@
 
 	pci_enable_device(chip->pci);
 	snd_intel8x0_chip_init(chip);
-	snd_ac97_resume(chip->ac97);
+	for (i = 0; i < 3; i++)
+		if (chip->ac97[i])
+			snd_ac97_resume(chip->ac97[i]);
 
 	chip->in_suspend = 0;
 	snd_power_change_state(card, SNDRV_CTL_POWER_D0);
@@ -1214,12 +1978,13 @@
 static void __devinit intel8x0_measure_ac97_clock(intel8x0_t *chip)
 {
 	snd_pcm_substream_t *subs;
+	ichdev_t *ichdev;
 	unsigned long port;
 	unsigned long pos, t;
 	unsigned long flags;
 	struct timeval start_time, stop_time;
 
-	if (chip->ac97->clock != 48000)
+	if (chip->ac97[0]->clock != 48000)
 		return; /* specified in module option */
 
 	subs = chip->pcm->streams[0].substream;
@@ -1227,19 +1992,26 @@
 		snd_printk("no playback buffer allocated - aborting measure ac97 clock\n");
 		return;
 	}
-	chip->playback.physbuf = subs->dma_addr;
-	chip->playback.size = chip->playback.fragsize = INTEL8X0_TESTBUF_SIZE;
-	chip->playback.substream = NULL; /* don't process interrupts */
+	ichdev = &chip->ichd[ICHD_PCMOUT];
+	ichdev->physbuf = subs->dma_addr;
+	ichdev->size = chip->ichd[ICHD_PCMOUT].fragsize = INTEL8X0_TESTBUF_SIZE;
+	ichdev->substream = NULL; /* don't process interrupts */
 
 	/* set rate */
-	if (snd_ac97_set_rate(chip->ac97, AC97_PCM_FRONT_DAC_RATE, 48000) < 0) {
-		snd_printk(KERN_ERR "cannot set ac97 rate: clock = %d\n", chip->ac97->clock);
+	if (snd_ac97_set_rate(chip->ac97[0], AC97_PCM_FRONT_DAC_RATE, 48000) < 0) {
+		snd_printk(KERN_ERR "cannot set ac97 rate: clock = %d\n", chip->ac97[0]->clock);
 		return;
 	}
-	snd_intel8x0_setup_periods(chip, &chip->playback);
-	port = chip->bmport + chip->playback.reg_offset;
+	snd_intel8x0_setup_periods(chip, ichdev);
+	port = ichdev->reg_offset;
 	spin_lock_irqsave(&chip->reg_lock, flags);
-	outb(ICH_IOCE | ICH_STARTBM, port + ICH_REG_PI_CR); /* trigger */
+	/* trigger */
+	if (chip->device_type != DEVICE_ALI)
+		iputbyte(chip, port + ICH_REG_OFF_CR, ICH_IOCE | ICH_STARTBM);
+	else {
+		iputbyte(chip, port + ICH_REG_OFF_CR, ICH_IOCE);
+		iputbyte(chip, ICHREG(ALI_DMACR), 1 << ichdev->ali_slot);
+	}
 	do_gettimeofday(&start_time);
 	spin_unlock_irqrestore(&chip->reg_lock, flags);
 #if 0
@@ -1251,18 +2023,21 @@
 #endif
 	spin_lock_irqsave(&chip->reg_lock, flags);
 	/* check the position */
-	pos = chip->playback.fragsize1;
+	pos = ichdev->fragsize1;
 	if (chip->device_type == DEVICE_SIS)
-		pos -= inw(ICHREG2(chip,chip->reg_po_picb));
+		pos -= igetword(chip, ichdev->reg_offset + ichdev->roff_picb);
 	else
-		pos -= inw(ICHREG2(chip,chip->reg_po_picb)) << 1;
-	pos += chip->playback.position;
+		pos -= igetword(chip, ichdev->reg_offset + ichdev->roff_picb) << 1;
+	pos += ichdev->position;
 	do_gettimeofday(&stop_time);
-	outb(0, port + ICH_REG_PI_CR); /* stop */
+	/* stop */
+	if (chip->device_type == DEVICE_ALI)
+		iputbyte(chip, ICHREG(ALI_DMACR), 1 << (ichdev->ali_slot + 8));
+	iputbyte(chip, port + ICH_REG_OFF_CR, 0);
 	/* reset whole DMA things */
-	while (!(inb(port + chip->reg_pi_sr) & ICH_DCH))
+	while (!(igetbyte(chip, port + ichdev->roff_sr) & ICH_DCH))
 		;
-	outb(ICH_RESETREGS, port + ICH_REG_PI_CR);
+	iputbyte(chip, port + ICH_REG_OFF_CR, ICH_RESETREGS);
 	spin_unlock_irqrestore(&chip->reg_lock, flags);
 
 	t = stop_time.tv_sec - start_time.tv_sec;
@@ -1282,8 +2057,60 @@
 		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);
+		chip->ac97[0]->clock = (chip->ac97[0]->clock * 48000) / pos;
+	printk(KERN_INFO "intel8x0: clocking to %d\n", chip->ac97[0]->clock);
+}
+
+static void snd_intel8x0_proc_read(snd_info_entry_t * entry,
+				   snd_info_buffer_t * buffer)
+{
+	intel8x0_t *chip = snd_magic_cast(intel8x0_t, entry->private_data, return);
+	unsigned int tmp;
+
+	snd_iprintf(buffer, "Intel8x0\n\n");
+	if (chip->device_type == DEVICE_ALI)
+		return;
+	tmp = igetdword(chip, ICHREG(GLOB_STA));
+	snd_iprintf(buffer, "Global control        : 0x%08x\n", igetdword(chip, ICHREG(GLOB_CNT)));
+	snd_iprintf(buffer, "Global status         : 0x%08x\n", tmp);
+	if (chip->device_type == DEVICE_INTEL_ICH4)
+		snd_iprintf(buffer, "SDM                   : 0x%08x\n", igetdword(chip, ICHREG(SDM)));
+	snd_iprintf(buffer, "AC'97 codecs ready    :%s%s%s%s\n",
+			tmp & ICH_PCR ? " primary" : "",
+			tmp & ICH_SCR ? " secondary" : "",
+			tmp & ICH_TCR ? " tertiary" : "",
+			(tmp & (ICH_PCR | ICH_SCR | ICH_TCR)) == 0 ? " none" : "");
+	if (chip->device_type == DEVICE_INTEL_ICH4)
+		snd_iprintf(buffer, "AC'97 codecs SDIN     : %i %i %i\n",
+			chip->ac97_sdin[0],
+			chip->ac97_sdin[1],
+			chip->ac97_sdin[2]);
+}
+
+static void __devinit snd_intel8x0_proc_init(intel8x0_t * chip)
+{
+	snd_info_entry_t *entry;
+
+	if ((entry = snd_info_create_card_entry(chip->card, "intel8x0", chip->card->proc_root)) != NULL) {
+		entry->content = SNDRV_INFO_CONTENT_TEXT;
+		entry->private_data = chip;
+		entry->mode = S_IFREG | S_IRUGO | S_IWUSR;
+		entry->c.text.read_size = 2048;
+		entry->c.text.read = snd_intel8x0_proc_read;
+		if (snd_info_register(entry) < 0) {
+			snd_info_free_entry(entry);
+			entry = NULL;
+		}
+	}
+	chip->proc_entry = entry;
+}
+
+static void snd_intel8x0_proc_done(intel8x0_t * chip)
+{
+	if (chip->proc_entry) {
+		snd_info_unregister(chip->proc_entry);
+		chip->proc_entry = NULL;
+	}
 }
 
 static int snd_intel8x0_dev_free(snd_device_t *device)
@@ -1298,9 +2125,21 @@
 					 intel8x0_t ** r_intel8x0)
 {
 	intel8x0_t *chip;
-	int err;
+	int err, i;
+	unsigned int int_sta_masks;
+	ichdev_t *ichdev;
 	static snd_device_ops_t ops = {
-		dev_free:	snd_intel8x0_dev_free,
+		.dev_free =	snd_intel8x0_dev_free,
+	};
+	static u32 intel_int_sta_masks[6] = {
+		ICH_PIINT, ICH_POINT, ICH_MCINT, ICH_M2INT, ICH_P2INT, ICH_SPINT
+	};
+	static u32 ali_int_sta_masks[6] = {
+		ALI_INT_PCMIN, ALI_INT_PCMOUT, ALI_INT_MICIN,
+		ALI_INT_CODECSPDIFOUT, ALI_INT_SPDIFIN, ALI_INT_SPDIFOUT
+	};
+	static u32 ali_reg_offsets[6] = {
+		0x40, 0x50, 0x60, 0x70, 0xa0, 0xb0
 	};
 
 	*r_intel8x0 = NULL;
@@ -1317,19 +2156,52 @@
 	chip->card = card;
 	chip->pci = pci;
 	chip->irq = -1;
-	chip->port = pci_resource_start(pci, 0);
+	snd_intel8x0_proc_init(chip);
 	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(chip->ctrl_name, "%s - Controller", card->shortname);
-	chip->bmport = pci_resource_start(pci, 1);
-	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;
+	if (pci_resource_flags(pci, 2) & IORESOURCE_MEM) {	/* ICH4 and Nforce */
+		chip->mmio = 1;
+		chip->addr = pci_resource_start(pci, 2);
+		if ((chip->res = request_mem_region(chip->addr, 512, chip->ac97_name)) == NULL) {
+			snd_intel8x0_free(chip);
+			snd_printk("unable to grab I/O memory 0x%lx-0x%lx\n", chip->addr, chip->addr + 512 - 1);
+			return -EBUSY;
+		}
+		chip->remap_addr = (unsigned long) ioremap_nocache(chip->addr, 512);
+		if (chip->remap_addr == 0) {
+			snd_intel8x0_free(chip);
+			snd_printk("AC'97 space ioremap problem\n");
+			return -EIO;
+		}
+	} else {
+		chip->addr = pci_resource_start(pci, 0);
+		if ((chip->res = request_region(chip->addr, 256, chip->ac97_name)) == NULL) {
+			snd_intel8x0_free(chip);
+			snd_printk("unable to grab ports 0x%lx-0x%lx\n", chip->addr, chip->addr + 256 - 1);
+			return -EBUSY;
+		}
+	}
+	if (pci_resource_flags(pci, 3) & IORESOURCE_MEM) {	/* ICH4 */
+		chip->bm_mmio = 1;
+		chip->bmaddr = pci_resource_start(pci, 3);
+		if ((chip->res_bm = request_mem_region(chip->bmaddr, 256, chip->ctrl_name)) == NULL) {
+			snd_intel8x0_free(chip);
+			snd_printk("unable to grab I/O memory 0x%lx-0x%lx\n", chip->bmaddr, chip->bmaddr + 512 - 1);
+			return -EBUSY;
+		}
+		chip->remap_bmaddr = (unsigned long) ioremap_nocache(chip->bmaddr, 256);
+		if (chip->remap_bmaddr == 0) {
+			snd_intel8x0_free(chip);
+			snd_printk("Controller space ioremap problem\n");
+			return -EIO;
+		}
+	} else {
+		chip->bmaddr = pci_resource_start(pci, 1);
+		if ((chip->res_bm = request_region(chip->bmaddr, 64, chip->ctrl_name)) == NULL) {
+			snd_intel8x0_free(chip);
+			snd_printk("unable to grab ports 0x%lx-0x%lx\n", chip->bmaddr, chip->bmaddr + 64 - 1);
+			return -EBUSY;
+		}
 	}
 	if (request_irq(pci->irq, snd_intel8x0_interrupt, SA_INTERRUPT|SA_SHIRQ, card->shortname, (void *)chip)) {
 		snd_intel8x0_free(chip);
@@ -1341,27 +2213,36 @@
 	synchronize_irq(chip->irq);
 
 	/* initialize offsets */
-	chip->reg_pi_sr = ICH_REG_PI_SR;
-	chip->reg_pi_picb = ICH_REG_PI_PICB;
-	chip->reg_po_sr = ICH_REG_PO_SR;
-	chip->reg_po_picb = ICH_REG_PO_PICB;
-	chip->reg_mc_sr = ICH_REG_MC_SR;
-	chip->reg_mc_picb = ICH_REG_MC_PICB;
-	if (device_type == DEVICE_SIS) {
-		chip->reg_pi_sr = ICH_REG_PI_PICB;
-		chip->reg_pi_picb = ICH_REG_PI_SR;
-		chip->reg_po_sr = ICH_REG_PO_PICB;
-		chip->reg_po_picb = ICH_REG_PO_SR;
-		chip->reg_mc_sr = ICH_REG_MC_PICB;
-		chip->reg_mc_picb = ICH_REG_MC_SR;
-	}
-	chip->playback.reg_offset = 0x10;
-	chip->capture.reg_offset = 0;
-	chip->capture_mic.reg_offset = 0x20;
+	for (i = 0; i <= ICHD_LAST; i++) {
+		ichdev = &chip->ichd[i];
+		ichdev->ichd = i;
+		ichdev->reg_offset = i * 0x10 + (i >= 0x30 ? 0x10 : 0);
+		ichdev->roff_sr = ICH_REG_OFF_SR;
+		ichdev->roff_picb = ICH_REG_OFF_PICB;
+		ichdev->int_sta_mask = device_type == DEVICE_ALI ? ali_int_sta_masks[i] : intel_int_sta_masks[i];
+	}
+	switch (device_type) {
+	case DEVICE_SIS:
+		for (i = 0; i <= ICHD_LAST; i++) {
+			ichdev = &chip->ichd[i];
+			ichdev->roff_sr = ICH_REG_OFF_PICB;
+			ichdev->roff_picb = ICH_REG_OFF_SR;
+		}
+		break;
+	case DEVICE_ALI:
+		for (i = 0; i <= ALID_LAST; i++) {
+			ichdev = &chip->ichd[i];
+			ichdev->reg_offset = ali_reg_offsets[i];
+			ichdev->ali_slot = i + 1;	/* is this right for last three devices? --jk */
+		}
+	}
 
 	/* allocate buffer descriptor lists */
 	/* the start of each lists must be aligned to 8 bytes */
-	chip->bdbars = (u32 *)snd_malloc_pci_pages(pci, 3 * sizeof(unsigned int) * ICH_MAX_FRAGS * 2, &chip->bdbars_addr);
+	chip->bdbars_count = 3;
+	if (device_type == DEVICE_INTEL_ICH4 || device_type == DEVICE_ALI)
+		chip->bdbars_count = 6;
+	chip->bdbars = (u32 *)snd_malloc_pci_pages(pci, chip->bdbars_count * sizeof(unsigned int) * ICH_MAX_FRAGS * 2, &chip->bdbars_addr);
 	if (chip->bdbars == NULL) {
 		snd_intel8x0_free(chip);
 		return -ENOMEM;
@@ -1376,12 +2257,15 @@
 		return -ENOMEM;
 	}
 #endif
-	chip->playback.bdbar = chip->bdbars; /* crop to 32bit */
-	chip->playback.bdbar_addr = (unsigned int)chip->bdbars_addr;
-	chip->capture.bdbar = chip->playback.bdbar + ICH_MAX_FRAGS * 2;
-	chip->capture.bdbar_addr = chip->playback.bdbar_addr + sizeof(u32) * ICH_MAX_FRAGS * 2;
-	chip->capture_mic.bdbar = chip->capture.bdbar + ICH_MAX_FRAGS * 2;
-	chip->capture_mic.bdbar_addr = chip->capture.bdbar_addr + sizeof(u32) * ICH_MAX_FRAGS * 2;
+	int_sta_masks = 0;
+	for (i = 0; i < chip->bdbars_count; i++) {
+		ichdev = &chip->ichd[i];
+		ichdev->bdbar = chip->bdbars + (i * ICH_MAX_FRAGS * 2);
+		ichdev->bdbar_addr = chip->bdbars_addr + (i * sizeof(u32) * ICH_MAX_FRAGS * 2);
+		int_sta_masks |= ichdev->int_sta_mask;
+	}
+	chip->int_sta_reg = device_type == DEVICE_ALI ? ICH_REG_ALI_INTERRUPTSR : ICH_REG_GLOB_STA;
+	chip->int_sta_mask = int_sta_masks;
 
 	if ((err = snd_intel8x0_chip_init(chip)) < 0) {
 		snd_intel8x0_free(chip);
@@ -1416,6 +2300,7 @@
 	{ PCI_DEVICE_ID_NVIDIA_MCP_AUDIO, "NVidia NForce" },
 	{ 0x746d, "AMD AMD8111" },
 	{ 0x7445, "AMD AMD768" },
+	{ 0x5455, "ALi M5455" },
 	{ 0, 0 },
 };
 
@@ -1461,15 +2346,52 @@
 		snd_card_free(card);
 		return err;
 	}
-	if (chip->ac97->ext_id & 0x0008) {	/* MIC VRM */
+	/* activate MIC PCM only when associated AC'97 codec */
+	if (chip->ichd[ICHD_MIC].ac97) {
 		if ((err = snd_intel8x0_pcm_mic(chip, pcm_dev++, NULL)) < 0) {
 			snd_card_free(card);
 			return err;
 		}
 	}
+	if (chip->device_type == DEVICE_ALI) {
+		if ((err = snd_intel8x0_ali_spdif(chip, pcm_dev++, NULL)) < 0) {
+			snd_card_free(card);
+			return err;
+		}
+	}
+	/* activate AC'97 S/PDIF only when associated AC'97 codec */
+	if (chip->bdbars_count > 3) {
+		err = 0;
+		if (chip->device_type == DEVICE_ALI) {
+			if (chip->ichd[ALID_AC97SPDIFOUT].ac97)
+				err = snd_intel8x0_ali_ac97spdif(chip, pcm_dev++, NULL);
+		} else {
+			if (chip->ichd[ICHD_SPBAR].ac97)
+				err = snd_intel8x0_pcm_spdif(chip, pcm_dev++, NULL);
+		}
+		if (err < 0) {
+			snd_card_free(card);
+			return err;
+		}
+		if (chip->device_type != DEVICE_ALI) {
+			/* activate MIC2 only when associated AC'97 codec */
+			if (chip->ichd[ICHD_MIC2].ac97)
+				if ((err = snd_intel8x0_pcm_mic2(chip, pcm_dev++, NULL)) < 0) {
+					snd_card_free(card);
+					return err;
+				}
+			/* activate PCM2IN only when associated AC'97 codec */
+			if (chip->ichd[ICHD_PCM2IN].ac97)
+				if ((err = snd_intel8x0_pcm_capture2(chip, pcm_dev++, NULL)) < 0) {
+					snd_card_free(card);
+					return err;
+				}
+		}
+	}
+	
 	
 	if (snd_mpu_port[dev] == 0x300 || snd_mpu_port[dev] == 0x330) {
-		if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_CMIPCI,
+		if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_INTEL8X0,
 					       snd_mpu_port[dev], 0,
 					       -1, 0, &chip->rmidi)) < 0) {
 			printk(KERN_ERR "intel8x0: no UART401 device at 0x%x, skipping.\n", snd_mpu_port[dev]);
@@ -1479,7 +2401,7 @@
 		snd_mpu_port[dev] = 0;
 
 	sprintf(card->longname, "%s at 0x%lx, irq %i",
-		card->shortname, chip->port, chip->irq);
+		card->shortname, chip->addr, chip->irq);
 
 	if (! snd_ac97_clock[dev])
 		intel8x0_measure_ac97_clock(chip);
@@ -1502,7 +2424,7 @@
 }
 
 static struct pci_driver driver = {
-	name: "Intel ICH",
+	.name = "Intel ICH",
 	id_table: snd_intel8x0_ids,
 	probe: snd_intel8x0_probe,
 	remove: __devexit_p(snd_intel8x0_remove),
@@ -1564,9 +2486,9 @@
 };
 
 static struct pci_driver joystick_driver = {
-	name: "Intel ICH Joystick",
-	id_table: snd_intel8x0_joystick_ids,
-	probe: snd_intel8x0_joystick_probe,
+	.name = "Intel ICH Joystick",
+	.id_table = snd_intel8x0_joystick_ids,
+	.probe = snd_intel8x0_joystick_probe,
 };
 #endif
 
diff -Nru a/sound/pci/korg1212/korg1212.c b/sound/pci/korg1212/korg1212.c
--- a/sound/pci/korg1212/korg1212.c	Mon Oct  7 11:25:25 2002
+++ b/sound/pci/korg1212/korg1212.c	Mon Oct  7 11:25:25 2002
@@ -1162,42 +1162,42 @@
 
 static snd_pcm_hardware_t snd_korg1212_playback_info =
 {
-        info:                (SNDRV_PCM_INFO_MMAP |
+	.info =               (SNDRV_PCM_INFO_MMAP |
                               SNDRV_PCM_INFO_MMAP_VALID |
                               SNDRV_PCM_INFO_INTERLEAVED),
-        formats:             SNDRV_PCM_FMTBIT_S16_LE,
-        rates:               (SNDRV_PCM_RATE_44100 |
+	.formats =	      SNDRV_PCM_FMTBIT_S16_LE,
+        .rates =              (SNDRV_PCM_RATE_44100 |
                               SNDRV_PCM_RATE_48000),
-        rate_min:            44100,
-        rate_max:            48000,
-        channels_min:        K1212_CHANNELS,
-        channels_max:        K1212_CHANNELS,
-        buffer_bytes_max:    K1212_BUF_SIZE,
-        period_bytes_min:    K1212_PERIOD_BYTES,
-        period_bytes_max:    K1212_PERIOD_BYTES,
-        periods_min:         K1212_PERIODS,
-        periods_max:         K1212_PERIODS,
-        fifo_size:           0,
+        .rate_min =           44100,
+        .rate_max =           48000,
+        .channels_min =       K1212_CHANNELS,
+        .channels_max =       K1212_CHANNELS,
+        .buffer_bytes_max =   K1212_BUF_SIZE,
+        .period_bytes_min =   K1212_PERIOD_BYTES,
+        .period_bytes_max =   K1212_PERIOD_BYTES,
+        .periods_min =        K1212_PERIODS,
+        .periods_max =        K1212_PERIODS,
+        .fifo_size =          0,
 };
 
 static snd_pcm_hardware_t snd_korg1212_capture_info =
 {
-        info:                (SNDRV_PCM_INFO_MMAP |
+        .info =               (SNDRV_PCM_INFO_MMAP |
                               SNDRV_PCM_INFO_MMAP_VALID |
                               SNDRV_PCM_INFO_INTERLEAVED),
-        formats:             SNDRV_PCM_FMTBIT_S16_LE,
-        rates:               (SNDRV_PCM_RATE_44100 |
+        .formats =	      SNDRV_PCM_FMTBIT_S16_LE,
+        .rates =	      (SNDRV_PCM_RATE_44100 |
                               SNDRV_PCM_RATE_48000),
-        rate_min:            44100,
-        rate_max:            48000,
-        channels_min:        K1212_CHANNELS,
-        channels_max:        K1212_CHANNELS,
-        buffer_bytes_max:    K1212_BUF_SIZE,
-        period_bytes_min:    K1212_PERIOD_BYTES,
-        period_bytes_max:    K1212_PERIOD_BYTES,
-        periods_min:         K1212_PERIODS,
-        periods_max:         K1212_PERIODS,
-        fifo_size:           0,
+        .rate_min =           44100,
+        .rate_max =           48000,
+        .channels_min =       K1212_CHANNELS,
+        .channels_max =       K1212_CHANNELS,
+        .buffer_bytes_max =   K1212_BUF_SIZE,
+        .period_bytes_min =   K1212_PERIOD_BYTES,
+        .period_bytes_max =   K1212_PERIOD_BYTES,
+        .periods_min =        K1212_PERIODS,
+        .periods_max =        K1212_PERIODS,
+        .fifo_size =          0,
 };
 
 static void snd_korg1212_free_pcm(snd_pcm_t *pcm)
@@ -1216,9 +1216,9 @@
 #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
+        .count = PERIOD_BYTES,
+        .list = period_bytes,
+        .mask = 0
 };
 
 static int snd_korg1212_playback_open(snd_pcm_substream_t *substream)
@@ -1504,26 +1504,26 @@
 }
 
 static snd_pcm_ops_t snd_korg1212_playback_ops = {
-        open:           snd_korg1212_playback_open,
-        close:          snd_korg1212_playback_close,
-        ioctl:          snd_korg1212_ioctl,
-        hw_params:      snd_korg1212_hw_params,
-        prepare:        snd_korg1212_prepare,
-        trigger:        snd_korg1212_trigger,
-        pointer:        snd_korg1212_pointer,
-        copy:           snd_korg1212_playback_copy,
-        silence:        snd_korg1212_playback_silence,
+        .open =		snd_korg1212_playback_open,
+        .close =	snd_korg1212_playback_close,
+        .ioctl =	snd_korg1212_ioctl,
+        .hw_params =	snd_korg1212_hw_params,
+        .prepare =	snd_korg1212_prepare,
+        .trigger =	snd_korg1212_trigger,
+        .pointer =	snd_korg1212_pointer,
+        .copy =		snd_korg1212_playback_copy,
+        .silence =	snd_korg1212_playback_silence,
 };
 
 static snd_pcm_ops_t snd_korg1212_capture_ops = {
-	open:		snd_korg1212_capture_open,
-	close:		snd_korg1212_capture_close,
-	ioctl:		snd_korg1212_ioctl,
-	hw_params:	snd_korg1212_hw_params,
-	prepare:	snd_korg1212_prepare,
-	trigger:	snd_korg1212_trigger,
-	pointer:	snd_korg1212_pointer,
-	copy:		snd_korg1212_capture_copy,
+	.open =		snd_korg1212_capture_open,
+	.close =	snd_korg1212_capture_close,
+	.ioctl =	snd_korg1212_ioctl,
+	.hw_params =	snd_korg1212_hw_params,
+	.prepare =	snd_korg1212_prepare,
+	.trigger =	snd_korg1212_trigger,
+	.pointer =	snd_korg1212_pointer,
+	.copy =		snd_korg1212_capture_copy,
 };
 
 /*
@@ -1803,31 +1803,31 @@
 
 #define MON_MIXER(ord,c_name)									\
         {											\
-                access:		SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_WRITE,	\
-                iface:          SNDRV_CTL_ELEM_IFACE_MIXER,					\
-                name:		c_name " Monitor Volume",						\
-                info:		snd_korg1212_control_volume_info,				\
-                get:		snd_korg1212_control_volume_get,				\
-                put:		snd_korg1212_control_volume_put,				\
-		private_value:	ord,								\
+                .access =	SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_WRITE,	\
+                .iface =        SNDRV_CTL_ELEM_IFACE_MIXER,					\
+                .name =		c_name " Monitor Volume",					\
+                .info =		snd_korg1212_control_volume_info,				\
+                .get =		snd_korg1212_control_volume_get,				\
+                .put =		snd_korg1212_control_volume_put,				\
+		.private_value = ord,								\
         },                                                                                      \
         {											\
-                access:		SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_WRITE,	\
-                iface:          SNDRV_CTL_ELEM_IFACE_MIXER,					\
-                name:		c_name " Monitor Route",						\
-                info:		snd_korg1212_control_route_info,				\
-                get:		snd_korg1212_control_route_get,					\
-                put:		snd_korg1212_control_route_put,					\
-		private_value:	ord,								\
+                .access =	SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_WRITE,	\
+                .iface =        SNDRV_CTL_ELEM_IFACE_MIXER,					\
+                .name =		c_name " Monitor Route",					\
+                .info =		snd_korg1212_control_route_info,				\
+                .get =		snd_korg1212_control_route_get,					\
+                .put =		snd_korg1212_control_route_put,					\
+		.private_value = ord,								\
         },                                                                                      \
         {											\
-                access:		SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_WRITE,	\
-                iface:          SNDRV_CTL_ELEM_IFACE_PCM,					\
-                name:		c_name " Monitor Phase Invert",						\
-                info:		snd_korg1212_control_phase_info,				\
-                get:		snd_korg1212_control_phase_get,					\
-                put:		snd_korg1212_control_phase_put,					\
-		private_value:	ord,								\
+                .access =	SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_WRITE,	\
+                .iface =        SNDRV_CTL_ELEM_IFACE_PCM,					\
+                .name =		c_name " Monitor Phase Invert",					\
+                .info =		snd_korg1212_control_phase_info,				\
+                .get =		snd_korg1212_control_phase_get,					\
+                .put =		snd_korg1212_control_phase_put,					\
+		.private_value = ord,								\
         }
 
 static snd_kcontrol_new_t snd_korg1212_controls[] = {
@@ -1836,20 +1836,20 @@
         MON_MIXER(0, "ADAT-1"), MON_MIXER(1, "ADAT-2"), MON_MIXER(2, "ADAT-3"), MON_MIXER(3, "ADAT-4"),
         MON_MIXER(4, "ADAT-5"), MON_MIXER(5, "ADAT-6"), MON_MIXER(6, "ADAT-7"), MON_MIXER(7, "ADAT-8"),
 	{
-                access:		SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_WRITE,
-                iface:          SNDRV_CTL_ELEM_IFACE_PCM,
-                name:		"Sync Source",
-                info:		snd_korg1212_control_sync_info,
-                get:		snd_korg1212_control_sync_get,
-                put:		snd_korg1212_control_sync_put,
+                .access =	SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_WRITE,
+                .iface =        SNDRV_CTL_ELEM_IFACE_PCM,
+                .name =		"Sync Source",
+                .info =		snd_korg1212_control_sync_info,
+                .get =		snd_korg1212_control_sync_get,
+                .put =		snd_korg1212_control_sync_put,
         },
         {
-                access:		SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_WRITE,
-                iface:          SNDRV_CTL_ELEM_IFACE_MIXER,
-                name:		"ADC Attenuation",
-                info:		snd_korg1212_control_analog_info,
-                get:		snd_korg1212_control_analog_get,
-                put:		snd_korg1212_control_analog_put,
+                .access =	SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_WRITE,
+                .iface =        SNDRV_CTL_ELEM_IFACE_MIXER,
+                .name =		"ADC Attenuation",
+                .info =		snd_korg1212_control_analog_info,
+                .get =		snd_korg1212_control_analog_get,
+                .put =		snd_korg1212_control_analog_put,
         }
 };
 
@@ -2305,10 +2305,10 @@
 }
 
 static struct pci_driver driver = {
-	name: "korg1212",
-	id_table: snd_korg1212_ids,
-	probe: snd_korg1212_probe,
-	remove: __devexit_p(snd_korg1212_remove),
+	.name = "korg1212",
+	.id_table = snd_korg1212_ids,
+	.probe = snd_korg1212_probe,
+	.remove = __devexit_p(snd_korg1212_remove),
 };
 
 static int __init alsa_card_korg1212_init(void)
diff -Nru a/sound/pci/maestro3.c b/sound/pci/maestro3.c
--- a/sound/pci/maestro3.c	Mon Oct  7 11:25:35 2002
+++ b/sound/pci/maestro3.c	Mon Oct  7 11:25:35 2002
@@ -1,5 +1,5 @@
 /*
- * Driver for ESS Maestro3/Allegro soundcards.
+ * Driver for ESS Maestro3/Allegro (ES1988) soundcards.
  * Copyright (c) 2000 by Zach Brown <zab@zabbo.net>
  *                       Takashi Iwai <tiwai@suse.de>
  *
@@ -52,6 +52,7 @@
 MODULE_LICENSE("GPL");
 MODULE_CLASSES("{sound}");
 MODULE_DEVICES("{{ESS,Maestro3 PCI},"
+		"{ESS,ES1988},"
 		"{ESS,Allegro PCI},"
 		"{ESS,Allegro-1 PCI},"
 	        "{ESS,Canyon3D-2/LE PCI}}");
@@ -776,6 +777,15 @@
 #define chip_t m3_t
 
 
+/* quirk lists */
+struct m3_quirk {
+	const char *name;	/* device name */
+	u16 vendor, device;	/* subsystem ids */
+	int amp_gpio;		/* gpio pin #  for external amp, -1 = default */
+	int irda_workaround;	/* non-zero if avoid to touch 0x10 on GPIO_DIRECTION
+				   (e.g. for IrDA on Dell Inspirons) */
+};
+
 struct m3_list {
 	int curlen;
 	int mem_addr;
@@ -825,9 +835,7 @@
 	snd_pcm_t *pcm;
 
 	struct pci_dev *pci;
-	/* pci_dev in 2.2 kernel doesn't have them, so we keep them private */
-	u16 subsystem_vendor;
-	u16 subsystem_device;
+	struct m3_quirk *quirk;
 
 	int dacs_active;
 	int timer_users;
@@ -908,6 +916,43 @@
 
 MODULE_DEVICE_TABLE(pci, snd_m3_ids);
 
+static struct m3_quirk m3_quirk_list[] = {
+	/* panasonic CF-28 "toughbook" */
+	{
+		.name = "Panasonic CF-28",
+		.vendor = 0x10f7,
+		.device = 0x833e,
+		.amp_gpio = 0x0d,
+	},
+	/* panasonic CF-72 "toughbook" */
+	{
+		.name = "Panasonic CF-72",
+		.vendor = 0x10f7,
+		.device = 0x833d,
+		.amp_gpio = 0x0d,
+	},
+	/* Dell Inspiron 4000 */
+	{
+		.name = "Dell Inspiron 4000",
+		.vendor = 0x1028,
+		.device = 0x00b0,
+		.amp_gpio = -1,
+		.irda_workaround = 1,
+	},
+	/* Dell Inspiron 8000 */
+	{
+		.name = "Dell Insprion 8000",
+		.vendor = 0x1028,
+		.device = 0x00a4,
+		.amp_gpio = -1,
+		.irda_workaround = 1,
+	},
+	/* FIXME: Inspiron 8100 and 8200 ids should be here, too */
+	/* END */
+	{ 0 }
+};
+
+
 /*
  * lowlevel functions
  */
@@ -1547,44 +1592,44 @@
 
 static snd_pcm_hardware_t snd_m3_playback =
 {
-	info:			(SNDRV_PCM_INFO_MMAP |
+	.info =			(SNDRV_PCM_INFO_MMAP |
 				 SNDRV_PCM_INFO_INTERLEAVED |
 				 SNDRV_PCM_INFO_MMAP_VALID |
 				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
 				 /*SNDRV_PCM_INFO_PAUSE |*/
 				 SNDRV_PCM_INFO_RESUME),
-	formats:		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
-	rates:			SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
-	rate_min:		8000,
-	rate_max:		48000,
-	channels_min:		1,
-	channels_max:		2,
-	buffer_bytes_max:	(512*1024),
-	period_bytes_min:	64,
-	period_bytes_max:	(512*1024),
-	periods_min:		1,
-	periods_max:		1024,
+	.formats =		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
+	.rates =		SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+	.rate_min =		8000,
+	.rate_max =		48000,
+	.channels_min =		1,
+	.channels_max =		2,
+	.buffer_bytes_max =	(512*1024),
+	.period_bytes_min =	64,
+	.period_bytes_max =	(512*1024),
+	.periods_min =		1,
+	.periods_max =		1024,
 };
 
 static snd_pcm_hardware_t snd_m3_capture =
 {
-	info:			(SNDRV_PCM_INFO_MMAP |
+	.info =			(SNDRV_PCM_INFO_MMAP |
 				 SNDRV_PCM_INFO_INTERLEAVED |
 				 SNDRV_PCM_INFO_MMAP_VALID |
 				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
 				 /*SNDRV_PCM_INFO_PAUSE |*/
 				 SNDRV_PCM_INFO_RESUME),
-	formats:		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
-	rates:			SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
-	rate_min:		8000,
-	rate_max:		48000,
-	channels_min:		1,
-	channels_max:		2,
-	buffer_bytes_max:	(512*1024),
-	period_bytes_min:	64,
-	period_bytes_max:	(512*1024),
-	periods_min:		1,
-	periods_max:		1024,
+	.formats =		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
+	.rates =		SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+	.rate_min =		8000,
+	.rate_max =		48000,
+	.channels_min =		1,
+	.channels_max =		2,
+	.buffer_bytes_max =	(512*1024),
+	.period_bytes_min =	64,
+	.period_bytes_max =	(512*1024),
+	.periods_min =		1,
+	.periods_max =		1024,
 };
 
 
@@ -1703,25 +1748,25 @@
  */
 
 static snd_pcm_ops_t snd_m3_playback_ops = {
-	open:		snd_m3_playback_open,
-	close:		snd_m3_playback_close,
-	ioctl:		snd_pcm_lib_ioctl,
-	hw_params:	snd_m3_pcm_hw_params,
-	hw_free:	snd_m3_pcm_hw_free,
-	prepare:	snd_m3_pcm_prepare,
-	trigger:	snd_m3_pcm_trigger,
-	pointer:	snd_m3_pcm_pointer,
+	.open =		snd_m3_playback_open,
+	.close =	snd_m3_playback_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	snd_m3_pcm_hw_params,
+	.hw_free =	snd_m3_pcm_hw_free,
+	.prepare =	snd_m3_pcm_prepare,
+	.trigger =	snd_m3_pcm_trigger,
+	.pointer =	snd_m3_pcm_pointer,
 };
 
 static snd_pcm_ops_t snd_m3_capture_ops = {
-	open:		snd_m3_capture_open,
-	close:		snd_m3_capture_close,
-	ioctl:		snd_pcm_lib_ioctl,
-	hw_params:	snd_m3_pcm_hw_params,
-	hw_free:	snd_m3_pcm_hw_free,
-	prepare:	snd_m3_pcm_prepare,
-	trigger:	snd_m3_pcm_trigger,
-	pointer:	snd_m3_pcm_pointer,
+	.open =		snd_m3_capture_open,
+	.close =	snd_m3_capture_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	snd_m3_pcm_hw_params,
+	.hw_free =	snd_m3_pcm_hw_free,
+	.prepare =	snd_m3_pcm_prepare,
+	.trigger =	snd_m3_pcm_trigger,
+	.pointer =	snd_m3_pcm_pointer,
 };
 
 static int __devinit
@@ -1859,10 +1904,7 @@
 
 	for (i = 0; i < 5; i++) {
 		dir = inw(io + GPIO_DIRECTION);
-		if (chip->subsystem_vendor == 0x1028 &&
-		    chip->subsystem_device == 0x00b0) /* Dell Inspiron 4000 */
-			; /* seems conflicting with IrDA */
-		else
+		if (! chip->quirk || ! chip->quirk->irda_workaround)
 			dir |= 0x10; /* assuming pci bus master? */
 
 		snd_m3_remote_codec_config(io, 0);
@@ -2470,8 +2512,10 @@
 {
 	m3_t *chip;
 	int i, err;
+	struct m3_quirk *quirk;
+	u16 subsystem_vendor, subsystem_device;
 	static snd_device_ops_t ops = {
-		dev_free:	snd_m3_dev_free,
+		.dev_free =	snd_m3_dev_free,
 	};
 
 	*chip_ret = NULL;
@@ -2500,29 +2544,36 @@
 		break;
 	}
 
+	chip->card = card;
+	chip->pci = pci;
+	chip->irq = -1;
+
 #ifndef LINUX_2_2
-	chip->subsystem_vendor = pci->subsystem_vendor;
-	chip->subsystem_device = pci->subsystem_device;
+	subsystem_vendor = pci->subsystem_vendor;
+	subsystem_device = pci->subsystem_device;
 #else
-	pci_read_config_word(pci, PCI_SUBSYSTEM_VENDOR_ID, &chip->subsystem_vendor);
-	pci_read_config_word(pci, PCI_SUBSYSTEM_ID, &chip->subsystem_device);
+	pci_read_config_word(pci, PCI_SUBSYSTEM_VENDOR_ID, &subsystem_vendor);
+	pci_read_config_word(pci, PCI_SUBSYSTEM_ID, &subsystem_device);
 #endif
+	for (quirk = m3_quirk_list; quirk->vendor; quirk++) {
+		if (subsystem_vendor == quirk->vendor &&
+		    subsystem_device == quirk->device) {
+			printk(KERN_INFO "maestro3: enabled hack for '%s'\n", quirk->name);
+			chip->quirk = quirk;
+			break;
+		}
+	}
 
-	chip->card = card;
-	chip->pci = pci;
-	chip->irq = -1;
 	chip->external_amp = enable_amp;
 	if (amp_gpio >= 0 && amp_gpio <= 0x0f)
 		chip->amp_gpio = amp_gpio;
-	else if (chip->allegro_flag) {
-		/* panasonic CF-28 "toughbook" has different GPIO connection.. */
-		if (chip->subsystem_vendor == 0x10f7 &&
-		    chip->subsystem_device == 0x833e)
-			chip->amp_gpio = 0x0d;
-		else
-			chip->amp_gpio = GPO_EXT_AMP_ALLEGRO;
-	} else
-		chip->amp_gpio = GPO_EXT_AMP_M3; /* presumably this is for all 'maestro3's.. */
+	else if (chip->quirk && chip->quirk->amp_gpio >= 0)
+		chip->amp_gpio = chip->quirk->amp_gpio;
+	else if (chip->allegro_flag)
+		chip->amp_gpio = GPO_EXT_AMP_ALLEGRO;
+	else /* presumably this is for all 'maestro3's.. */
+		chip->amp_gpio = GPO_EXT_AMP_M3;
+
 	chip->num_substreams = NR_DSPS;
 	chip->substreams = kmalloc(sizeof(m3_dma_t) * chip->num_substreams, GFP_KERNEL);
 	if (chip->substreams == NULL) {
@@ -2668,13 +2719,13 @@
 }
 
 static struct pci_driver driver = {
-	name: "Maestro3",
-	id_table: snd_m3_ids,
-	probe: snd_m3_probe,
-	remove: __devexit_p(snd_m3_remove),
+	.name = "Maestro3",
+	.id_table = snd_m3_ids,
+	.probe = snd_m3_probe,
+	.remove = __devexit_p(snd_m3_remove),
 #ifdef CONFIG_PM
-	suspend: snd_m3_suspend,
-	resume: snd_m3_resume,
+	.suspend = snd_m3_suspend,
+	.resume = snd_m3_resume,
 #endif
 };
 	
diff -Nru a/sound/pci/nm256/nm256.c b/sound/pci/nm256/nm256.c
--- a/sound/pci/nm256/nm256.c	Mon Oct  7 11:25:37 2002
+++ b/sound/pci/nm256/nm256.c	Mon Oct  7 11:25:37 2002
@@ -410,9 +410,9 @@
 };
 #define NUM_SAMPLERATES (sizeof(samplerates) / sizeof(samplerates[0]))
 static snd_pcm_hw_constraint_list_t constraints_rates = {
-	count: NUM_SAMPLERATES, 
-	list: samplerates,
-	mask: 0,
+	.count = NUM_SAMPLERATES, 
+	.list = samplerates,
+	.mask = 0,
 };
 
 /*
@@ -751,46 +751,46 @@
  */
 static snd_pcm_hardware_t snd_nm256_playback =
 {
-	info:
+	.info =
 #ifdef __i386__
 				SNDRV_PCM_INFO_MMAP|SNDRV_PCM_INFO_MMAP_VALID|
 #endif
 				SNDRV_PCM_INFO_INTERLEAVED |
 				/*SNDRV_PCM_INFO_PAUSE |*/
 				SNDRV_PCM_INFO_RESUME,
-	formats:		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
-	rates:			SNDRV_PCM_RATE_KNOT/*24k*/ | SNDRV_PCM_RATE_8000_48000,
-	rate_min:		8000,
-	rate_max:		48000,
-	channels_min:		1,
-	channels_max:		2,
-	periods_min:		2,
-	periods_max:		1024,
-	buffer_bytes_max:	128 * 1024,
-	period_bytes_min:	256,
-	period_bytes_max:	128 * 1024,
+	.formats =		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
+	.rates =		SNDRV_PCM_RATE_KNOT/*24k*/ | SNDRV_PCM_RATE_8000_48000,
+	.rate_min =		8000,
+	.rate_max =		48000,
+	.channels_min =		1,
+	.channels_max =		2,
+	.periods_min =		2,
+	.periods_max =		1024,
+	.buffer_bytes_max =	128 * 1024,
+	.period_bytes_min =	256,
+	.period_bytes_max =	128 * 1024,
 };
 
 static snd_pcm_hardware_t snd_nm256_capture =
 {
-	info:
+	.info =
 #ifdef __i386__
 				SNDRV_PCM_INFO_MMAP|SNDRV_PCM_INFO_MMAP_VALID|
 #endif
 				SNDRV_PCM_INFO_INTERLEAVED |
 				/*SNDRV_PCM_INFO_PAUSE |*/
 				SNDRV_PCM_INFO_RESUME,
-	formats:		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
-	rates:			SNDRV_PCM_RATE_KNOT/*24k*/ | SNDRV_PCM_RATE_8000_48000,
-	rate_min:		8000,
-	rate_max:		48000,
-	channels_min:		1,
-	channels_max:		2,
-	periods_min:		2,
-	periods_max:		1024,
-	buffer_bytes_max:	128 * 1024,
-	period_bytes_min:	256,
-	period_bytes_max:	128 * 1024,
+	.formats =		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
+	.rates =		SNDRV_PCM_RATE_KNOT/*24k*/ | SNDRV_PCM_RATE_8000_48000,
+	.rate_min =		8000,
+	.rate_max =		48000,
+	.channels_min =		1,
+	.channels_max =		2,
+	.periods_min =		2,
+	.periods_max =		1024,
+	.buffer_bytes_max =	128 * 1024,
+	.period_bytes_min =	256,
+	.period_bytes_max =	128 * 1024,
 };
 
 
@@ -866,29 +866,29 @@
  * create a pcm instance
  */
 static snd_pcm_ops_t snd_nm256_playback_ops = {
-	open:		snd_nm256_playback_open,
-	close:		snd_nm256_playback_close,
-	ioctl:		snd_pcm_lib_ioctl,
-	hw_params:	snd_nm256_pcm_hw_params,
-	prepare:	snd_nm256_pcm_prepare,
-	trigger:	snd_nm256_playback_trigger,
-	pointer:	snd_nm256_playback_pointer,
+	.open =		snd_nm256_playback_open,
+	.close =	snd_nm256_playback_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	snd_nm256_pcm_hw_params,
+	.prepare =	snd_nm256_pcm_prepare,
+	.trigger =	snd_nm256_playback_trigger,
+	.pointer =	snd_nm256_playback_pointer,
 #ifndef __i386__
-	copy:		snd_nm256_playback_copy,
-	silence:	snd_nm256_playback_silence,
+	.copy =		snd_nm256_playback_copy,
+	.silence =	snd_nm256_playback_silence,
 #endif
 };
 
 static snd_pcm_ops_t snd_nm256_capture_ops = {
-	open:		snd_nm256_capture_open,
-	close:		snd_nm256_capture_close,
-	ioctl:		snd_pcm_lib_ioctl,
-	hw_params:	snd_nm256_pcm_hw_params,
-	prepare:	snd_nm256_pcm_prepare,
-	trigger:	snd_nm256_capture_trigger,
-	pointer:	snd_nm256_capture_pointer,
+	.open =		snd_nm256_capture_open,
+	.close =	snd_nm256_capture_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	snd_nm256_pcm_hw_params,
+	.prepare =	snd_nm256_pcm_prepare,
+	.trigger =	snd_nm256_capture_trigger,
+	.pointer =	snd_nm256_capture_pointer,
 #ifndef __i386__
-	copy:		snd_nm256_capture_copy,
+	.copy =		snd_nm256_capture_copy,
 #endif
 };
 
@@ -1395,7 +1395,7 @@
 	nm256_t *chip;
 	int err, pval;
 	static snd_device_ops_t ops = {
-		dev_free:	snd_nm256_dev_free,
+		.dev_free =	snd_nm256_dev_free,
 	};
 	u32 addr;
 
@@ -1646,13 +1646,13 @@
 
 
 static struct pci_driver driver = {
-	name: "NeoMagic 256",
-	id_table: snd_nm256_ids,
-	probe: snd_nm256_probe,
-	remove: __devexit_p(snd_nm256_remove),
+	.name = "NeoMagic 256",
+	.id_table = snd_nm256_ids,
+	.probe = snd_nm256_probe,
+	.remove = __devexit_p(snd_nm256_remove),
 #ifdef CONFIG_PM
-	suspend: snd_nm256_suspend,
-	resume: snd_nm256_resume,
+	.suspend = snd_nm256_suspend,
+	.resume = snd_nm256_resume,
 #endif
 };
 
diff -Nru a/sound/pci/rme32.c b/sound/pci/rme32.c
--- a/sound/pci/rme32.c	Mon Oct  7 11:25:39 2002
+++ b/sound/pci/rme32.c	Mon Oct  7 11:25:39 2002
@@ -284,7 +284,7 @@
  * Digital output capabilites (S/PDIF)
  */
 static snd_pcm_hardware_t snd_rme32_playback_spdif_info = {
-	info:		(SNDRV_PCM_INFO_MMAP |
+	.info =		(SNDRV_PCM_INFO_MMAP |
 			 SNDRV_PCM_INFO_MMAP_VALID |
 			 SNDRV_PCM_INFO_INTERLEAVED | 
 			 SNDRV_PCM_INFO_PAUSE),
@@ -309,7 +309,7 @@
  * Digital input capabilites (S/PDIF)
  */
 static snd_pcm_hardware_t snd_rme32_capture_spdif_info = {
-	info:		(SNDRV_PCM_INFO_MMAP |
+	.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),
@@ -332,7 +332,7 @@
  */
 static snd_pcm_hardware_t snd_rme32_playback_adat_info =
 {
-	info:		     (SNDRV_PCM_INFO_MMAP |
+	.info =		     (SNDRV_PCM_INFO_MMAP |
 			      SNDRV_PCM_INFO_MMAP_VALID |
 			      SNDRV_PCM_INFO_INTERLEAVED |
 			      SNDRV_PCM_INFO_PAUSE),
@@ -356,7 +356,7 @@
  */
 static snd_pcm_hardware_t snd_rme32_capture_adat_info =
 {
-	info:		     (SNDRV_PCM_INFO_MMAP |
+	.info =		     (SNDRV_PCM_INFO_MMAP |
 			      SNDRV_PCM_INFO_MMAP_VALID |
 			      SNDRV_PCM_INFO_INTERLEAVED |
 			      SNDRV_PCM_INFO_PAUSE),
@@ -793,9 +793,9 @@
 #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
+	.count = PERIOD_BYTES,
+	.list = period_bytes,
+	.mask = 0
 };
 
 static int snd_rme32_playback_spdif_open(snd_pcm_substream_t * substream)
@@ -859,6 +859,10 @@
 	spin_unlock_irqrestore(&rme32->lock, flags);
 
 	runtime->hw = snd_rme32_capture_spdif_info;
+	if (RME32_PRO_WITH_8414(rme32)) {
+		runtime->hw.rates |= SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000;
+		runtime->hw.rate_max = 96000;
+	}
 
 	snd_pcm_hw_constraint_minmax(runtime,
 				     SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
@@ -1142,53 +1146,53 @@
 }
 
 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,
+	.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,
+	.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 snd_pcm_ops_t snd_rme32_playback_adat_ops = {
-	open:		snd_rme32_playback_adat_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,
+	.open =		snd_rme32_playback_adat_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_adat_ops = {
-	open:		snd_rme32_capture_adat_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,
+	.open =		snd_rme32_capture_adat_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)
@@ -1517,10 +1521,8 @@
 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] };
+	static char *texts[4] = { "Optical", "Coaxial", "Internal", "XLR" };
 
 	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
 	uinfo->count = 1;
@@ -1614,8 +1616,8 @@
 	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;
+	if (uinfo->value.enumerated.item > 3) {
+		uinfo->value.enumerated.item = 3;
 	}
 	strcpy(uinfo->value.enumerated.name,
 	       texts[uinfo->value.enumerated.item]);
@@ -1760,56 +1762,56 @@
 
 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
+		.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_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, 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
+		.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 =	"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 =	"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
+		.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
 	}
 };
 
@@ -1898,10 +1900,10 @@
 }
 
 static struct pci_driver driver = {
-	name:		"RME Digi32",
-	id_table:	snd_rme32_ids,
-	probe:		snd_rme32_probe,
-	remove:		__devexit_p(snd_rme32_remove),
+	.name =		"RME Digi32",
+	.id_table =	snd_rme32_ids,
+	.probe =	snd_rme32_probe,
+	.remove =	__devexit_p(snd_rme32_remove),
 };
 
 static int __init alsa_card_rme32_init(void)
diff -Nru a/sound/pci/rme96.c b/sound/pci/rme96.c
--- a/sound/pci/rme96.c	Mon Oct  7 11:25:31 2002
+++ b/sound/pci/rme96.c	Mon Oct  7 11:25:31 2002
@@ -375,28 +375,28 @@
  */
 static snd_pcm_hardware_t snd_rme96_playback_spdif_info =
 {
-	info:		     (SNDRV_PCM_INFO_MMAP |
+	.info =		     (SNDRV_PCM_INFO_MMAP |
 			      SNDRV_PCM_INFO_MMAP_VALID |
 			      SNDRV_PCM_INFO_INTERLEAVED |
 			      SNDRV_PCM_INFO_PAUSE),
-	formats:	     (SNDRV_PCM_FMTBIT_S16_LE |
+	.formats =	     (SNDRV_PCM_FMTBIT_S16_LE |
 			      SNDRV_PCM_FMTBIT_S32_LE),
-	rates:		     (SNDRV_PCM_RATE_32000 |
+	.rates =	     (SNDRV_PCM_RATE_32000 |
 			      SNDRV_PCM_RATE_44100 | 
 			      SNDRV_PCM_RATE_48000 | 
 			      SNDRV_PCM_RATE_64000 |
 			      SNDRV_PCM_RATE_88200 | 
 			      SNDRV_PCM_RATE_96000),
-	rate_min:	     32000,
-	rate_max:	     96000,
-	channels_min:	     2,
-	channels_max:	     2,
-	buffer_bytes_max:   RME96_BUFFER_SIZE,
-	period_bytes_min:   RME96_SMALL_BLOCK_SIZE,
-	period_bytes_max:   RME96_LARGE_BLOCK_SIZE,
-	periods_min:	     RME96_BUFFER_SIZE / RME96_LARGE_BLOCK_SIZE,
-	periods_max:	     RME96_BUFFER_SIZE / RME96_SMALL_BLOCK_SIZE,
-	fifo_size:	     0,
+	.rate_min =	     32000,
+	.rate_max =	     96000,
+	.channels_min =	     2,
+	.channels_max =	     2,
+	.buffer_bytes_max =  RME96_BUFFER_SIZE,
+	.period_bytes_min =  RME96_SMALL_BLOCK_SIZE,
+	.period_bytes_max =  RME96_LARGE_BLOCK_SIZE,
+	.periods_min =	     RME96_BUFFER_SIZE / RME96_LARGE_BLOCK_SIZE,
+	.periods_max =	     RME96_BUFFER_SIZE / RME96_SMALL_BLOCK_SIZE,
+	.fifo_size =	     0,
 };
 
 /*
@@ -404,28 +404,28 @@
  */
 static snd_pcm_hardware_t snd_rme96_capture_spdif_info =
 {
-	info:		     (SNDRV_PCM_INFO_MMAP |
+	.info =		     (SNDRV_PCM_INFO_MMAP |
 			      SNDRV_PCM_INFO_MMAP_VALID |
 			      SNDRV_PCM_INFO_INTERLEAVED |
 			      SNDRV_PCM_INFO_PAUSE),
-	formats:	     (SNDRV_PCM_FMTBIT_S16_LE |
+	.formats =	     (SNDRV_PCM_FMTBIT_S16_LE |
 			      SNDRV_PCM_FMTBIT_S32_LE),
-	rates:		     (SNDRV_PCM_RATE_32000 |
+	.rates =	     (SNDRV_PCM_RATE_32000 |
 			      SNDRV_PCM_RATE_44100 | 
 			      SNDRV_PCM_RATE_48000 | 
 			      SNDRV_PCM_RATE_64000 |
 			      SNDRV_PCM_RATE_88200 | 
 			      SNDRV_PCM_RATE_96000),
-	rate_min:	     32000,
-	rate_max:	     96000,
-	channels_min:	     2,
-	channels_max:	     2,
-	buffer_bytes_max:   RME96_BUFFER_SIZE,
-	period_bytes_min:   RME96_SMALL_BLOCK_SIZE,
-	period_bytes_max:   RME96_LARGE_BLOCK_SIZE,
-	periods_min:	     RME96_BUFFER_SIZE / RME96_LARGE_BLOCK_SIZE,
-	periods_max:	     RME96_BUFFER_SIZE / RME96_SMALL_BLOCK_SIZE,
-	fifo_size:	     0,
+	.rate_min =	     32000,
+	.rate_max =	     96000,
+	.channels_min =	     2,
+	.channels_max =	     2,
+	.buffer_bytes_max =  RME96_BUFFER_SIZE,
+	.period_bytes_min =  RME96_SMALL_BLOCK_SIZE,
+	.period_bytes_max =  RME96_LARGE_BLOCK_SIZE,
+	.periods_min =	     RME96_BUFFER_SIZE / RME96_LARGE_BLOCK_SIZE,
+	.periods_max =	     RME96_BUFFER_SIZE / RME96_SMALL_BLOCK_SIZE,
+	.fifo_size =	     0,
 };
 
 /*
@@ -433,24 +433,24 @@
  */
 static snd_pcm_hardware_t snd_rme96_playback_adat_info =
 {
-	info:		     (SNDRV_PCM_INFO_MMAP |
+	.info =		     (SNDRV_PCM_INFO_MMAP |
 			      SNDRV_PCM_INFO_MMAP_VALID |
 			      SNDRV_PCM_INFO_INTERLEAVED |
 			      SNDRV_PCM_INFO_PAUSE),
-	formats:	     (SNDRV_PCM_FMTBIT_S16_LE |
+	.formats =	     (SNDRV_PCM_FMTBIT_S16_LE |
 			      SNDRV_PCM_FMTBIT_S32_LE),
-	rates:	             (SNDRV_PCM_RATE_44100 | 
+	.rates =             (SNDRV_PCM_RATE_44100 | 
 			      SNDRV_PCM_RATE_48000),
-	rate_min:            44100,
-	rate_max:            48000,
-	channels_min:        8,
-	channels_max:	     8,
-	buffer_bytes_max:   RME96_BUFFER_SIZE,
-	period_bytes_min:   RME96_SMALL_BLOCK_SIZE,
-	period_bytes_max:   RME96_LARGE_BLOCK_SIZE,
-	periods_min:	     RME96_BUFFER_SIZE / RME96_LARGE_BLOCK_SIZE,
-	periods_max:	     RME96_BUFFER_SIZE / RME96_SMALL_BLOCK_SIZE,
-	fifo_size:	     0,
+	.rate_min =          44100,
+	.rate_max =          48000,
+	.channels_min =      8,
+	.channels_max =	     8,
+	.buffer_bytes_max =  RME96_BUFFER_SIZE,
+	.period_bytes_min =  RME96_SMALL_BLOCK_SIZE,
+	.period_bytes_max =  RME96_LARGE_BLOCK_SIZE,
+	.periods_min =	     RME96_BUFFER_SIZE / RME96_LARGE_BLOCK_SIZE,
+	.periods_max =	     RME96_BUFFER_SIZE / RME96_SMALL_BLOCK_SIZE,
+	.fifo_size =	     0,
 };
 
 /*
@@ -458,24 +458,24 @@
  */
 static snd_pcm_hardware_t snd_rme96_capture_adat_info =
 {
-	info:		     (SNDRV_PCM_INFO_MMAP |
+	.info =		     (SNDRV_PCM_INFO_MMAP |
 			      SNDRV_PCM_INFO_MMAP_VALID |
 			      SNDRV_PCM_INFO_INTERLEAVED |
 			      SNDRV_PCM_INFO_PAUSE),
-	formats:	     (SNDRV_PCM_FMTBIT_S16_LE |
+	.formats =	     (SNDRV_PCM_FMTBIT_S16_LE |
 			      SNDRV_PCM_FMTBIT_S32_LE),
-	rates:	             (SNDRV_PCM_RATE_44100 | 
+	.rates =	     (SNDRV_PCM_RATE_44100 | 
 			      SNDRV_PCM_RATE_48000),
-	rate_min:            44100,
-	rate_max:            48000,
-	channels_min:        8,
-	channels_max:	     8,
-	buffer_bytes_max:   RME96_BUFFER_SIZE,
-	period_bytes_min:   RME96_SMALL_BLOCK_SIZE,
-	period_bytes_max:   RME96_LARGE_BLOCK_SIZE,
-	periods_min:	     RME96_BUFFER_SIZE / RME96_LARGE_BLOCK_SIZE,
-	periods_max:	     RME96_BUFFER_SIZE / RME96_SMALL_BLOCK_SIZE,
-	fifo_size:           0,
+	.rate_min =          44100,
+	.rate_max =          48000,
+	.channels_min =      8,
+	.channels_max =	     8,
+	.buffer_bytes_max =  RME96_BUFFER_SIZE,
+	.period_bytes_min =  RME96_SMALL_BLOCK_SIZE,
+	.period_bytes_max =  RME96_LARGE_BLOCK_SIZE,
+	.periods_min =	     RME96_BUFFER_SIZE / RME96_LARGE_BLOCK_SIZE,
+	.periods_max =	     RME96_BUFFER_SIZE / RME96_SMALL_BLOCK_SIZE,
+	.fifo_size =         0,
 };
 
 /*
@@ -1150,9 +1150,9 @@
 #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
+	.count = PERIOD_BYTES,
+	.list = period_bytes,
+	.mask = 0
 };
 
 static int
@@ -1493,53 +1493,53 @@
 }
 
 static snd_pcm_ops_t snd_rme96_playback_spdif_ops = {
-	open:		snd_rme96_playback_spdif_open,
-	close:		snd_rme96_playback_close,
-	ioctl:		snd_pcm_lib_ioctl,
-	hw_params:	snd_rme96_playback_hw_params,
-	hw_free:	snd_rme96_playback_hw_free,
-	prepare:	snd_rme96_playback_prepare,
-	trigger:	snd_rme96_playback_trigger,
-	pointer:	snd_rme96_playback_pointer,
-	copy:		snd_rme96_playback_copy,
-	silence:	snd_rme96_playback_silence,
+	.open =		snd_rme96_playback_spdif_open,
+	.close =	snd_rme96_playback_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	snd_rme96_playback_hw_params,
+	.hw_free =	snd_rme96_playback_hw_free,
+	.prepare =	snd_rme96_playback_prepare,
+	.trigger =	snd_rme96_playback_trigger,
+	.pointer =	snd_rme96_playback_pointer,
+	.copy =		snd_rme96_playback_copy,
+	.silence =	snd_rme96_playback_silence,
 };
 
 static snd_pcm_ops_t snd_rme96_capture_spdif_ops = {
-	open:		snd_rme96_capture_spdif_open,
-	close:		snd_rme96_capture_close,
-	ioctl:		snd_pcm_lib_ioctl,
-	hw_params:	snd_rme96_capture_hw_params,
-	hw_free:	snd_rme96_capture_hw_free,
-	prepare:	snd_rme96_capture_prepare,
-	trigger:	snd_rme96_capture_trigger,
-	pointer:	snd_rme96_capture_pointer,
-	copy:		snd_rme96_capture_copy,
+	.open =		snd_rme96_capture_spdif_open,
+	.close =	snd_rme96_capture_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	snd_rme96_capture_hw_params,
+	.hw_free =	snd_rme96_capture_hw_free,
+	.prepare =	snd_rme96_capture_prepare,
+	.trigger =	snd_rme96_capture_trigger,
+	.pointer =	snd_rme96_capture_pointer,
+	.copy =		snd_rme96_capture_copy,
 };
 
 static snd_pcm_ops_t snd_rme96_playback_adat_ops = {
-	open:		snd_rme96_playback_adat_open,
-	close:		snd_rme96_playback_close,
-	ioctl:		snd_pcm_lib_ioctl,
-	hw_params:	snd_rme96_playback_hw_params,
-	hw_free:	snd_rme96_playback_hw_free,
-	prepare:	snd_rme96_playback_prepare,
-	trigger:	snd_rme96_playback_trigger,
-	pointer:	snd_rme96_playback_pointer,
-	copy:		snd_rme96_playback_copy,
-	silence:	snd_rme96_playback_silence,
+	.open =		snd_rme96_playback_adat_open,
+	.close =	snd_rme96_playback_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	snd_rme96_playback_hw_params,
+	.hw_free =	snd_rme96_playback_hw_free,
+	.prepare =	snd_rme96_playback_prepare,
+	.trigger =	snd_rme96_playback_trigger,
+	.pointer =	snd_rme96_playback_pointer,
+	.copy =		snd_rme96_playback_copy,
+	.silence =	snd_rme96_playback_silence,
 };
 
 static snd_pcm_ops_t snd_rme96_capture_adat_ops = {
-	open:		snd_rme96_capture_adat_open,
-	close:		snd_rme96_capture_close,
-	ioctl:		snd_pcm_lib_ioctl,
-	hw_params:	snd_rme96_capture_hw_params,
-	hw_free:	snd_rme96_capture_hw_free,
-	prepare:	snd_rme96_capture_prepare,
-	trigger:	snd_rme96_capture_trigger,
-	pointer:	snd_rme96_capture_pointer,
-	copy:		snd_rme96_capture_copy,
+	.open =		snd_rme96_capture_adat_open,
+	.close =	snd_rme96_capture_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	snd_rme96_capture_hw_params,
+	.hw_free =	snd_rme96_capture_hw_free,
+	.prepare =	snd_rme96_capture_prepare,
+	.trigger =	snd_rme96_capture_trigger,
+	.pointer =	snd_rme96_capture_pointer,
+	.copy =		snd_rme96_capture_copy,
 };
 
 static void
@@ -2299,81 +2299,81 @@
 
 static snd_kcontrol_new_t snd_rme96_controls[] = {
 {
-	iface:		SNDRV_CTL_ELEM_IFACE_PCM,
-	name:		SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
-	info:		snd_rme96_control_spdif_info,
-	get:		snd_rme96_control_spdif_get,
-	put:		snd_rme96_control_spdif_put
+	.iface =	SNDRV_CTL_ELEM_IFACE_PCM,
+	.name =		SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
+	.info =		snd_rme96_control_spdif_info,
+	.get =		snd_rme96_control_spdif_get,
+	.put =		snd_rme96_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_rme96_control_spdif_stream_info,
-	get:		snd_rme96_control_spdif_stream_get,
-	put:		snd_rme96_control_spdif_stream_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_rme96_control_spdif_stream_info,
+	.get =		snd_rme96_control_spdif_stream_get,
+	.put =		snd_rme96_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_rme96_control_spdif_mask_info,
-	get:		snd_rme96_control_spdif_mask_get,
-	private_value:	IEC958_AES0_NONAUDIO |
+	.access =	SNDRV_CTL_ELEM_ACCESS_READ,
+	.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name =		SNDRV_CTL_NAME_IEC958("",PLAYBACK,CON_MASK),
+	.info =		snd_rme96_control_spdif_mask_info,
+	.get =		snd_rme96_control_spdif_mask_get,
+	.private_value = IEC958_AES0_NONAUDIO |
 			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_rme96_control_spdif_mask_info,
-	get:		snd_rme96_control_spdif_mask_get,
-	private_value:	IEC958_AES0_NONAUDIO |
+	.access =	SNDRV_CTL_ELEM_ACCESS_READ,
+	.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name =		SNDRV_CTL_NAME_IEC958("",PLAYBACK,PRO_MASK),
+	.info =		snd_rme96_control_spdif_mask_info,
+	.get =		snd_rme96_control_spdif_mask_get,
+	.private_value = IEC958_AES0_NONAUDIO |
 			IEC958_AES0_PROFESSIONAL |
 			IEC958_AES0_PRO_EMPHASIS
 },
 {
-        iface:          SNDRV_CTL_ELEM_IFACE_PCM,
-	name:           "Input Connector",
-	info:           snd_rme96_info_inputtype_control, 
-	get:            snd_rme96_get_inputtype_control,
-	put:            snd_rme96_put_inputtype_control 
+        .iface =        SNDRV_CTL_ELEM_IFACE_PCM,
+	.name =         "Input Connector",
+	.info =         snd_rme96_info_inputtype_control, 
+	.get =          snd_rme96_get_inputtype_control,
+	.put =          snd_rme96_put_inputtype_control 
 },
 {
-        iface:          SNDRV_CTL_ELEM_IFACE_PCM,
-	name:           "Loopback Input",
-	info:           snd_rme96_info_loopback_control,
-	get:            snd_rme96_get_loopback_control,
-	put:            snd_rme96_put_loopback_control
+        .iface =        SNDRV_CTL_ELEM_IFACE_PCM,
+	.name =         "Loopback Input",
+	.info =         snd_rme96_info_loopback_control,
+	.get =          snd_rme96_get_loopback_control,
+	.put =          snd_rme96_put_loopback_control
 },
 {
-        iface:          SNDRV_CTL_ELEM_IFACE_PCM,
-	name:           "Clock Mode",
-	info:           snd_rme96_info_clockmode_control, 
-	get:            snd_rme96_get_clockmode_control,
-	put:            snd_rme96_put_clockmode_control
+        .iface =        SNDRV_CTL_ELEM_IFACE_PCM,
+	.name =         "Clock Mode",
+	.info =         snd_rme96_info_clockmode_control, 
+	.get =          snd_rme96_get_clockmode_control,
+	.put =          snd_rme96_put_clockmode_control
 },
 {
-        iface:          SNDRV_CTL_ELEM_IFACE_PCM,
-	name:           "Monitor Tracks",
-	info:           snd_rme96_info_montracks_control, 
-	get:            snd_rme96_get_montracks_control,
-	put:            snd_rme96_put_montracks_control
+        .iface =        SNDRV_CTL_ELEM_IFACE_PCM,
+	.name =         "Monitor Tracks",
+	.info =         snd_rme96_info_montracks_control, 
+	.get =          snd_rme96_get_montracks_control,
+	.put =          snd_rme96_put_montracks_control
 },
 {
-        iface:          SNDRV_CTL_ELEM_IFACE_PCM,
-	name:           "Attenuation",
-	info:           snd_rme96_info_attenuation_control, 
-	get:            snd_rme96_get_attenuation_control,
-	put:            snd_rme96_put_attenuation_control
+        .iface =        SNDRV_CTL_ELEM_IFACE_PCM,
+	.name =         "Attenuation",
+	.info =         snd_rme96_info_attenuation_control, 
+	.get =          snd_rme96_get_attenuation_control,
+	.put =          snd_rme96_put_attenuation_control
 },
 {
-        iface:          SNDRV_CTL_ELEM_IFACE_MIXER,
-	name:           "DAC Playback Volume",
-	info:           snd_rme96_dac_volume_info,
-	get:            snd_rme96_dac_volume_get,
-	put:            snd_rme96_dac_volume_put
+        .iface =        SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name =         "DAC Playback Volume",
+	.info =         snd_rme96_dac_volume_info,
+	.get =          snd_rme96_dac_volume_get,
+	.put =          snd_rme96_dac_volume_put
 }
 };
 
@@ -2477,10 +2477,10 @@
 }
 
 static struct pci_driver driver = {
-	name: "RME Digi96",
-	id_table: snd_rme96_ids,
-	probe: snd_rme96_probe,
-	remove: __devexit_p(snd_rme96_remove),
+	.name = "RME Digi96",
+	.id_table = snd_rme96_ids,
+	.probe = snd_rme96_probe,
+	.remove = __devexit_p(snd_rme96_remove),
 };
 
 static int __init alsa_card_rme96_init(void)
diff -Nru a/sound/pci/rme9652/hammerfall_mem.c b/sound/pci/rme9652/hammerfall_mem.c
--- a/sound/pci/rme9652/hammerfall_mem.c	Mon Oct  7 11:25:26 2002
+++ b/sound/pci/rme9652/hammerfall_mem.c	Mon Oct  7 11:25:26 2002
@@ -25,7 +25,7 @@
     along with this program; if not, write to the Free Software
     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
 
-    $Id: hammerfall_mem.c,v 1.2 2002/06/19 08:52:11 perex Exp $
+    $Id: hammerfall_mem.c,v 1.3 2002/09/12 09:03:28 tiwai Exp $
 
 
     Tue Oct 17 2000  Jaroslav Kysela <perex@suse.cz>
@@ -92,11 +92,6 @@
    memory after starting things running, that would be very
    undesirable.  
 */
-
-/* remove hack for pci_alloc_consistent to avoid dependecy on snd module */
-#ifdef HACK_PCI_ALLOC_CONSISTENT
-#undef pci_alloc_consistent
-#endif
 
 static void *hammerfall_malloc_pages(struct pci_dev *pci,
 				  unsigned long size,
diff -Nru a/sound/pci/rme9652/hdsp.c b/sound/pci/rme9652/hdsp.c
--- a/sound/pci/rme9652/hdsp.c	Mon Oct  7 11:25:29 2002
+++ b/sound/pci/rme9652/hdsp.c	Mon Oct  7 11:25:29 2002
@@ -1022,16 +1022,16 @@
 
 snd_rawmidi_ops_t snd_hdsp_midi_output =
 {
-	open:		snd_hdsp_midi_output_open,
-	close:		snd_hdsp_midi_output_close,
-	trigger:	snd_hdsp_midi_output_trigger,
+	.open =		snd_hdsp_midi_output_open,
+	.close =	snd_hdsp_midi_output_close,
+	.trigger =	snd_hdsp_midi_output_trigger,
 };
 
 snd_rawmidi_ops_t snd_hdsp_midi_input =
 {
-	open:		snd_hdsp_midi_input_open,
-	close:		snd_hdsp_midi_input_close,
-	trigger:	snd_hdsp_midi_input_trigger,
+	.open =		snd_hdsp_midi_input_open,
+	.close =	snd_hdsp_midi_input_close,
+	.trigger =	snd_hdsp_midi_input_trigger,
 };
 
 static int __devinit snd_hdsp_create_midi (snd_card_t *card, hdsp_t *hdsp, int id)
@@ -1166,9 +1166,9 @@
 }
 
 #define HDSP_SPDIF_IN(xname, xindex) \
-{ iface: SNDRV_CTL_ELEM_IFACE_PCM, name: xname, index: xindex, \
-  info: snd_hdsp_info_spdif_in, \
-  get: snd_hdsp_get_spdif_in, put: snd_hdsp_put_spdif_in }
+{ .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = xname, .index = xindex, \
+  .info = snd_hdsp_info_spdif_in, \
+  .get = snd_hdsp_get_spdif_in, .put = snd_hdsp_put_spdif_in }
 
 static unsigned int hdsp_spdif_in(hdsp_t *hdsp)
 {
@@ -1226,9 +1226,9 @@
 }
 
 #define HDSP_SPDIF_OUT(xname, xindex) \
-{ iface: SNDRV_CTL_ELEM_IFACE_PCM, name: xname, index: xindex, \
-  info: snd_hdsp_info_spdif_out, \
-  get: snd_hdsp_get_spdif_out, put: snd_hdsp_put_spdif_out }
+{ .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = xname, .index = xindex, \
+  .info = snd_hdsp_info_spdif_out, \
+  .get = snd_hdsp_get_spdif_out, .put = snd_hdsp_put_spdif_out }
 
 static int hdsp_spdif_out(hdsp_t *hdsp)
 {
@@ -1284,9 +1284,9 @@
 }
 
 #define HDSP_SYNC_PREF(xname, xindex) \
-{ iface: SNDRV_CTL_ELEM_IFACE_PCM, name: xname, index: xindex, \
-  info: snd_hdsp_info_sync_pref, \
-  get: snd_hdsp_get_sync_pref, put: snd_hdsp_put_sync_pref }
+{ .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = xname, .index = xindex, \
+  .info = snd_hdsp_info_sync_pref, \
+  .get = snd_hdsp_get_sync_pref, .put = snd_hdsp_put_sync_pref }
 
 static int hdsp_sync_pref(hdsp_t *hdsp)
 {
@@ -1399,10 +1399,10 @@
 }
 
 #define HDSP_PASSTHRU(xname, xindex) \
-{ iface: SNDRV_CTL_ELEM_IFACE_PCM, name: xname, index: xindex, \
-  info: snd_hdsp_info_passthru, \
-  put: snd_hdsp_put_passthru, \
-  get: snd_hdsp_get_passthru }
+{ .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = xname, .index = xindex, \
+  .info = snd_hdsp_info_passthru, \
+  .put = snd_hdsp_put_passthru, \
+  .get = snd_hdsp_get_passthru }
 
 static int snd_hdsp_info_passthru(snd_kcontrol_t * kcontrol, snd_ctl_elem_info_t * uinfo)
 {
@@ -1445,9 +1445,9 @@
 }
 
 #define HDSP_LINE_OUT(xname, xindex) \
-{ iface: SNDRV_CTL_ELEM_IFACE_PCM, name: xname, index: xindex, \
-  info: snd_hdsp_info_line_out, \
-  get: snd_hdsp_get_line_out, put: snd_hdsp_put_line_out }
+{ .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = xname, .index = xindex, \
+  .info = snd_hdsp_info_line_out, \
+  .get = snd_hdsp_get_line_out, .put = snd_hdsp_put_line_out }
 
 static int hdsp_line_out(hdsp_t *hdsp)
 {
@@ -1503,9 +1503,9 @@
 }
 
 #define HDSP_MIXER(xname, xindex) \
-{ iface: SNDRV_CTL_ELEM_IFACE_PCM, name: xname, index: xindex, \
-  info: snd_hdsp_info_mixer, \
-  get: snd_hdsp_get_mixer, put: snd_hdsp_put_mixer }
+{ .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = xname, .index = xindex, \
+  .info = snd_hdsp_info_mixer, \
+  .get = snd_hdsp_get_mixer, .put = snd_hdsp_put_mixer }
 
 static int snd_hdsp_info_mixer(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
 {
@@ -1577,11 +1577,11 @@
 */
 
 #define HDSP_PLAYBACK_MIXER \
-{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, \
-  access: SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_WRITE | \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+  .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_WRITE | \
 		 SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
-  info: snd_hdsp_info_playback_mixer, \
-  get: snd_hdsp_get_playback_mixer, put: snd_hdsp_put_playback_mixer }
+  .info = snd_hdsp_info_playback_mixer, \
+  .get = snd_hdsp_get_playback_mixer, .put = snd_hdsp_put_playback_mixer }
 
 static int snd_hdsp_info_playback_mixer(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
 {
@@ -1634,10 +1634,10 @@
 }
 
 #define HDSP_PEAK_PLAYBACK \
-{ iface: SNDRV_CTL_ELEM_IFACE_PCM, \
-  access: SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
-  info: snd_hdsp_info_peak_playback, \
-  get: snd_hdsp_get_peak_playback \
+{ .iface = SNDRV_CTL_ELEM_IFACE_PCM, \
+  .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
+  .info = snd_hdsp_info_peak_playback, \
+  .get = snd_hdsp_get_peak_playback \
 }
 
 static int snd_hdsp_info_peak_playback(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
@@ -1657,10 +1657,10 @@
 }
 
 #define HDSP_PEAK_INPUT \
-{ iface: SNDRV_CTL_ELEM_IFACE_PCM, \
-  access: SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
-  info: snd_hdsp_info_peak_input, \
-  get: snd_hdsp_get_peak_input \
+{ .iface = SNDRV_CTL_ELEM_IFACE_PCM, \
+  .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
+  .info = snd_hdsp_info_peak_input, \
+  .get = snd_hdsp_get_peak_input \
 }
 
 static int snd_hdsp_info_peak_input(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
@@ -1680,10 +1680,10 @@
 }
 
 #define HDSP_PEAK_OUTPUT \
-{ iface: SNDRV_CTL_ELEM_IFACE_PCM, \
-  access: SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
-  info: snd_hdsp_info_peak_output, \
-  get: snd_hdsp_get_peak_output \
+{ .iface = SNDRV_CTL_ELEM_IFACE_PCM, \
+  .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
+  .info = snd_hdsp_info_peak_output, \
+  .get = snd_hdsp_get_peak_output \
 }
 
 static int snd_hdsp_info_peak_output(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
@@ -1703,10 +1703,10 @@
 }
 
 #define HDSP_RMS_INPUT \
-{ iface: SNDRV_CTL_ELEM_IFACE_PCM, \
-  access: SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
-  info: snd_hdsp_info_rms_input, \
-  get: snd_hdsp_get_rms_input \
+{ .iface = SNDRV_CTL_ELEM_IFACE_PCM, \
+  .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
+  .info = snd_hdsp_info_rms_input, \
+  .get = snd_hdsp_get_rms_input \
 }
 
 static int snd_hdsp_info_rms_input(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
@@ -1724,10 +1724,10 @@
 }
 
 #define HDSP_RMS_PLAYBACK \
-{ iface: SNDRV_CTL_ELEM_IFACE_PCM, \
-  access: SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
-  info: snd_hdsp_info_rms_playback, \
-  get: snd_hdsp_get_rms_playback \
+{ .iface = SNDRV_CTL_ELEM_IFACE_PCM, \
+  .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
+  .info = snd_hdsp_info_rms_playback, \
+  .get = snd_hdsp_get_rms_playback \
 }
 
 static int snd_hdsp_info_rms_playback(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
@@ -1746,37 +1746,37 @@
 
 static snd_kcontrol_new_t snd_hdsp_controls[] = {
 {
-	iface:		SNDRV_CTL_ELEM_IFACE_PCM,
-	name:		SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
-	info:		snd_hdsp_control_spdif_info,
-	get:		snd_hdsp_control_spdif_get,
-	put:		snd_hdsp_control_spdif_put,
+	.iface =	SNDRV_CTL_ELEM_IFACE_PCM,
+	.name =		SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
+	.info =		snd_hdsp_control_spdif_info,
+	.get =		snd_hdsp_control_spdif_get,
+	.put =		snd_hdsp_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_hdsp_control_spdif_stream_info,
-	get:		snd_hdsp_control_spdif_stream_get,
-	put:		snd_hdsp_control_spdif_stream_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_hdsp_control_spdif_stream_info,
+	.get =		snd_hdsp_control_spdif_stream_get,
+	.put =		snd_hdsp_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_hdsp_control_spdif_mask_info,
-	get:		snd_hdsp_control_spdif_mask_get,
-	private_value:	IEC958_AES0_NONAUDIO |
+	.access =	SNDRV_CTL_ELEM_ACCESS_READ,
+	.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name =		SNDRV_CTL_NAME_IEC958("",PLAYBACK,CON_MASK),
+	.info =		snd_hdsp_control_spdif_mask_info,
+	.get =		snd_hdsp_control_spdif_mask_get,
+	.private_value = IEC958_AES0_NONAUDIO |
 			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_hdsp_control_spdif_mask_info,
-	get:		snd_hdsp_control_spdif_mask_get,
-	private_value:	IEC958_AES0_NONAUDIO |
+	.access =	SNDRV_CTL_ELEM_ACCESS_READ,
+	.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name =		SNDRV_CTL_NAME_IEC958("",PLAYBACK,PRO_MASK),
+	.info =		snd_hdsp_control_spdif_mask_info,
+	.get =		snd_hdsp_control_spdif_mask_get,
+	.private_value = IEC958_AES0_NONAUDIO |
 			IEC958_AES0_PROFESSIONAL |
 			IEC958_AES0_PRO_EMPHASIS,
 },
@@ -2546,53 +2546,53 @@
 
 static snd_pcm_hardware_t snd_hdsp_playback_subinfo =
 {
-	info:			(SNDRV_PCM_INFO_MMAP |
+	.info =			(SNDRV_PCM_INFO_MMAP |
 				 SNDRV_PCM_INFO_MMAP_VALID |
 				 SNDRV_PCM_INFO_NONINTERLEAVED |
 				 SNDRV_PCM_INFO_SYNC_START |
 				 SNDRV_PCM_INFO_DOUBLE),
-	formats:		SNDRV_PCM_FMTBIT_S32_LE,
-	rates:			(SNDRV_PCM_RATE_32000 |
+	.formats =		SNDRV_PCM_FMTBIT_S32_LE,
+	.rates =		(SNDRV_PCM_RATE_32000 |
 				 SNDRV_PCM_RATE_44100 | 
 				 SNDRV_PCM_RATE_48000 | 
 				 SNDRV_PCM_RATE_64000 | 
 				 SNDRV_PCM_RATE_88200 | 
 				 SNDRV_PCM_RATE_96000),
-	rate_min:		32000,
-	rate_max:		96000,
-	channels_min:		10,
-	channels_max:		HDSP_MAX_CHANNELS,
-	buffer_bytes_max:	1024*1024,
-	period_bytes_min:	1,
-	period_bytes_max:	1024*1024,
-	periods_min:		2,
-	periods_max:		2,
-	fifo_size:		0,
+	.rate_min =		32000,
+	.rate_max =		96000,
+	.channels_min =		10,
+	.channels_max =		HDSP_MAX_CHANNELS,
+	.buffer_bytes_max =	1024*1024,
+	.period_bytes_min =	1,
+	.period_bytes_max =	1024*1024,
+	.periods_min =		2,
+	.periods_max =		2,
+	.fifo_size =		0,
 };
 
 static snd_pcm_hardware_t snd_hdsp_capture_subinfo =
 {
-	info:			(SNDRV_PCM_INFO_MMAP |
+	.info =			(SNDRV_PCM_INFO_MMAP |
 				 SNDRV_PCM_INFO_MMAP_VALID |
 				 SNDRV_PCM_INFO_NONINTERLEAVED |
 				 SNDRV_PCM_INFO_SYNC_START),
-	formats:		SNDRV_PCM_FMTBIT_S32_LE,
-	rates:			(SNDRV_PCM_RATE_32000 |
+	.formats =		SNDRV_PCM_FMTBIT_S32_LE,
+	.rates =		(SNDRV_PCM_RATE_32000 |
 				 SNDRV_PCM_RATE_44100 | 
 				 SNDRV_PCM_RATE_48000 | 
 				 SNDRV_PCM_RATE_64000 | 
 				 SNDRV_PCM_RATE_88200 | 
 				 SNDRV_PCM_RATE_96000),
-	rate_min:		32000,
-	rate_max:		96000,
-	channels_min:		10,
-	channels_max:		HDSP_MAX_CHANNELS,
-	buffer_bytes_max:	1024*1024,
-	period_bytes_min:	1,
-	period_bytes_max:	1024*1024,
-	periods_min:		2,
-	periods_max:		2,
-	fifo_size:		0,
+	.rate_min =		32000,
+	.rate_max =		96000,
+	.channels_min =		10,
+	.channels_max =		HDSP_MAX_CHANNELS,
+	.buffer_bytes_max =	1024*1024,
+	.period_bytes_min =	1,
+	.period_bytes_max =	1024*1024,
+	.periods_min =		2,
+	.periods_max =		2,
+	.fifo_size =		0,
 };
 
 static unsigned int period_sizes[] = { 64, 128, 256, 512, 1024, 2048, 4096, 8192 };
@@ -2600,9 +2600,9 @@
 #define PERIOD_SIZES sizeof(period_sizes) / sizeof(period_sizes[0])
 
 static snd_pcm_hw_constraint_list_t hw_constraints_period_sizes = {
-	count: PERIOD_SIZES,
-	list: period_sizes,
-	mask: 0
+	.count = PERIOD_SIZES,
+	.list = period_sizes,
+	.mask = 0
 };
 
 static int snd_hdsp_hw_rule_channels(snd_pcm_hw_params_t *params,
@@ -2785,26 +2785,26 @@
 }
 
 static snd_pcm_ops_t snd_hdsp_playback_ops = {
-	open:		snd_hdsp_playback_open,
-	close:		snd_hdsp_playback_release,
-	ioctl:		snd_hdsp_ioctl,
-	hw_params:	snd_hdsp_hw_params,
-	prepare:	snd_hdsp_prepare,
-	trigger:	snd_hdsp_trigger,
-	pointer:	snd_hdsp_hw_pointer,
-	copy:		snd_hdsp_playback_copy,
-	silence:	snd_hdsp_hw_silence,
+	.open =		snd_hdsp_playback_open,
+	.close =	snd_hdsp_playback_release,
+	.ioctl =	snd_hdsp_ioctl,
+	.hw_params =	snd_hdsp_hw_params,
+	.prepare =	snd_hdsp_prepare,
+	.trigger =	snd_hdsp_trigger,
+	.pointer =	snd_hdsp_hw_pointer,
+	.copy =		snd_hdsp_playback_copy,
+	.silence =	snd_hdsp_hw_silence,
 };
 
 static snd_pcm_ops_t snd_hdsp_capture_ops = {
-	open:		snd_hdsp_capture_open,
-	close:		snd_hdsp_capture_release,
-	ioctl:		snd_hdsp_ioctl,
-	hw_params:	snd_hdsp_hw_params,
-	prepare:	snd_hdsp_prepare,
-	trigger:	snd_hdsp_trigger,
-	pointer:	snd_hdsp_hw_pointer,
-	copy:		snd_hdsp_capture_copy,
+	.open =		snd_hdsp_capture_open,
+	.close =	snd_hdsp_capture_release,
+	.ioctl =	snd_hdsp_ioctl,
+	.hw_params =	snd_hdsp_hw_params,
+	.prepare =	snd_hdsp_prepare,
+	.trigger =	snd_hdsp_trigger,
+	.pointer =	snd_hdsp_hw_pointer,
+	.copy =		snd_hdsp_capture_copy,
 };
 
 static int __devinit snd_hdsp_create_pcm(snd_card_t *card,
@@ -3115,10 +3115,10 @@
 }
 
 static struct pci_driver driver = {
-	name:"RME Hammerfall DSP",
-	id_table:snd_hdsp_ids,
-	probe:snd_hdsp_probe,
-	remove:__devexit_p(snd_hdsp_remove),
+	.name = "RME Hammerfall DSP",
+	.id_table = snd_hdsp_ids,
+	.probe = snd_hdsp_probe,
+	.remove = __devexit_p(snd_hdsp_remove),
 };
 
 static int __init alsa_card_hdsp_init(void)
diff -Nru a/sound/pci/rme9652/rme9652.c b/sound/pci/rme9652/rme9652.c
--- a/sound/pci/rme9652/rme9652.c	Mon Oct  7 11:25:34 2002
+++ b/sound/pci/rme9652/rme9652.c	Mon Oct  7 11:25:34 2002
@@ -878,10 +878,10 @@
 }
 
 #define RME9652_ADAT1_IN(xname, xindex) \
-{ iface: SNDRV_CTL_ELEM_IFACE_PCM, name: xname, index: xindex, \
-  info: snd_rme9652_info_adat1_in, \
-  get: snd_rme9652_get_adat1_in, \
-  put: snd_rme9652_put_adat1_in }
+{ .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = xname, .index = xindex, \
+  .info = snd_rme9652_info_adat1_in, \
+  .get = snd_rme9652_get_adat1_in, \
+  .put = snd_rme9652_put_adat1_in }
 
 static unsigned int rme9652_adat1_in(rme9652_t *rme9652)
 {
@@ -958,9 +958,9 @@
 }
 
 #define RME9652_SPDIF_IN(xname, xindex) \
-{ iface: SNDRV_CTL_ELEM_IFACE_PCM, name: xname, index: xindex, \
-  info: snd_rme9652_info_spdif_in, \
-  get: snd_rme9652_get_spdif_in, put: snd_rme9652_put_spdif_in }
+{ .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = xname, .index = xindex, \
+  .info = snd_rme9652_info_spdif_in, \
+  .get = snd_rme9652_get_spdif_in, .put = snd_rme9652_put_spdif_in }
 
 static unsigned int rme9652_spdif_in(rme9652_t *rme9652)
 {
@@ -1031,9 +1031,9 @@
 }
 
 #define RME9652_SPDIF_OUT(xname, xindex) \
-{ iface: SNDRV_CTL_ELEM_IFACE_PCM, name: xname, index: xindex, \
-  info: snd_rme9652_info_spdif_out, \
-  get: snd_rme9652_get_spdif_out, put: snd_rme9652_put_spdif_out }
+{ .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = xname, .index = xindex, \
+  .info = snd_rme9652_info_spdif_out, \
+  .get = snd_rme9652_get_spdif_out, .put = snd_rme9652_put_spdif_out }
 
 static int rme9652_spdif_out(rme9652_t *rme9652)
 {
@@ -1101,9 +1101,9 @@
 }
 
 #define RME9652_SYNC_MODE(xname, xindex) \
-{ iface: SNDRV_CTL_ELEM_IFACE_PCM, name: xname, index: xindex, \
-  info: snd_rme9652_info_sync_mode, \
-  get: snd_rme9652_get_sync_mode, put: snd_rme9652_put_sync_mode }
+{ .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = xname, .index = xindex, \
+  .info = snd_rme9652_info_sync_mode, \
+  .get = snd_rme9652_get_sync_mode, .put = snd_rme9652_put_sync_mode }
 
 static int rme9652_sync_mode(rme9652_t *rme9652)
 {
@@ -1188,9 +1188,9 @@
 }
 
 #define RME9652_SYNC_PREF(xname, xindex) \
-{ iface: SNDRV_CTL_ELEM_IFACE_PCM, name: xname, index: xindex, \
-  info: snd_rme9652_info_sync_pref, \
-  get: snd_rme9652_get_sync_pref, put: snd_rme9652_put_sync_pref }
+{ .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = xname, .index = xindex, \
+  .info = snd_rme9652_info_sync_pref, \
+  .get = snd_rme9652_get_sync_pref, .put = snd_rme9652_put_sync_pref }
 
 static int rme9652_sync_pref(rme9652_t *rme9652)
 {
@@ -1336,10 +1336,10 @@
 }
 
 #define RME9652_PASSTHRU(xname, xindex) \
-{ iface: SNDRV_CTL_ELEM_IFACE_PCM, name: xname, index: xindex, \
-  info: snd_rme9652_info_passthru, \
-  put: snd_rme9652_put_passthru, \
-  get: snd_rme9652_get_passthru }
+{ .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = xname, .index = xindex, \
+  .info = snd_rme9652_info_passthru, \
+  .put = snd_rme9652_put_passthru, \
+  .get = snd_rme9652_get_passthru }
 
 static int snd_rme9652_info_passthru(snd_kcontrol_t * kcontrol, snd_ctl_elem_info_t * uinfo)
 {
@@ -1384,10 +1384,10 @@
 /* Read-only switches */
 
 #define RME9652_SPDIF_RATE(xname, xindex) \
-{ iface: SNDRV_CTL_ELEM_IFACE_PCM, name: xname, index: xindex, \
-  access: SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
-  info: snd_rme9652_info_spdif_rate, \
-  get: snd_rme9652_get_spdif_rate }
+{ .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = xname, .index = xindex, \
+  .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
+  .info = snd_rme9652_info_spdif_rate, \
+  .get = snd_rme9652_get_spdif_rate }
 
 static int snd_rme9652_info_spdif_rate(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
 {
@@ -1410,10 +1410,10 @@
 }
 
 #define RME9652_ADAT_SYNC(xname, xindex, xidx) \
-{ iface: SNDRV_CTL_ELEM_IFACE_PCM, name: xname, index: xindex, \
-  access: SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
-  info: snd_rme9652_info_adat_sync, \
-  get: snd_rme9652_get_adat_sync, private_value: xidx }
+{ .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = xname, .index = xindex, \
+  .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
+  .info = snd_rme9652_info_adat_sync, \
+  .get = snd_rme9652_get_adat_sync, .private_value = xidx }
 
 static int snd_rme9652_info_adat_sync(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
 {
@@ -1446,10 +1446,10 @@
 }
 
 #define RME9652_TC_VALID(xname, xindex) \
-{ iface: SNDRV_CTL_ELEM_IFACE_PCM, name: xname, index: xindex, \
-  access: SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
-  info: snd_rme9652_info_tc_valid, \
-  get: snd_rme9652_get_tc_valid }
+{ .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = xname, .index = xindex, \
+  .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
+  .info = snd_rme9652_info_tc_valid, \
+  .get = snd_rme9652_get_tc_valid }
 
 static int snd_rme9652_info_tc_valid(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
 {
@@ -1530,37 +1530,37 @@
 
 static snd_kcontrol_new_t snd_rme9652_controls[] = {
 {
-	iface:		SNDRV_CTL_ELEM_IFACE_PCM,
-	name:		SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
-	info:		snd_rme9652_control_spdif_info,
-	get:		snd_rme9652_control_spdif_get,
-	put:		snd_rme9652_control_spdif_put,
+	.iface =	SNDRV_CTL_ELEM_IFACE_PCM,
+	.name =		SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
+	.info =		snd_rme9652_control_spdif_info,
+	.get =		snd_rme9652_control_spdif_get,
+	.put =		snd_rme9652_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_rme9652_control_spdif_stream_info,
-	get:		snd_rme9652_control_spdif_stream_get,
-	put:		snd_rme9652_control_spdif_stream_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_rme9652_control_spdif_stream_info,
+	.get =		snd_rme9652_control_spdif_stream_get,
+	.put =		snd_rme9652_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_rme9652_control_spdif_mask_info,
-	get:		snd_rme9652_control_spdif_mask_get,
-	private_value:	IEC958_AES0_NONAUDIO |
+	.access =	SNDRV_CTL_ELEM_ACCESS_READ,
+	.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name =		SNDRV_CTL_NAME_IEC958("",PLAYBACK,CON_MASK),
+	.info =		snd_rme9652_control_spdif_mask_info,
+	.get =		snd_rme9652_control_spdif_mask_get,
+	.private_value = IEC958_AES0_NONAUDIO |
 			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_rme9652_control_spdif_mask_info,
-	get:		snd_rme9652_control_spdif_mask_get,
-	private_value:	IEC958_AES0_NONAUDIO |
+	.access =	SNDRV_CTL_ELEM_ACCESS_READ,
+	.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name =		SNDRV_CTL_NAME_IEC958("",PLAYBACK,PRO_MASK),
+	.info =		snd_rme9652_control_spdif_mask_info,
+	.get =		snd_rme9652_control_spdif_mask_get,
+	.private_value = IEC958_AES0_NONAUDIO |
 			IEC958_AES0_PROFESSIONAL |
 			IEC958_AES0_PRO_EMPHASIS,
 },
@@ -1569,12 +1569,12 @@
 RME9652_SYNC_MODE("Sync Mode", 0),
 RME9652_SYNC_PREF("Preferred Sync Source", 0),
 {
-	iface: SNDRV_CTL_ELEM_IFACE_PCM,
-	name: "Channels Thru",
-	index: 0,
-	info: snd_rme9652_info_thru,
-	get: snd_rme9652_get_thru,
-	put: snd_rme9652_put_thru,
+	.iface = SNDRV_CTL_ELEM_IFACE_PCM,
+	.name = "Channels Thru",
+	.index = 0,
+	.info = snd_rme9652_info_thru,
+	.get = snd_rme9652_get_thru,
+	.put = snd_rme9652_put_thru,
 },
 RME9652_SPDIF_RATE("IEC958 Sample Rate", 0),
 RME9652_ADAT_SYNC("ADAT1 Sync Check", 0, 0),
@@ -2265,49 +2265,49 @@
 
 static snd_pcm_hardware_t snd_rme9652_playback_subinfo =
 {
-	info:			(SNDRV_PCM_INFO_MMAP |
+	.info =			(SNDRV_PCM_INFO_MMAP |
 				 SNDRV_PCM_INFO_MMAP_VALID |
 				 SNDRV_PCM_INFO_NONINTERLEAVED |
 				 SNDRV_PCM_INFO_SYNC_START |
 				 SNDRV_PCM_INFO_DOUBLE),
-	formats:		SNDRV_PCM_FMTBIT_S32_LE,
-	rates:			(SNDRV_PCM_RATE_44100 | 
+	.formats =		SNDRV_PCM_FMTBIT_S32_LE,
+	.rates =		(SNDRV_PCM_RATE_44100 | 
 				 SNDRV_PCM_RATE_48000 | 
 				 SNDRV_PCM_RATE_88200 | 
 				 SNDRV_PCM_RATE_96000),
-	rate_min:		44100,
-	rate_max:		96000,
-	channels_min:		10,
-	channels_max:		26,
-	buffer_bytes_max:	1024*1024,
-	period_bytes_min:	1,
-	period_bytes_max:	1024*1024,
-	periods_min:		2,
-	periods_max:		2,
-	fifo_size:		0,
+	.rate_min =		44100,
+	.rate_max =		96000,
+	.channels_min =		10,
+	.channels_max =		26,
+	.buffer_bytes_max =	1024*1024,
+	.period_bytes_min =	1,
+	.period_bytes_max =	1024*1024,
+	.periods_min =		2,
+	.periods_max =		2,
+	.fifo_size =		0,
 };
 
 static snd_pcm_hardware_t snd_rme9652_capture_subinfo =
 {
-	info:			(SNDRV_PCM_INFO_MMAP |
+	.info =			(SNDRV_PCM_INFO_MMAP |
 				 SNDRV_PCM_INFO_MMAP_VALID |
 				 SNDRV_PCM_INFO_NONINTERLEAVED |
 				 SNDRV_PCM_INFO_SYNC_START),
-	formats:		SNDRV_PCM_FMTBIT_S32_LE,
-	rates:			(SNDRV_PCM_RATE_44100 | 
+	.formats =		SNDRV_PCM_FMTBIT_S32_LE,
+	.rates =		(SNDRV_PCM_RATE_44100 | 
 				 SNDRV_PCM_RATE_48000 | 
 				 SNDRV_PCM_RATE_88200 | 
 				 SNDRV_PCM_RATE_96000),
-	rate_min:		44100,
-	rate_max:		96000,
-	channels_min:		10,
-	channels_max:		26,
-	buffer_bytes_max:	1024*1024,
-	period_bytes_min:	1,
-	period_bytes_max:	1024*1024,
-	periods_min:		2,
-	periods_max:		2,
-	fifo_size:		0,
+	.rate_min =		44100,
+	.rate_max =		96000,
+	.channels_min =		10,
+	.channels_max =		26,
+	.buffer_bytes_max =	1024*1024,
+	.period_bytes_min =	1,
+	.period_bytes_max =	1024*1024,
+	.periods_min =		2,
+	.periods_max =		2,
+	.fifo_size =		0,
 };
 
 static unsigned int period_sizes[] = { 64, 128, 256, 512, 1024, 2048, 4096, 8192 };
@@ -2315,9 +2315,9 @@
 #define PERIOD_SIZES sizeof(period_sizes) / sizeof(period_sizes[0])
 
 static snd_pcm_hw_constraint_list_t hw_constraints_period_sizes = {
-	count: PERIOD_SIZES,
-	list: period_sizes,
-	mask: 0
+	.count = PERIOD_SIZES,
+	.list = period_sizes,
+	.mask = 0
 };
 
 static int snd_rme9652_hw_rule_channels(snd_pcm_hw_params_t *params,
@@ -2492,26 +2492,26 @@
 }
 
 static snd_pcm_ops_t snd_rme9652_playback_ops = {
-	open:		snd_rme9652_playback_open,
-	close:		snd_rme9652_playback_release,
-	ioctl:		snd_rme9652_ioctl,
-	hw_params:	snd_rme9652_hw_params,
-	prepare:	snd_rme9652_prepare,
-	trigger:	snd_rme9652_trigger,
-	pointer:	snd_rme9652_hw_pointer,
-	copy:		snd_rme9652_playback_copy,
-	silence:	snd_rme9652_hw_silence,
+	.open =		snd_rme9652_playback_open,
+	.close =	snd_rme9652_playback_release,
+	.ioctl =	snd_rme9652_ioctl,
+	.hw_params =	snd_rme9652_hw_params,
+	.prepare =	snd_rme9652_prepare,
+	.trigger =	snd_rme9652_trigger,
+	.pointer =	snd_rme9652_hw_pointer,
+	.copy =		snd_rme9652_playback_copy,
+	.silence =	snd_rme9652_hw_silence,
 };
 
 static snd_pcm_ops_t snd_rme9652_capture_ops = {
-	open:		snd_rme9652_capture_open,
-	close:		snd_rme9652_capture_release,
-	ioctl:		snd_rme9652_ioctl,
-	hw_params:	snd_rme9652_hw_params,
-	prepare:	snd_rme9652_prepare,
-	trigger:	snd_rme9652_trigger,
-	pointer:	snd_rme9652_hw_pointer,
-	copy:		snd_rme9652_capture_copy,
+	.open =		snd_rme9652_capture_open,
+	.close =	snd_rme9652_capture_release,
+	.ioctl =	snd_rme9652_ioctl,
+	.hw_params =	snd_rme9652_hw_params,
+	.prepare =	snd_rme9652_prepare,
+	.trigger =	snd_rme9652_trigger,
+	.pointer =	snd_rme9652_hw_pointer,
+	.copy =		snd_rme9652_capture_copy,
 };
 
 static int __init snd_rme9652_create_pcm(snd_card_t *card,
@@ -2733,10 +2733,10 @@
 }
 
 static struct pci_driver driver = {
-	name:"RME Digi9652 (Hammerfall)",
-	id_table:snd_rme9652_ids,
-	probe:snd_rme9652_probe,
-	remove:__devexit_p(snd_rme9652_remove),
+	.name ="RME Digi9652 (Hammerfall)",
+	.id_table = snd_rme9652_ids,
+	.probe = snd_rme9652_probe,
+	.remove = __devexit_p(snd_rme9652_remove),
 };
 
 static int __init alsa_card_hammerfall_init(void)
diff -Nru a/sound/pci/sonicvibes.c b/sound/pci/sonicvibes.c
--- a/sound/pci/sonicvibes.c	Mon Oct  7 11:25:26 2002
+++ b/sound/pci/sonicvibes.c	Mon Oct  7 11:25:26 2002
@@ -759,40 +759,40 @@
 
 static snd_pcm_hardware_t snd_sonicvibes_playback =
 {
-	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
 				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
 				 SNDRV_PCM_INFO_MMAP_VALID),
-	formats:		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
-	rates:			SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
-	rate_min:		4000,
-	rate_max:		48000,
-	channels_min:		1,
-	channels_max:		2,
-	buffer_bytes_max:	(128*1024),
-	period_bytes_min:	32,
-	period_bytes_max:	(128*1024),
-	periods_min:		1,
-	periods_max:		1024,
-	fifo_size:		0,
+	.formats =		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
+	.rates =		SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+	.rate_min =		4000,
+	.rate_max =		48000,
+	.channels_min =		1,
+	.channels_max =		2,
+	.buffer_bytes_max =	(128*1024),
+	.period_bytes_min =	32,
+	.period_bytes_max =	(128*1024),
+	.periods_min =		1,
+	.periods_max =		1024,
+	.fifo_size =		0,
 };
 
 static snd_pcm_hardware_t snd_sonicvibes_capture =
 {
-	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
 				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
 				 SNDRV_PCM_INFO_MMAP_VALID),
-	formats:		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
-	rates:			SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
-	rate_min:		4000,
-	rate_max:		48000,
-	channels_min:		1,
-	channels_max:		2,
-	buffer_bytes_max:	(128*1024),
-	period_bytes_min:	32,
-	period_bytes_max:	(128*1024),
-	periods_min:		1,
-	periods_max:		1024,
-	fifo_size:		0,
+	.formats =		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
+	.rates =		SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+	.rate_min =		4000,
+	.rate_max =		48000,
+	.channels_min =		1,
+	.channels_max =		2,
+	.buffer_bytes_max =	(128*1024),
+	.period_bytes_min =	32,
+	.period_bytes_max =	(128*1024),
+	.periods_min =		1,
+	.periods_max =		1024,
+	.fifo_size =		0,
 };
 
 static int snd_sonicvibes_playback_open(snd_pcm_substream_t * substream)
@@ -839,25 +839,25 @@
 }
 
 static snd_pcm_ops_t snd_sonicvibes_playback_ops = {
-	open:		snd_sonicvibes_playback_open,
-	close:		snd_sonicvibes_playback_close,
-	ioctl:		snd_pcm_lib_ioctl,
-	hw_params:	snd_sonicvibes_hw_params,
-	hw_free:	snd_sonicvibes_hw_free,
-	prepare:	snd_sonicvibes_playback_prepare,
-	trigger:	snd_sonicvibes_playback_trigger,
-	pointer:	snd_sonicvibes_playback_pointer,
+	.open =		snd_sonicvibes_playback_open,
+	.close =	snd_sonicvibes_playback_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	snd_sonicvibes_hw_params,
+	.hw_free =	snd_sonicvibes_hw_free,
+	.prepare =	snd_sonicvibes_playback_prepare,
+	.trigger =	snd_sonicvibes_playback_trigger,
+	.pointer =	snd_sonicvibes_playback_pointer,
 };
 
 static snd_pcm_ops_t snd_sonicvibes_capture_ops = {
-	open:		snd_sonicvibes_capture_open,
-	close:		snd_sonicvibes_capture_close,
-	ioctl:		snd_pcm_lib_ioctl,
-	hw_params:	snd_sonicvibes_hw_params,
-	hw_free:	snd_sonicvibes_hw_free,
-	prepare:	snd_sonicvibes_capture_prepare,
-	trigger:	snd_sonicvibes_capture_trigger,
-	pointer:	snd_sonicvibes_capture_pointer,
+	.open =		snd_sonicvibes_capture_open,
+	.close =	snd_sonicvibes_capture_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	snd_sonicvibes_hw_params,
+	.hw_free =	snd_sonicvibes_hw_free,
+	.prepare =	snd_sonicvibes_capture_prepare,
+	.trigger =	snd_sonicvibes_capture_trigger,
+	.pointer =	snd_sonicvibes_capture_pointer,
 };
 
 static void snd_sonicvibes_pcm_free(snd_pcm_t *pcm)
@@ -897,9 +897,9 @@
  */
 
 #define SONICVIBES_MUX(xname, xindex) \
-{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \
-  info: snd_sonicvibes_info_mux, \
-  get: snd_sonicvibes_get_mux, put: snd_sonicvibes_put_mux }
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
+  .info = snd_sonicvibes_info_mux, \
+  .get = snd_sonicvibes_get_mux, .put = snd_sonicvibes_put_mux }
 
 static int snd_sonicvibes_info_mux(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
 {
@@ -953,10 +953,10 @@
 }
 
 #define SONICVIBES_SINGLE(xname, xindex, reg, shift, mask, invert) \
-{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \
-  info: snd_sonicvibes_info_single, \
-  get: snd_sonicvibes_get_single, put: snd_sonicvibes_put_single, \
-  private_value: reg | (shift << 8) | (mask << 16) | (invert << 24) }
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
+  .info = snd_sonicvibes_info_single, \
+  .get = snd_sonicvibes_get_single, .put = snd_sonicvibes_put_single, \
+  .private_value = reg | (shift << 8) | (mask << 16) | (invert << 24) }
 
 static int snd_sonicvibes_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
 {
@@ -1011,10 +1011,10 @@
 }
 
 #define SONICVIBES_DOUBLE(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \
-{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \
-  info: snd_sonicvibes_info_double, \
-  get: snd_sonicvibes_get_double, put: snd_sonicvibes_put_double, \
-  private_value: left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) }
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
+  .info = snd_sonicvibes_info_double, \
+  .get = snd_sonicvibes_get_double, .put = snd_sonicvibes_put_double, \
+  .private_value = left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) }
 
 static int snd_sonicvibes_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
 {
@@ -1258,7 +1258,7 @@
 	unsigned int dmaa, dmac;
 	int err;
 	static snd_device_ops_t ops = {
-		dev_free:	snd_sonicvibes_dev_free,
+		.dev_free =	snd_sonicvibes_dev_free,
 	};
 
 	*rsonic = NULL;
@@ -1538,10 +1538,10 @@
 }
 
 static struct pci_driver driver = {
-	name: "S3 SonicVibes",
-	id_table: snd_sonic_ids,
-	probe: snd_sonic_probe,
-	remove: __devexit_p(snd_sonic_remove),
+	.name = "S3 SonicVibes",
+	.id_table = snd_sonic_ids,
+	.probe = snd_sonic_probe,
+	.remove = __devexit_p(snd_sonic_remove),
 };
 
 static int __init alsa_card_sonicvibes_init(void)
diff -Nru a/sound/pci/trident/trident.c b/sound/pci/trident/trident.c
--- a/sound/pci/trident/trident.c	Mon Oct  7 11:25:25 2002
+++ b/sound/pci/trident/trident.c	Mon Oct  7 11:25:25 2002
@@ -210,13 +210,13 @@
 #endif
 
 static struct pci_driver driver = {
-	name: "Trident4DWaveAudio",
-	id_table: snd_trident_ids,
-	probe: snd_trident_probe,
-	remove: __devexit_p(snd_trident_remove),
+	.name = "Trident4DWaveAudio",
+	.id_table = snd_trident_ids,
+	.probe = snd_trident_probe,
+	.remove = __devexit_p(snd_trident_remove),
 #ifdef CONFIG_PM
-	suspend: snd_card_trident_suspend,
-	resume: snd_card_trident_resume,
+	.suspend = snd_card_trident_suspend,
+	.resume = snd_card_trident_resume,
 #endif
 };
 
diff -Nru a/sound/pci/trident/trident_main.c b/sound/pci/trident/trident_main.c
--- a/sound/pci/trident/trident_main.c	Mon Oct  7 11:25:36 2002
+++ b/sound/pci/trident/trident_main.c	Mon Oct  7 11:25:36 2002
@@ -25,7 +25,6 @@
  *
  */
 
-#define __NO_VERSION__
 #include <sound/driver.h>
 #include <asm/io.h>
 #include <linux/delay.h>
@@ -1695,23 +1694,23 @@
 
 static snd_pcm_hardware_t snd_trident_playback =
 {
-	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
 				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
 				 SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START |
 				 SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME),
-	formats:		(SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE |
+	.formats =		(SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE |
 				 SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U16_LE),
-	rates:			SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
-	rate_min:		4000,
-	rate_max:		48000,
-	channels_min:		1,
-	channels_max:		2,
-	buffer_bytes_max:	(256*1024),
-	period_bytes_min:	64,
-	period_bytes_max:	(256*1024),
-	periods_min:		1,
-	periods_max:		1024,
-	fifo_size:		0,
+	.rates =		SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+	.rate_min =		4000,
+	.rate_max =		48000,
+	.channels_min =		1,
+	.channels_max =		2,
+	.buffer_bytes_max =	(256*1024),
+	.period_bytes_min =	64,
+	.period_bytes_max =	(256*1024),
+	.periods_min =		1,
+	.periods_max =		1024,
+	.fifo_size =		0,
 };
 
 /*
@@ -1720,23 +1719,23 @@
 
 static snd_pcm_hardware_t snd_trident_capture =
 {
-	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
 				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
 				 SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START |
 				 SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME),
-	formats:		(SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE |
+	.formats =		(SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE |
 				 SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U16_LE),
-	rates:			SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
-	rate_min:		4000,
-	rate_max:		48000,
-	channels_min:		1,
-	channels_max:		2,
-	buffer_bytes_max:	(128*1024),
-	period_bytes_min:	64,
-	period_bytes_max:	(128*1024),
-	periods_min:		1,
-	periods_max:		1024,
-	fifo_size:		0,
+	.rates =		SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+	.rate_min =		4000,
+	.rate_max =		48000,
+	.channels_min =		1,
+	.channels_max =		2,
+	.buffer_bytes_max =	(128*1024),
+	.period_bytes_min =	64,
+	.period_bytes_max =	(128*1024),
+	.periods_min =		1,
+	.periods_max =		1024,
+	.fifo_size =		0,
 };
 
 /*
@@ -1745,22 +1744,22 @@
 
 static snd_pcm_hardware_t snd_trident_foldback =
 {
-	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
 				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
 				 SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START |
 				 SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME),
-	formats:		SNDRV_PCM_FMTBIT_S16_LE,
-	rates:			SNDRV_PCM_RATE_48000,
-	rate_min:		48000,
-	rate_max:		48000,
-	channels_min:		2,
-	channels_max:		2,
-	buffer_bytes_max:	(128*1024),
-	period_bytes_min:	64,
-	period_bytes_max:	(128*1024),
-	periods_min:		1,
-	periods_max:		1024,
-	fifo_size:		0,
+	.formats =		SNDRV_PCM_FMTBIT_S16_LE,
+	.rates =		SNDRV_PCM_RATE_48000,
+	.rate_min =		48000,
+	.rate_max =		48000,
+	.channels_min =		2,
+	.channels_max =		2,
+	.buffer_bytes_max =	(128*1024),
+	.period_bytes_min =	64,
+	.period_bytes_max =	(128*1024),
+	.periods_min =		1,
+	.periods_max =		1024,
+	.fifo_size =		0,
 };
 
 /*
@@ -1769,23 +1768,23 @@
 
 static snd_pcm_hardware_t snd_trident_spdif =
 {
-	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
 				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
 				 SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START |
 				 SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME),
-	formats:		SNDRV_PCM_FMTBIT_S16_LE,
-	rates:			(SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
+	.formats =		SNDRV_PCM_FMTBIT_S16_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:	(128*1024),
-	period_bytes_min:	64,
-	period_bytes_max:	(128*1024),
-	periods_min:		1,
-	periods_max:		1024,
-	fifo_size:		0,
+	.rate_min =		32000,
+	.rate_max =		48000,
+	.channels_min =		2,
+	.channels_max =		2,
+	.buffer_bytes_max =	(128*1024),
+	.period_bytes_min =	64,
+	.period_bytes_max =	(128*1024),
+	.periods_min =		1,
+	.periods_max =		1024,
+	.fifo_size =		0,
 };
 
 static void snd_trident_pcm_free_substream(snd_pcm_runtime_t *runtime)
@@ -2026,58 +2025,58 @@
   ---------------------------------------------------------------------------*/
 
 static snd_pcm_ops_t snd_trident_playback_ops = {
-	open:		snd_trident_playback_open,
-	close:		snd_trident_playback_close,
-	ioctl:		snd_trident_ioctl,
-	hw_params:	snd_trident_playback_hw_params,
-	hw_free:	snd_trident_playback_hw_free,
-	prepare:	snd_trident_playback_prepare,
-	trigger:	snd_trident_trigger,
-	pointer:	snd_trident_playback_pointer,
+	.open =		snd_trident_playback_open,
+	.close =	snd_trident_playback_close,
+	.ioctl =	snd_trident_ioctl,
+	.hw_params =	snd_trident_playback_hw_params,
+	.hw_free =	snd_trident_playback_hw_free,
+	.prepare =	snd_trident_playback_prepare,
+	.trigger =	snd_trident_trigger,
+	.pointer =	snd_trident_playback_pointer,
 };
 
 static snd_pcm_ops_t snd_trident_capture_ops = {
-	open:		snd_trident_capture_open,
-	close:		snd_trident_capture_close,
-	ioctl:		snd_trident_ioctl,
-	hw_params:	snd_trident_capture_hw_params,
-	hw_free:	snd_trident_capture_hw_free,
-	prepare:	snd_trident_capture_prepare,
-	trigger:	snd_trident_trigger,
-	pointer:	snd_trident_capture_pointer,
+	.open =		snd_trident_capture_open,
+	.close =	snd_trident_capture_close,
+	.ioctl =	snd_trident_ioctl,
+	.hw_params =	snd_trident_capture_hw_params,
+	.hw_free =	snd_trident_capture_hw_free,
+	.prepare =	snd_trident_capture_prepare,
+	.trigger =	snd_trident_trigger,
+	.pointer =	snd_trident_capture_pointer,
 };
 
 static snd_pcm_ops_t snd_trident_si7018_capture_ops = {
-	open:		snd_trident_capture_open,
-	close:		snd_trident_capture_close,
-	ioctl:		snd_trident_ioctl,
-	hw_params:	snd_trident_si7018_capture_hw_params,
-	hw_free:	snd_trident_si7018_capture_hw_free,
-	prepare:	snd_trident_si7018_capture_prepare,
-	trigger:	snd_trident_trigger,
-	pointer:	snd_trident_playback_pointer,
+	.open =		snd_trident_capture_open,
+	.close =	snd_trident_capture_close,
+	.ioctl =	snd_trident_ioctl,
+	.hw_params =	snd_trident_si7018_capture_hw_params,
+	.hw_free =	snd_trident_si7018_capture_hw_free,
+	.prepare =	snd_trident_si7018_capture_prepare,
+	.trigger =	snd_trident_trigger,
+	.pointer =	snd_trident_playback_pointer,
 };
 
 static snd_pcm_ops_t snd_trident_foldback_ops = {
-	open:		snd_trident_foldback_open,
-	close:		snd_trident_foldback_close,
-	ioctl:		snd_trident_ioctl,
-	hw_params:	snd_trident_foldback_hw_params,
-	hw_free:	snd_trident_foldback_hw_free,
-	prepare:	snd_trident_foldback_prepare,
-	trigger:	snd_trident_trigger,
-	pointer:	snd_trident_playback_pointer,
+	.open =		snd_trident_foldback_open,
+	.close =	snd_trident_foldback_close,
+	.ioctl =	snd_trident_ioctl,
+	.hw_params =	snd_trident_foldback_hw_params,
+	.hw_free =	snd_trident_foldback_hw_free,
+	.prepare =	snd_trident_foldback_prepare,
+	.trigger =	snd_trident_trigger,
+	.pointer =	snd_trident_playback_pointer,
 };
 
 static snd_pcm_ops_t snd_trident_spdif_ops = {
-	open:		snd_trident_spdif_open,
-	close:		snd_trident_spdif_close,
-	ioctl:		snd_trident_ioctl,
-	hw_params:	snd_trident_spdif_hw_params,
-	hw_free:	snd_trident_spdif_hw_free,
-	prepare:	snd_trident_spdif_prepare,
-	trigger:	snd_trident_trigger,
-	pointer:	snd_trident_spdif_pointer,
+	.open =		snd_trident_spdif_open,
+	.close =	snd_trident_spdif_close,
+	.ioctl =	snd_trident_ioctl,
+	.hw_params =	snd_trident_spdif_hw_params,
+	.hw_free =	snd_trident_spdif_hw_free,
+	.prepare =	snd_trident_spdif_prepare,
+	.trigger =	snd_trident_trigger,
+	.pointer =	snd_trident_spdif_pointer,
 };
 
 /*---------------------------------------------------------------------------
@@ -2294,12 +2293,12 @@
 
 static snd_kcontrol_new_t snd_trident_spdif_control __devinitdata =
 {
-	iface:		SNDRV_CTL_ELEM_IFACE_MIXER,
-	name:           SNDRV_CTL_NAME_IEC958("",PLAYBACK,SWITCH),
-	info:		snd_trident_spdif_control_info,
-	get:		snd_trident_spdif_control_get,
-	put:		snd_trident_spdif_control_put,
-	private_value:  0x28,
+	.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name =         SNDRV_CTL_NAME_IEC958("",PLAYBACK,SWITCH),
+	.info =		snd_trident_spdif_control_info,
+	.get =		snd_trident_spdif_control_get,
+	.put =		snd_trident_spdif_control_put,
+	.private_value = 0x28,
 };
 
 /*---------------------------------------------------------------------------
@@ -2353,11 +2352,11 @@
 
 static snd_kcontrol_new_t snd_trident_spdif_default __devinitdata =
 {
-	iface:		SNDRV_CTL_ELEM_IFACE_PCM,
-	name:           SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
-	info:		snd_trident_spdif_default_info,
-	get:		snd_trident_spdif_default_get,
-	put:		snd_trident_spdif_default_put
+	.iface =		SNDRV_CTL_ELEM_IFACE_PCM,
+	.name =           SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
+	.info =		snd_trident_spdif_default_info,
+	.get =		snd_trident_spdif_default_get,
+	.put =		snd_trident_spdif_default_put
 };
 
 /*---------------------------------------------------------------------------
@@ -2385,11 +2384,11 @@
 
 static snd_kcontrol_new_t snd_trident_spdif_mask __devinitdata =
 {
-	access:		SNDRV_CTL_ELEM_ACCESS_READ,
-	iface:		SNDRV_CTL_ELEM_IFACE_PCM,
-	name:           SNDRV_CTL_NAME_IEC958("",PLAYBACK,MASK),
-	info:		snd_trident_spdif_mask_info,
-	get:		snd_trident_spdif_mask_get,
+	.access =	SNDRV_CTL_ELEM_ACCESS_READ,
+	.iface =	SNDRV_CTL_ELEM_IFACE_PCM,
+	.name =         SNDRV_CTL_NAME_IEC958("",PLAYBACK,MASK),
+	.info =		snd_trident_spdif_mask_info,
+	.get =		snd_trident_spdif_mask_get,
 };
 
 /*---------------------------------------------------------------------------
@@ -2443,12 +2442,12 @@
 
 static snd_kcontrol_new_t snd_trident_spdif_stream __devinitdata =
 {
-	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_trident_spdif_stream_info,
-	get:		snd_trident_spdif_stream_get,
-	put:		snd_trident_spdif_stream_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_trident_spdif_stream_info,
+	.get =		snd_trident_spdif_stream_get,
+	.put =		snd_trident_spdif_stream_put
 };
 
 /*---------------------------------------------------------------------------
@@ -2502,12 +2501,12 @@
 
 static snd_kcontrol_new_t snd_trident_ac97_rear_control __devinitdata =
 {
-	iface:		SNDRV_CTL_ELEM_IFACE_MIXER,
-	name:           "Rear Path",
-	info:		snd_trident_ac97_control_info,
-	get:		snd_trident_ac97_control_get,
-	put:		snd_trident_ac97_control_put,
-	private_value:  4,
+	.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name =         "Rear Path",
+	.info =		snd_trident_ac97_control_info,
+	.get =		snd_trident_ac97_control_get,
+	.put =		snd_trident_ac97_control_put,
+	.private_value = 4,
 };
 
 /*---------------------------------------------------------------------------
@@ -2558,22 +2557,22 @@
 
 static snd_kcontrol_new_t snd_trident_vol_music_control __devinitdata =
 {
-	iface:		SNDRV_CTL_ELEM_IFACE_MIXER,
-	name:           "Music Playback Volume",
-	info:		snd_trident_vol_control_info,
-	get:		snd_trident_vol_control_get,
-	put:		snd_trident_vol_control_put,
-	private_value:  16,
+	.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name =         "Music Playback Volume",
+	.info =		snd_trident_vol_control_info,
+	.get =		snd_trident_vol_control_get,
+	.put =		snd_trident_vol_control_put,
+	.private_value = 16,
 };
 
 static snd_kcontrol_new_t snd_trident_vol_wave_control __devinitdata =
 {
-	iface:		SNDRV_CTL_ELEM_IFACE_MIXER,
-	name:           "Wave Playback Volume",
-	info:		snd_trident_vol_control_info,
-	get:		snd_trident_vol_control_get,
-	put:		snd_trident_vol_control_put,
-	private_value:  0,
+	.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name =         "Wave Playback Volume",
+	.info =		snd_trident_vol_control_info,
+	.get =		snd_trident_vol_control_get,
+	.put =		snd_trident_vol_control_put,
+	.private_value = 0,
 };
 
 /*---------------------------------------------------------------------------
@@ -2634,12 +2633,12 @@
 
 static snd_kcontrol_new_t snd_trident_pcm_vol_control __devinitdata =
 {
-	iface:		SNDRV_CTL_ELEM_IFACE_MIXER,
-	name:           "PCM Front Playback Volume",
-	access:		SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
-	info:		snd_trident_pcm_vol_control_info,
-	get:		snd_trident_pcm_vol_control_get,
-	put:		snd_trident_pcm_vol_control_put,
+	.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name =         "PCM Front Playback Volume",
+	.access =	SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
+	.info =		snd_trident_pcm_vol_control_info,
+	.get =		snd_trident_pcm_vol_control_get,
+	.put =		snd_trident_pcm_vol_control_put,
 };
 
 /*---------------------------------------------------------------------------
@@ -2695,12 +2694,12 @@
 
 static snd_kcontrol_new_t snd_trident_pcm_pan_control __devinitdata =
 {
-	iface:		SNDRV_CTL_ELEM_IFACE_MIXER,
-	name:           "PCM Pan Playback Control",
-	access:		SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
-	info:		snd_trident_pcm_pan_control_info,
-	get:		snd_trident_pcm_pan_control_get,
-	put:		snd_trident_pcm_pan_control_put,
+	.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name =         "PCM Pan Playback Control",
+	.access =	SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
+	.info =		snd_trident_pcm_pan_control_info,
+	.get =		snd_trident_pcm_pan_control_get,
+	.put =		snd_trident_pcm_pan_control_put,
 };
 
 /*---------------------------------------------------------------------------
@@ -2748,12 +2747,12 @@
 
 static snd_kcontrol_new_t snd_trident_pcm_rvol_control __devinitdata =
 {
-	iface:		SNDRV_CTL_ELEM_IFACE_MIXER,
-	name:           "PCM Reverb Playback Volume",
-	access:		SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
-	info:		snd_trident_pcm_rvol_control_info,
-	get:		snd_trident_pcm_rvol_control_get,
-	put:		snd_trident_pcm_rvol_control_put,
+	.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name =         "PCM Reverb Playback Volume",
+	.access =	SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
+	.info =		snd_trident_pcm_rvol_control_info,
+	.get =		snd_trident_pcm_rvol_control_get,
+	.put =		snd_trident_pcm_rvol_control_put,
 };
 
 /*---------------------------------------------------------------------------
@@ -2801,12 +2800,12 @@
 
 static snd_kcontrol_new_t snd_trident_pcm_cvol_control __devinitdata =
 {
-	iface:		SNDRV_CTL_ELEM_IFACE_MIXER,
-	name:           "PCM Chorus Playback Volume",
-	access:		SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
-	info:		snd_trident_pcm_cvol_control_info,
-	get:		snd_trident_pcm_cvol_control_get,
-	put:		snd_trident_pcm_cvol_control_put,
+	.iface =	SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name =         "PCM Chorus Playback Volume",
+	.access =	SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
+	.info =		snd_trident_pcm_cvol_control_info,
+	.get =		snd_trident_pcm_cvol_control_get,
+	.put =		snd_trident_pcm_cvol_control_put,
 };
 
 static void snd_trident_notify_pcm_change1(snd_card_t * card, snd_kcontrol_t *kctl, int activate)
@@ -3153,7 +3152,7 @@
 	snd_trident_voice_t *voice;
 	snd_trident_pcm_mixer_t *tmix;
 	static snd_device_ops_t ops = {
-		dev_free:	snd_trident_dev_free,
+		.dev_free =	snd_trident_dev_free,
 	};
 
 	*rtrident = NULL;
diff -Nru a/sound/pci/trident/trident_memory.c b/sound/pci/trident/trident_memory.c
--- a/sound/pci/trident/trident_memory.c	Mon Oct  7 11:25:36 2002
+++ b/sound/pci/trident/trident_memory.c	Mon Oct  7 11:25:36 2002
@@ -23,7 +23,6 @@
  *
  */
 
-#define __NO_VERSION__
 #include <sound/driver.h>
 #include <asm/io.h>
 #include <linux/time.h>
diff -Nru a/sound/pci/via686.c b/sound/pci/via686.c
--- a/sound/pci/via686.c	Mon Oct  7 11:25:29 2002
+++ /dev/null	Wed Dec 31 16:00:00 1969
@@ -1,1239 +0,0 @@
-/*
- *   ALSA driver for VIA VT82C686A (South Bridge)
- *
- *	Copyright (c) 2000 Jaroslav Kysela <perex@suse.cz>
- *
- *   This program is free software; you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License as published by
- *   the Free Software Foundation; either version 2 of the License, or
- *   (at your option) any later version.
- *
- *   This program is distributed in the hope that it will be useful,
- *   but WITHOUT ANY WARRANTY; without even the implied warranty of
- *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *   GNU General Public License for more details.
- *
- *   You should have received a copy of the GNU General Public License
- *   along with this program; if not, write to the Free Software
- *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
- *
- */      
-
-#include <sound/driver.h>
-#include <asm/io.h>
-#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>
-#include <sound/pcm_sgbuf.h>
-#include <sound/pcm_params.h>
-#include <sound/info.h>
-#include <sound/ac97_codec.h>
-#include <sound/mpu401.h>
-#define SNDRV_GET_ID
-#include <sound/initval.h>
-
-MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
-MODULE_DESCRIPTION("VIA VT82C686A");
-MODULE_LICENSE("GPL");
-MODULE_CLASSES("{sound}");
-MODULE_DEVICES("{{VIA,VT82C686A,pci},{VIA,VT82C686B}}");
-
-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 */
-static long snd_mpu_port[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = -1};
-static int snd_ac97_clock[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 48000};
-
-MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
-MODULE_PARM_DESC(snd_index, "Index value for VIA 82C686A bridge.");
-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 VIA 82C686A bridge.");
-MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC);
-MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
-MODULE_PARM_DESC(snd_enable, "Enable audio part of VIA 82C686A bridge.");
-MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC);
-MODULE_PARM(snd_mpu_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
-MODULE_PARM_DESC(snd_mpu_port, "MPU-401 port.");
-MODULE_PARM_SYNTAX(snd_mpu_port, SNDRV_PORT_DESC);
-MODULE_PARM(snd_ac97_clock, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
-MODULE_PARM_DESC(snd_ac97_clock, "AC'97 codec clock (default 48000Hz).");
-MODULE_PARM_SYNTAX(snd_ac97_clock, SNDRV_ENABLED ",default:48000");
-
-/*
- *  Direct registers
- */
-
-#ifndef PCI_DEVICE_ID_VIA_82C686_5
-#define PCI_DEVICE_ID_VIA_82C686_5	0x3058
-#endif
-
-#define VIAREG(via, x) ((via)->port + VIA_REG_##x)
-
-/* offsets */
-#define VIA_REG_OFFSET_STATUS		0x00	/* byte - channel status */
-#define   VIA_REG_STAT_ACTIVE		0x80	/* RO */
-#define   VIA_REG_STAT_PAUSED		0x40	/* RO */
-#define   VIA_REG_STAT_TRIGGER_QUEUED	0x08	/* RO */
-#define   VIA_REG_STAT_STOPPED		0x04	/* RWC */
-#define   VIA_REG_STAT_EOL		0x02	/* RWC */
-#define   VIA_REG_STAT_FLAG		0x01	/* RWC */
-#define VIA_REG_OFFSET_CONTROL		0x01	/* byte - channel control */
-#define   VIA_REG_CTRL_START		0x80	/* WO */
-#define   VIA_REG_CTRL_TERMINATE	0x40	/* WO */
-#define   VIA_REG_CTRL_PAUSE		0x08	/* RW */
-#define   VIA_REG_CTRL_RESET		0x01	/* RW - probably reset? undocumented */
-#define VIA_REG_OFFSET_TYPE		0x02	/* byte - channel type */
-#define   VIA_REG_TYPE_AUTOSTART	0x80	/* RW - autostart at EOL */
-#define   VIA_REG_TYPE_16BIT		0x20	/* RW */
-#define   VIA_REG_TYPE_STEREO		0x10	/* RW */
-#define   VIA_REG_TYPE_INT_LLINE	0x00
-#define   VIA_REG_TYPE_INT_LSAMPLE	0x04
-#define   VIA_REG_TYPE_INT_LESSONE	0x08
-#define   VIA_REG_TYPE_INT_MASK		0x0c
-#define   VIA_REG_TYPE_INT_EOL		0x02
-#define   VIA_REG_TYPE_INT_FLAG		0x01
-#define VIA_REG_OFFSET_TABLE_PTR	0x04	/* dword - channel table pointer */
-#define VIA_REG_OFFSET_CURR_PTR		0x04	/* dword - channel current pointer */
-#define VIA_REG_OFFSET_CURR_COUNT	0x0c	/* dword - channel current count */
-/* playback block */
-#define VIA_REG_PLAYBACK_STATUS		0x00	/* byte - channel status */
-#define VIA_REG_PLAYBACK_CONTROL	0x01	/* byte - channel control */
-#define VIA_REG_PLAYBACK_TYPE		0x02	/* byte - channel type */
-#define VIA_REG_PLAYBACK_TABLE_PTR	0x04	/* dword - channel table pointer */
-#define VIA_REG_PLAYBACK_CURR_PTR	0x04	/* dword - channel current pointer */
-#define VIA_REG_PLAYBACK_CURR_COUNT	0x0c	/* dword - channel current count */
-/* capture block */
-#define VIA_REG_CAPTURE_STATUS		0x10	/* byte - channel status */
-#define VIA_REG_CAPTURE_CONTROL		0x11	/* byte - channel control */
-#define VIA_REG_CAPTURE_TYPE		0x12	/* byte - channel type */
-#define VIA_REG_CAPTURE_TABLE_PTR	0x14	/* dword - channel table pointer */
-#define VIA_REG_CAPTURE_CURR_PTR	0x14	/* dword - channel current pointer */
-#define VIA_REG_CAPTURE_CURR_COUNT	0x1c	/* dword - channel current count */
-/* FM block */
-#define VIA_REG_FM_STATUS		0x20	/* byte - channel status */
-#define VIA_REG_FM_CONTROL		0x21	/* byte - channel control */
-#define VIA_REG_FM_TYPE			0x22	/* byte - channel type */
-#define VIA_REG_FM_TABLE_PTR		0x24	/* dword - channel table pointer */
-#define VIA_REG_FM_CURR_PTR		0x24	/* dword - channel current pointer */
-#define VIA_REG_FM_CURR_COUNT		0x2c	/* dword - channel current count */
-/* AC'97 */
-#define VIA_REG_AC97			0x80	/* dword */
-#define   VIA_REG_AC97_CODEC_ID_MASK	(3<<30)
-#define   VIA_REG_AC97_CODEC_ID_SHIFT	30
-#define   VIA_REG_AC97_CODEC_ID_PRIMARY	0x00
-#define   VIA_REG_AC97_CODEC_ID_SECONDARY 0x01
-#define   VIA_REG_AC97_SECONDARY_VALID	(1<<27)
-#define   VIA_REG_AC97_PRIMARY_VALID	(1<<25)
-#define   VIA_REG_AC97_BUSY		(1<<24)
-#define   VIA_REG_AC97_READ		(1<<23)
-#define   VIA_REG_AC97_CMD_SHIFT	16
-#define   VIA_REG_AC97_CMD_MASK		0x7e
-#define   VIA_REG_AC97_DATA_SHIFT	0
-#define   VIA_REG_AC97_DATA_MASK	0xffff
-#define VIA_REG_SGD_SHADOW		0x84	/* dword */
-
-#define VIA_TBL_BIT_FLAG	0x40000000
-#define VIA_TBL_BIT_EOL		0x80000000
-
-/*
- * pcm stream
- */
-
-typedef struct {
-	unsigned long reg_offset;
-        snd_pcm_substream_t *substream;
-	int running;
-        unsigned int size;
-        unsigned int fragsize;
-	unsigned int frags;
-	unsigned int lastptr;
-	unsigned int lastcount;
-	unsigned int page_per_frag;
-	unsigned int curidx;
-	unsigned int tbl_entries;	/* number of descriptor table entries */
-	unsigned int tbl_size;		/* size of a table entry */
-	u32 *table; /* physical address + flag */
-	dma_addr_t table_addr;
-} viadev_t;
-
-
-static int build_via_table(viadev_t *dev, snd_pcm_substream_t *substream,
-			   struct pci_dev *pci)
-{
-	int i, size;
-	struct snd_sg_buf *sgbuf = snd_magic_cast(snd_pcm_sgbuf_t, substream->dma_private, return -EINVAL);
-
-	if (dev->table) {
-		snd_free_pci_pages(pci, PAGE_ALIGN(dev->tbl_entries * 8), dev->table, dev->table_addr);
-		dev->table = NULL;
-	}
-
-	/* allocate buffer descriptor lists */
-	if (dev->fragsize < PAGE_SIZE) {
-		dev->tbl_size = dev->fragsize;
-		dev->tbl_entries = dev->frags;
-		dev->page_per_frag = 1;
-	} else {
-		dev->tbl_size = PAGE_SIZE;
-		dev->tbl_entries = sgbuf->pages;
-		dev->page_per_frag = dev->fragsize >> PAGE_SHIFT;
-	}
-	/* the start of each lists must be aligned to 8 bytes,
-	 * but the kernel pages are much bigger, so we don't care
-	 */
-	dev->table = (u32*)snd_malloc_pci_pages(pci, PAGE_ALIGN(dev->tbl_entries * 8), &dev->table_addr);
-	if (! dev->table)
-		return -ENOMEM;
-
-	if (dev->tbl_size < PAGE_SIZE) {
-		for (i = 0; i < dev->tbl_entries; i++)
-			dev->table[i << 1] = cpu_to_le32((u32)sgbuf->table[0].addr + dev->fragsize * i);
-	} else {
-		for (i = 0; i < dev->tbl_entries; i++)
-			dev->table[i << 1] = cpu_to_le32((u32)sgbuf->table[i].addr);
-	}
-	size = dev->size;
-	for (i = 0; i < dev->tbl_entries - 1; i++) {
-		dev->table[(i << 1) + 1] = cpu_to_le32(VIA_TBL_BIT_FLAG | dev->tbl_size);
-		size -= dev->tbl_size;
-	}
-	dev->table[(dev->tbl_entries << 1) - 1] = cpu_to_le32(VIA_TBL_BIT_EOL | size);
-
-	return 0;
-}
-
-
-static void clean_via_table(viadev_t *dev, snd_pcm_substream_t *substream,
-			    struct pci_dev *pci)
-{
-	if (dev->table) {
-		snd_free_pci_pages(pci, PAGE_ALIGN(dev->tbl_entries * 8), dev->table, dev->table_addr);
-		dev->table = NULL;
-	}
-}
-
-
-/*
- */
-
-typedef struct _snd_via686a via686a_t;
-#define chip_t via686a_t
-
-struct _snd_via686a {
-	int irq;
-
-	unsigned long port;
-	struct resource *res_port;
-	unsigned char revision;
-
-	unsigned char old_legacy;
-	unsigned char old_legacy_cfg;
-
-	struct pci_dev *pci;
-	snd_card_t *card;
-
-	snd_pcm_t *pcm;
-	/*snd_pcm_t *pcm_fm;*/
-	viadev_t playback;
-	viadev_t capture;
-	/*viadev_t playback_fm;*/
-
-	snd_rawmidi_t *rmidi;
-
-	ac97_t *ac97;
-	unsigned int ac97_clock;
-	unsigned int ac97_secondary;	/* secondary AC'97 codec is present */
-
-	spinlock_t reg_lock;
-	snd_info_entry_t *proc_entry;
-};
-
-static struct pci_device_id snd_via686a_ids[] __devinitdata = {
-	{ 0x1106, 0x3058, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, },	/* 686A */
-	{ 0, }
-};
-
-MODULE_DEVICE_TABLE(pci, snd_via686a_ids);
-
-/*
- *  Basic I/O
- */
-
-static inline unsigned int snd_via686a_codec_xread(via686a_t *chip)
-{
-	return inl(VIAREG(chip, AC97));
-}
- 
-static inline void snd_via686a_codec_xwrite(via686a_t *chip, unsigned int val)
-{
-	outl(val, VIAREG(chip, AC97));
-}
- 
-static int snd_via686a_codec_ready(via686a_t *chip, int secondary)
-{
-	unsigned int timeout = 1000;	/* 1ms */
-	unsigned int val;
-	
-	while (timeout-- > 0) {
-		udelay(1);
-		if (!((val = snd_via686a_codec_xread(chip)) & VIA_REG_AC97_BUSY))
-			return val & 0xffff;
-	}
-	snd_printk("codec_ready: codec %i is not ready [0x%x]\n", secondary, snd_via686a_codec_xread(chip));
-	return -EIO;
-}
- 
-static int snd_via686a_codec_valid(via686a_t *chip, int secondary)
-{
-	unsigned int timeout = 1000;	/* 1ms */
-	unsigned int val;
-	unsigned int stat = !secondary ? VIA_REG_AC97_PRIMARY_VALID :
-					 VIA_REG_AC97_SECONDARY_VALID;
-	
-	while (timeout-- > 0) {
-		udelay(1);
-		if ((val = snd_via686a_codec_xread(chip)) & stat)
-			return val & 0xffff;
-	}
-	snd_printk("codec_valid: codec %i is not valid [0x%x]\n", secondary, snd_via686a_codec_xread(chip));
-	return -EIO;
-}
- 
-static void snd_via686a_codec_wait(ac97_t *ac97)
-{
-	via686a_t *chip = snd_magic_cast(via686a_t, ac97->private_data, return);
-	int err;
-	err = snd_via686a_codec_ready(chip, ac97->num);
-	/* here we need to wait fairly for long time.. */
-	set_current_state(TASK_UNINTERRUPTIBLE);
-	schedule_timeout(HZ/2);
-}
-
-static void snd_via686a_codec_write(ac97_t *ac97,
-				    unsigned short reg,
-				    unsigned short val)
-{
-	via686a_t *chip = snd_magic_cast(via686a_t, ac97->private_data, return);
-	unsigned int xval;
-	
-	xval = !ac97->num ? VIA_REG_AC97_CODEC_ID_PRIMARY : VIA_REG_AC97_CODEC_ID_SECONDARY;
-	xval <<= VIA_REG_AC97_CODEC_ID_SHIFT;
-	xval |= reg << VIA_REG_AC97_CMD_SHIFT;
-	xval |= val << VIA_REG_AC97_DATA_SHIFT;
-	spin_lock(&chip->reg_lock);
-	snd_via686a_codec_xwrite(chip, xval);
-	snd_via686a_codec_ready(chip, ac97->num);
-	spin_unlock(&chip->reg_lock);
-}
-
-static unsigned short snd_via686a_codec_read(ac97_t *ac97, unsigned short reg)
-{
-	via686a_t *chip = snd_magic_cast(via686a_t, ac97->private_data, return ~0);
-	unsigned int xval, val = 0xffff;
-	int again = 0;
-
-	xval = !ac97->num ? VIA_REG_AC97_CODEC_ID_PRIMARY : VIA_REG_AC97_CODEC_ID_SECONDARY;
-	xval <<= VIA_REG_AC97_CODEC_ID_SHIFT;
-	xval = (!ac97->num ? VIA_REG_AC97_PRIMARY_VALID : VIA_REG_AC97_SECONDARY_VALID);
-	xval |= VIA_REG_AC97_READ;
-	xval |= reg << VIA_REG_AC97_CMD_SHIFT;
-	spin_lock(&chip->reg_lock);
-      	while (1) {
-      		if (again++ > 3) {
-		        spin_unlock(&chip->reg_lock);
-		      	return 0xffff;
-		}
-		snd_via686a_codec_xwrite(chip, xval);
-		if (snd_via686a_codec_ready(chip, ac97->num) < 0)
-			continue;
-		if (snd_via686a_codec_valid(chip, ac97->num) >= 0) {
-			udelay(25);
-			val = snd_via686a_codec_xread(chip);
-			break;
-		}
-	}
-	spin_unlock(&chip->reg_lock);
-	return val & 0xffff;
-}
-
-#if 0
-static void snd_via686a_channel_print(via686a_t *chip, viadev_t *viadev)
-{
-	unsigned long port = chip->port + viadev->reg_offset;
-
-	printk("[0x%x] status = 0x%x, control = 0x%x, type = 0x%x, ptr = 0x%x, count = 0x%x\n",
-			port,
-			inb(port + VIA_REG_OFFSET_STATUS),
-			inb(port + VIA_REG_OFFSET_CONTROL),
-			inb(port + VIA_REG_OFFSET_TYPE),
-			inl(port + VIA_REG_OFFSET_CURR_PTR),
-			inl(port + VIA_REG_OFFSET_CURR_COUNT));
-}
-#endif
-
-static void snd_via686a_channel_reset(via686a_t *chip, viadev_t *viadev)
-{
-	unsigned long port = chip->port + viadev->reg_offset;
-
-	outb(VIA_REG_CTRL_PAUSE | VIA_REG_CTRL_TERMINATE | VIA_REG_CTRL_RESET, port + VIA_REG_OFFSET_CONTROL);
-	udelay(50);
-	outb(0x00, port + VIA_REG_OFFSET_CONTROL);
-	outb(0xff, port + VIA_REG_OFFSET_STATUS);
-	outb(0x00, port + VIA_REG_OFFSET_TYPE);
-	outl(0, port + VIA_REG_OFFSET_CURR_PTR);
-}
-
-static int snd_via686a_trigger(via686a_t *chip, viadev_t *viadev, int cmd)
-{
-	unsigned char val = 0;
-	unsigned long port = chip->port + viadev->reg_offset;
-	
-	switch (cmd) {
-	case SNDRV_PCM_TRIGGER_START:
-		val = VIA_REG_CTRL_START;
-		viadev->running = 1;
-		break;
-	case SNDRV_PCM_TRIGGER_STOP:
-		val = VIA_REG_CTRL_TERMINATE | VIA_REG_CTRL_RESET;
-		viadev->running = 0;
-		break;
-	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
-		val = VIA_REG_CTRL_PAUSE;
-		viadev->running = 1;
-		break;
-	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
-		val = 0;
-		viadev->running = 0;
-		break;
-	default:
-		return -EINVAL;
-	}
-	outb(val, port + VIA_REG_OFFSET_CONTROL);
-	if (cmd == SNDRV_PCM_TRIGGER_STOP)
-		snd_via686a_channel_reset(chip, viadev);
-	return 0;
-}
-
-
-static int snd_via686a_setup_periods(via686a_t *chip, viadev_t *viadev,
-				      snd_pcm_substream_t *substream)
-{
-	snd_pcm_runtime_t *runtime = substream->runtime;
-	unsigned long port = chip->port + viadev->reg_offset;
-	int v, err;
-
-	viadev->size = snd_pcm_lib_buffer_bytes(substream);
-	viadev->fragsize = snd_pcm_lib_period_bytes(substream);
-	viadev->frags = runtime->periods;
-	viadev->lastptr = ~0;
-	viadev->lastcount = ~0;
-	viadev->curidx = 0;
-
-	/* the period size must be in power of 2 */
-	v = ld2(viadev->fragsize);
-	if (viadev->fragsize != (1 << v)) {
-		snd_printd(KERN_ERR "invalid fragment size %d\n", viadev->fragsize);
-		return -EINVAL;
-	}
-
-	snd_via686a_channel_reset(chip, viadev);
-
-	err = build_via_table(viadev, substream, chip->pci);
-	if (err < 0)
-		return err;
-
-	runtime->dma_bytes = viadev->size;
-	outl((u32)viadev->table_addr, port + VIA_REG_OFFSET_TABLE_PTR);
-	outb(VIA_REG_TYPE_AUTOSTART |
-	     (runtime->format == SNDRV_PCM_FORMAT_S16_LE ? VIA_REG_TYPE_16BIT : 0) |
-	     (runtime->channels > 1 ? VIA_REG_TYPE_STEREO : 0) |
-	     ((viadev->reg_offset & 0x10) == 0 ? VIA_REG_TYPE_INT_LSAMPLE : 0) |
-	     VIA_REG_TYPE_INT_EOL |
-	     VIA_REG_TYPE_INT_FLAG, port + VIA_REG_OFFSET_TYPE);
-	return 0;
-}
-
-/*
- *  Interrupt handler
- */
-
-static inline void snd_via686a_update(via686a_t *chip, viadev_t *viadev)
-{
-	outb(VIA_REG_STAT_FLAG | VIA_REG_STAT_EOL, VIAREG(chip, OFFSET_STATUS) + viadev->reg_offset);
-	if (viadev->substream && viadev->running) {
-		viadev->curidx++;
-		if (viadev->curidx >= viadev->page_per_frag) {
-			viadev->curidx = 0;
-			spin_unlock(&chip->reg_lock);
-			snd_pcm_period_elapsed(viadev->substream);
-			spin_lock(&chip->reg_lock);
-		}
-	}
-}
-
-static void snd_via686a_interrupt(int irq, void *dev_id, struct pt_regs *regs)
-{
-	via686a_t *chip = snd_magic_cast(via686a_t, dev_id, return);
-	unsigned int status;
-
-	spin_lock(&chip->reg_lock);
-	status = inl(VIAREG(chip, SGD_SHADOW));
-	if ((status & 0x00000077) == 0) {
-		spin_unlock(&chip->reg_lock);
-		if (chip->rmidi != NULL) {
-			snd_mpu401_uart_interrupt(irq, chip->rmidi->private_data, regs);
-		}
-		return;
-	}
-	if (inb(VIAREG(chip, PLAYBACK_STATUS)) & (VIA_REG_STAT_EOL|VIA_REG_STAT_FLAG))
-		snd_via686a_update(chip, &chip->playback);
-	if (inb(VIAREG(chip, CAPTURE_STATUS)) & (VIA_REG_STAT_EOL|VIA_REG_STAT_FLAG))
-		snd_via686a_update(chip, &chip->capture);
-	/*if (inb(VIAREG(chip, FM_STATUS)) & (VIA_REG_STAT_EOL|VIA_REG_STAT_FLAG))
-	  snd_via686a_update(chip, &chip->playback_fm);*/
-	spin_unlock(&chip->reg_lock);
-}
-
-/*
- *  PCM part
- */
-
-static int snd_via686a_playback_trigger(snd_pcm_substream_t * substream,
-					int cmd)
-{
-	via686a_t *chip = snd_pcm_substream_chip(substream);
-
-	return snd_via686a_trigger(chip, &chip->playback, cmd);
-}
-
-static int snd_via686a_capture_trigger(snd_pcm_substream_t * substream,
-				       int cmd)
-{
-	via686a_t *chip = snd_pcm_substream_chip(substream);
-
-	return snd_via686a_trigger(chip, &chip->capture, cmd);
-}
-
-static int snd_via686a_hw_params(snd_pcm_substream_t * substream,
-				 snd_pcm_hw_params_t * hw_params)
-{
-	return snd_pcm_sgbuf_alloc(substream, params_buffer_bytes(hw_params));
-}
-
-static int snd_via686a_hw_free(snd_pcm_substream_t * substream)
-{
-	return 0;
-}
-
-static int snd_via686a_playback_prepare(snd_pcm_substream_t * substream)
-{
-	via686a_t *chip = snd_pcm_substream_chip(substream);
-	snd_pcm_runtime_t *runtime = substream->runtime;
-
-	snd_ac97_set_rate(chip->ac97, AC97_PCM_FRONT_DAC_RATE, runtime->rate);
-	return snd_via686a_setup_periods(chip, &chip->playback, substream);
-}
-
-static int snd_via686a_capture_prepare(snd_pcm_substream_t * substream)
-{
-	via686a_t *chip = snd_pcm_substream_chip(substream);
-	snd_pcm_runtime_t *runtime = substream->runtime;
-
-	snd_ac97_set_rate(chip->ac97, AC97_PCM_LR_ADC_RATE, runtime->rate);
-	return snd_via686a_setup_periods(chip, &chip->capture, substream);
-}
-
-static inline unsigned int snd_via686a_cur_ptr(via686a_t *chip, viadev_t *viadev)
-{
-	unsigned int val, ptr, count;
-	
-	ptr = inl(VIAREG(chip, OFFSET_CURR_PTR) + viadev->reg_offset);
-	count = inl(VIAREG(chip, OFFSET_CURR_COUNT) + viadev->reg_offset);
-	if (ptr == viadev->lastptr && count > viadev->lastcount)
-		ptr += 8;
-	if (!(inb(VIAREG(chip, OFFSET_STATUS) + viadev->reg_offset) & VIA_REG_STAT_ACTIVE))
-		return 0;
-	snd_assert(viadev->tbl_entries, return 0);
-	/* get index */
-	if (ptr <= (unsigned int)viadev->table_addr)
-		val = 0;
-	else
-		val = ((ptr - (unsigned int)viadev->table_addr) / 8 - 1) % viadev->tbl_entries;
-	if (val < viadev->tbl_entries - 1) {
-		val *= viadev->tbl_size;
-		val += viadev->tbl_size - count;
-	} else {
-		val *= viadev->tbl_size;
-		val += (viadev->size % viadev->tbl_size) + 1 - count;
-	}
-	viadev->lastptr = ptr;
-	viadev->lastcount = count;
-	// printk("pointer: ptr = 0x%x (%i), count = 0x%x, val = 0x%x\n", ptr, count, val);
-	return val;
-}
-
-static snd_pcm_uframes_t snd_via686a_playback_pointer(snd_pcm_substream_t * substream)
-{
-	via686a_t *chip = snd_pcm_substream_chip(substream);
-	return bytes_to_frames(substream->runtime, snd_via686a_cur_ptr(chip, &chip->playback));
-}
-
-static snd_pcm_uframes_t snd_via686a_capture_pointer(snd_pcm_substream_t * substream)
-{
-	via686a_t *chip = snd_pcm_substream_chip(substream);
-	return bytes_to_frames(substream->runtime, snd_via686a_cur_ptr(chip, &chip->capture));
-}
-
-static snd_pcm_hardware_t snd_via686a_playback =
-{
-	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
-				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
-				 SNDRV_PCM_INFO_MMAP_VALID |
-				 SNDRV_PCM_INFO_PAUSE),
-	formats:		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
-	rates:			0,
-	rate_min:		8000,
-	rate_max:		48000,
-	channels_min:		1,
-	channels_max:		2,
-	buffer_bytes_max:	128 * 1024,
-	period_bytes_min:	32,
-	period_bytes_max:	128 * 1024,
-	periods_min:		2,
-	periods_max:		1024,
-	fifo_size:		0,
-};
-
-static snd_pcm_hardware_t snd_via686a_capture =
-{
-	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
-				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
-				 SNDRV_PCM_INFO_MMAP_VALID),
-	formats:		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
-	rates:			0,
-	rate_min:		8000,
-	rate_max:		48000,
-	channels_min:		1,
-	channels_max:		2,
-	buffer_bytes_max:	128 * 1024,
-	period_bytes_min:	32,
-	period_bytes_max:	128 * 1024,
-	periods_min:		2,
-	periods_max:		1024,
-	fifo_size:		0,
-};
-
-static int snd_via686a_playback_open(snd_pcm_substream_t * substream)
-{
-	via686a_t *chip = snd_pcm_substream_chip(substream);
-	snd_pcm_runtime_t *runtime = substream->runtime;
-	int err;
-
-	chip->playback.substream = substream;
-	runtime->hw = snd_via686a_playback;
-	runtime->hw.rates = chip->ac97->rates_front_dac;
-	if (!(runtime->hw.rates & SNDRV_PCM_RATE_8000))
-		runtime->hw.rate_min = 48000;
-	if ((err = snd_pcm_sgbuf_init(substream, chip->pci, 32)) < 0)
-		return err;
-	if ((err = snd_pcm_hw_constraint_pow2(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES)) < 0)
-		return err;
-#if 0
-	/* applying the following constraint together with the power-of-2 rule
-	 * above may result in too narrow space.
-	 * this one is not strictly necessary, so let's disable it.
-	 */
-	if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
-		return err;
-#endif
-	return 0;
-}
-
-static int snd_via686a_capture_open(snd_pcm_substream_t * substream)
-{
-	via686a_t *chip = snd_pcm_substream_chip(substream);
-	snd_pcm_runtime_t *runtime = substream->runtime;
-	int err;
-
-	chip->capture.substream = substream;
-	runtime->hw = snd_via686a_capture;
-	runtime->hw.rates = chip->ac97->rates_adc;
-	if (!(runtime->hw.rates & SNDRV_PCM_RATE_8000))
-		runtime->hw.rate_min = 48000;
-	if ((err = snd_pcm_sgbuf_init(substream, chip->pci, 32)) < 0)
-		return err;
-	if ((err = snd_pcm_hw_constraint_pow2(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES)) < 0)
-		return err;
-#if 0
-	if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
-		return err;
-#endif
-	return 0;
-}
-
-static int snd_via686a_playback_close(snd_pcm_substream_t * substream)
-{
-	via686a_t *chip = snd_pcm_substream_chip(substream);
-
-	clean_via_table(&chip->playback, substream, chip->pci);
-	snd_pcm_sgbuf_delete(substream);
-	chip->playback.substream = NULL;
-	/* disable DAC power */
-	snd_ac97_update_bits(chip->ac97, AC97_POWERDOWN, 0x0200, 0x0200);
-	return 0;
-}
-
-static int snd_via686a_capture_close(snd_pcm_substream_t * substream)
-{
-	via686a_t *chip = snd_pcm_substream_chip(substream);
-
-	clean_via_table(&chip->capture, substream, chip->pci);
-	snd_pcm_sgbuf_delete(substream);
-	chip->capture.substream = NULL;
-	/* disable ADC power */
-	snd_ac97_update_bits(chip->ac97, AC97_POWERDOWN, 0x0100, 0x0100);
-	return 0;
-}
-
-static snd_pcm_ops_t snd_via686a_playback_ops = {
-	open:		snd_via686a_playback_open,
-	close:		snd_via686a_playback_close,
-	ioctl:		snd_pcm_lib_ioctl,
-	hw_params:	snd_via686a_hw_params,
-	hw_free:	snd_via686a_hw_free,
-	prepare:	snd_via686a_playback_prepare,
-	trigger:	snd_via686a_playback_trigger,
-	pointer:	snd_via686a_playback_pointer,
-	copy:           snd_pcm_sgbuf_ops_copy_playback,
-	silence:        snd_pcm_sgbuf_ops_silence,
-	page:           snd_pcm_sgbuf_ops_page,
-};
-
-static snd_pcm_ops_t snd_via686a_capture_ops = {
-	open:		snd_via686a_capture_open,
-	close:		snd_via686a_capture_close,
-	ioctl:		snd_pcm_lib_ioctl,
-	hw_params:	snd_via686a_hw_params,
-	hw_free:	snd_via686a_hw_free,
-	prepare:	snd_via686a_capture_prepare,
-	trigger:	snd_via686a_capture_trigger,
-	pointer:	snd_via686a_capture_pointer,
-	copy:           snd_pcm_sgbuf_ops_copy_capture,
-	silence:        snd_pcm_sgbuf_ops_silence,
-	page:           snd_pcm_sgbuf_ops_page,
-};
-
-static void snd_via686a_pcm_free(snd_pcm_t *pcm)
-{
-	via686a_t *chip = snd_magic_cast(via686a_t, pcm->private_data, return);
-	chip->pcm = NULL;
-}
-
-static int __devinit snd_via686a_pcm(via686a_t *chip, int device, snd_pcm_t ** rpcm)
-{
-	snd_pcm_t *pcm;
-	int err;
-
-	if (rpcm)
-		*rpcm = NULL;
-	err = snd_pcm_new(chip->card, "VIA 82C686A", device, 1, 1, &pcm);
-	if (err < 0)
-		return err;
-
-	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_via686a_playback_ops);
-	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_via686a_capture_ops);
-
-	pcm->private_data = chip;
-	pcm->private_free = snd_via686a_pcm_free;
-	pcm->info_flags = 0;
-	strcpy(pcm->name, "VIA 82C686A");
-	chip->pcm = pcm;
-
-	if (rpcm)
-		*rpcm = NULL;
-	return 0;
-}
-
-
-/*
- *  Mixer part
- */
-
-static void snd_via686a_codec_init(ac97_t *ac97)
-{
-	// via686a_t *chip = snd_magic_cast(via686a_t, ac97->private_data, return);
-
-	/* disable DAC & ADC power */
-	snd_ac97_update_bits(ac97, AC97_POWERDOWN, 0x0300, 0x0300);
-	/* disable center DAC/surround DAC/LFE DAC/MIC ADC */
-	snd_ac97_update_bits(ac97, AC97_EXTENDED_STATUS, 0xe800, 0xe800);
-}
-
-static void snd_via686a_mixer_free_ac97(ac97_t *ac97)
-{
-	via686a_t *chip = snd_magic_cast(via686a_t, ac97->private_data, return);
-	chip->ac97 = NULL;
-}
-
-static int __devinit snd_via686a_mixer(via686a_t *chip)
-{
-	ac97_t ac97;
-	int err;
-
-	memset(&ac97, 0, sizeof(ac97));
-	ac97.write = snd_via686a_codec_write;
-	ac97.read = snd_via686a_codec_read;
-	ac97.init = snd_via686a_codec_init;
-	ac97.wait = snd_via686a_codec_wait;
-	ac97.private_data = chip;
-	ac97.private_free = snd_via686a_mixer_free_ac97;
-	ac97.clock = chip->ac97_clock;
-	if ((err = snd_ac97_mixer(chip->card, &ac97, &chip->ac97)) < 0)
-		return err;
-	return 0;
-}
-
-/*
- * joystick
- */
-
-static int snd_via686a_joystick_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_via686a_joystick_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
-{
-	via686a_t *chip = snd_kcontrol_chip(kcontrol);
-	u16 val;
-
-	pci_read_config_word(chip->pci, 0x42, &val);
-	ucontrol->value.integer.value[0] = (val & 0x08) ? 1 : 0;
-	return 0;
-}
-
-static int snd_via686a_joystick_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
-{
-	via686a_t *chip = snd_kcontrol_chip(kcontrol);
-	u16 val, oval;
-
-	pci_read_config_word(chip->pci, 0x42, &oval);
-	val = oval & ~0x08;
-	if (ucontrol->value.integer.value[0])
-		val |= 0x08;
-	if (val != oval) {
-		pci_write_config_word(chip->pci, 0x42, val);
-		return 1;
-	}
-	return 0;
-}
-
-static snd_kcontrol_new_t snd_via686a_joystick_control __devinitdata = {
-	name: "Joystick",
-	iface: SNDRV_CTL_ELEM_IFACE_CARD,
-	info: snd_via686a_joystick_info,
-	get: snd_via686a_joystick_get,
-	put: snd_via686a_joystick_put,
-};
-
-/*
- *
- */
-
-static int __devinit snd_via686a_chip_init(via686a_t *chip)
-{
-	ac97_t ac97;
-	unsigned int val;
-	int max_count;
-	unsigned char pval;
-
-	memset(&ac97, 0, sizeof(ac97));
-	ac97.private_data = chip;
-
-#if 0 /* broken on K7M? */
-	/* disable all legacy ports */
-	pci_write_config_byte(chip->pci, 0x42, 0);
-#endif
-
-	/* deassert ACLink reset, force SYNC */
-	pci_write_config_byte(chip->pci, 0x41, 0xe0);
-	udelay(100);
-	pci_write_config_byte(chip->pci, 0x41, 0x00);
-	udelay(100);
-	/* ACLink on, deassert ACLink reset, VSR, SGD data out */
-	/* note - FM data out has trouble with non VRA codecs !! */
-	pci_write_config_byte(chip->pci, 0x41, 0xcc);
-	udelay(100);
-
-	/* wait until codec ready */
-	max_count = ((3 * HZ) / 4) + 1;
-	do {
-		pci_read_config_byte(chip->pci, 0x40, &pval);
-		if (pval & 0x01) /* primary codec ready */
-			break;
-		set_current_state(TASK_UNINTERRUPTIBLE);
-		schedule_timeout(1);
-	} while (--max_count > 0);
-
-	if ((val = snd_via686a_codec_xread(chip)) & VIA_REG_AC97_BUSY)
-		snd_printk("AC'97 codec is not ready [0x%x]\n", val);
-
-	/* and then reset codec.. */
-	snd_via686a_codec_write(&ac97, AC97_RESET, 0x0000);
-
-	/* check the primary codec */
-	snd_via686a_codec_xwrite(chip, VIA_REG_AC97_READ |
-				 VIA_REG_AC97_PRIMARY_VALID |
-				 (VIA_REG_AC97_CODEC_ID_PRIMARY << VIA_REG_AC97_CODEC_ID_SHIFT));
-	max_count = ((3 * HZ) / 4) + 1;
-	do {
-		if ((val = snd_via686a_codec_xread(chip)) & VIA_REG_AC97_PRIMARY_VALID)
-			goto __ac97_ok1;
-		set_current_state(TASK_UNINTERRUPTIBLE);
-		schedule_timeout(1);
-	} while (--max_count > 0);
-	snd_printk("Primary AC'97 codec is not valid [0x%x]\n", val);
-
-      __ac97_ok1:
-#if 0 /* FIXME: we don't support the second codec yet so skip the detection now.. */
-	snd_via686a_codec_xwrite(chip, VIA_REG_AC97_READ |
-				 VIA_REG_AC97_SECONDARY_VALID |
-				 (VIA_REG_AC97_CODEC_ID_SECONDARY << VIA_REG_AC97_CODEC_ID_SHIFT));
-	max_count = ((3 * HZ) / 4) + 1;
-	snd_via686a_codec_xwrite(chip, VIA_REG_AC97_READ |
-				 VIA_REG_AC97_SECONDARY_VALID |
-				 (VIA_REG_AC97_CODEC_ID_SECONDARY << VIA_REG_AC97_CODEC_ID_SHIFT));
-	do {
-		if ((val = snd_via686a_codec_xread(chip)) & VIA_REG_AC97_SECONDARY_VALID) {
-			chip->ac97_secondary = 1;
-			goto __ac97_ok2;
-		}
-		set_current_state(TASK_INTERRUPTIBLE);
-		schedule_timeout(1);
-	} while (--max_count > 0);
-	/* This is ok, the most of motherboards have only one codec */
-
-      __ac97_ok2:
-#endif
-
-#if 0
-	{
-	unsigned char cmdb;
-
-	pci_read_config_byte(chip->pci, 0x40, &cmdb);
-	printk("PCI[0x40] = 0x%x\n", cmdb);
-	pci_read_config_byte(chip->pci, 0x42, &cmdb);
-	printk("PCI[0x42] = 0x%x\n", cmdb);
-	pci_read_config_byte(chip->pci, 0x43, &cmdb);
-	printk("PCI[0x43] = 0x%x\n", cmdb);
-	pci_read_config_byte(chip->pci, 0x44, &cmdb);
-	printk("PCI[0x44] = 0x%x\n", cmdb);
-	pci_read_config_byte(chip->pci, 0x48, &cmdb);
-	printk("PCI[0x48] = 0x%x\n", cmdb);
-	}
-#endif
-
-	/* route FM trap to IRQ, disable FM trap */
-	pci_write_config_byte(chip->pci, 0x48, 0);
-	
-	/* disable all GPI interrupts */
-	outl(0, chip->port + 0x8c);
-
-	/* disable interrupts */
-	snd_via686a_channel_reset(chip, &chip->playback);
-	snd_via686a_channel_reset(chip, &chip->capture);
-	/*snd_via686a_channel_reset(chip, &chip->playback_fm);*/
-	return 0;
-}
-
-static int snd_via686a_free(via686a_t *chip)
-{
-	if (chip->irq < 0)
-		goto __end_hw;
-	/* disable interrupts */
-	snd_via686a_channel_reset(chip, &chip->playback);
-	snd_via686a_channel_reset(chip, &chip->capture);
-	/*snd_via686a_channel_reset(chip, &chip->playback_fm);*/
-	/* --- */
-	synchronize_irq(chip->irq);
-      __end_hw:
-	if (chip->res_port) {
-		release_resource(chip->res_port);
-		kfree_nocheck(chip->res_port);
-	}
-	if (chip->irq >= 0)
-		free_irq(chip->irq, (void *)chip);
-	pci_write_config_byte(chip->pci, 0x42, chip->old_legacy);
-	pci_write_config_byte(chip->pci, 0x43, chip->old_legacy_cfg);
-	snd_magic_kfree(chip);
-	return 0;
-}
-
-static int snd_via686a_dev_free(snd_device_t *device)
-{
-	via686a_t *chip = snd_magic_cast(via686a_t, device->device_data, return -ENXIO);
-	return snd_via686a_free(chip);
-}
-
-static int __devinit snd_via686a_create(snd_card_t * card,
-				     struct pci_dev *pci,
-				     unsigned int ac97_clock,
-				     unsigned char old_legacy,
-				     unsigned char old_legacy_cfg,
-				     via686a_t ** r_via)
-{
-	via686a_t *chip;
-	int err;
-        static snd_device_ops_t ops = {
-		dev_free:	snd_via686a_dev_free,
-        };
-
-	if ((err = pci_enable_device(pci)) < 0)
-		return err;
-
-	if ((chip = snd_magic_kcalloc(via686a_t, 0, GFP_KERNEL)) == NULL)
-		return -ENOMEM;
-
-	chip->old_legacy = old_legacy;
-	chip->old_legacy_cfg = old_legacy_cfg;
-
-	spin_lock_init(&chip->reg_lock);
-	chip->card = card;
-	chip->pci = pci;
-	chip->irq = -1;
-	chip->port = pci_resource_start(pci, 0);
-	if ((chip->res_port = request_region(chip->port, 256, "VIA686A")) == NULL) {
-		snd_via686a_free(chip);
-		snd_printk("unable to grab ports 0x%lx-0x%lx\n", chip->port, chip->port + 256 - 1);
-		return -EBUSY;
-	}
-	if (request_irq(pci->irq, snd_via686a_interrupt, SA_INTERRUPT|SA_SHIRQ, "VIA686A", (void *)chip)) {
-		snd_via686a_free(chip);
-		snd_printk("unable to grab IRQ %d\n", chip->irq);
-		return -EBUSY;
-	}
-	chip->irq = pci->irq;
-	if (ac97_clock >= 8000 && ac97_clock <= 48000)
-		chip->ac97_clock = ac97_clock;
-	pci_read_config_byte(pci, PCI_REVISION_ID, &chip->revision);
-	synchronize_irq(chip->irq);
-
-	/* initialize offsets */
-	chip->playback.reg_offset = VIA_REG_PLAYBACK_STATUS;
-	chip->capture.reg_offset = VIA_REG_CAPTURE_STATUS;
-	/*chip->playback_fm.reg_offset = VIA_REG_FM_STATUS;*/
-
-	if ((err = snd_via686a_chip_init(chip)) < 0) {
-		snd_via686a_free(chip);
-		return err;
-	}
-
-	if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
-		snd_via686a_free(chip);
-		return err;
-	}
-
-	*r_via = chip;
-	return 0;
-}
-
-static int __devinit snd_via686a_probe(struct pci_dev *pci,
-				       const struct pci_device_id *id)
-{
-	static int dev;
-	snd_card_t *card;
-	via686a_t *chip;
-	int pcm_dev = 0;
-	unsigned char legacy;
-	unsigned char legacy_cfg;
-	int rev_h = 0, err;
-
-	if (dev >= SNDRV_CARDS)
-		return -ENODEV;
-	if (!snd_enable[dev]) {
-		dev++;
-		return -ENOENT;
-	}
-
-	card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, 0);
-	if (card == NULL)
-		return -ENOMEM;
-
-	pci_read_config_byte(pci, 0x42, &legacy);
-	pci_read_config_byte(pci, 0x43, &legacy_cfg);
-
-	if ((err = snd_via686a_create(card,
-				      pci,
-				      snd_ac97_clock[dev],
-				      legacy,
-				      legacy_cfg,
-				      &chip)) < 0) {
-		snd_card_free(card);
-		return err;
-	}
-
-
-	if (snd_via686a_mixer(chip) < 0) {
-		snd_card_free(card);
-		return err;
-	}
-	if (snd_via686a_pcm(chip, pcm_dev++, NULL) < 0) {
-		snd_card_free(card);
-		return err;
-	}
-#if 0
-	if (snd_via686a_pcm_fm(chip, pcm_dev++, NULL) < 0) {
-		snd_card_free(card);
-		return err;
-	}
-#endif
-
-	legacy |= 0x40;		/* disable MIDI */
-	legacy &= ~0x08;	/* disable joystick */
-	if (chip->revision >= 0x20) {
-		if (check_region(pci_resource_start(pci, 2), 4)) {
-			rev_h = 0;
-			legacy &= ~0x80;	/* disable PCI I/O 2 */
-		} else {
-			rev_h = 1;
-			legacy |= 0x80;		/* enable PCI I/O 2 */
-		}
-	}
-	pci_write_config_byte(pci, 0x42, legacy);
-	pci_write_config_byte(pci, 0x43, legacy_cfg);
-	if (rev_h && snd_mpu_port[dev] >= 0x200) {	/* force MIDI */
-		legacy |= 0x02;	/* enable MPU */
-		pci_write_config_dword(pci, 0x18, (snd_mpu_port[dev] & 0xfffc) | 0x01);
-	} else {
-		if (rev_h && (legacy & 0x02)) {
-			snd_mpu_port[dev] = pci_resource_start(pci, 2);
-			if (snd_mpu_port[dev] < 0x200)	/* bad value */
-				legacy &= ~0x02;	/* disable MIDI */
-		} else {
-			switch (snd_mpu_port[dev]) {	/* force MIDI */
-			case 0x300:
-			case 0x310:
-			case 0x320:
-			case 0x330:
-				legacy_cfg &= ~(3 << 2);
-				legacy_cfg |= (snd_mpu_port[dev] & 0x0030) >> 2;
-				legacy |= 0x02;
-				break;
-			default:			/* no, use BIOS settings */
-				if (legacy & 0x02)
-					snd_mpu_port[dev] = 0x300 + ((legacy_cfg & 0x000c) << 2);
-			}
-		}
-	}
-	pci_write_config_byte(pci, 0x42, legacy);
-	pci_write_config_byte(pci, 0x43, legacy_cfg);
-	if (legacy & 0x02) {
-		if (check_region(snd_mpu_port[dev], 2)) {
-			printk(KERN_WARNING "unable to get MPU-401 port at 0x%lx, skipping\n", snd_mpu_port[dev]);
-			legacy &= ~0x02;
-			pci_write_config_byte(pci, 0x42, legacy);
-			goto __skip_mpu;
-		}
-		if (snd_mpu401_uart_new(card, 0, MPU401_HW_VIA686A,
-					snd_mpu_port[dev], 0,
-					pci->irq, 0,
-					&chip->rmidi) < 0) {
-			printk(KERN_WARNING "unable to initialize MPU-401 at 0x%lx, skipping\n", snd_mpu_port[dev]);
-			legacy &= ~0x02;
-			pci_write_config_byte(pci, 0x42, legacy);
-			goto __skip_mpu;
-		}
-		legacy &= ~0x40;	/* enable MIDI interrupt */
-		pci_write_config_byte(pci, 0x42, legacy);
-	      __skip_mpu:
-		;
-	}
-	
-	/* card switches */
-	err = snd_ctl_add(card, snd_ctl_new1(&snd_via686a_joystick_control, chip));
-	if (err < 0) {
-		snd_card_free(card);
-		return err;
-	}
-
-	strcpy(card->driver, "VIA686A");
-	strcpy(card->shortname, "VIA 82C686A/B");
-	
-	sprintf(card->longname, "%s at 0x%lx, irq %d",
-		card->shortname, chip->port, chip->irq);
-
-	if ((err = snd_card_register(card)) < 0) {
-		snd_card_free(card);
-		return err;
-	}
-	pci_set_drvdata(pci, card);
-	dev++;
-	return 0;
-}
-
-static void __devexit snd_via686a_remove(struct pci_dev *pci)
-{
-	snd_card_free(pci_get_drvdata(pci));
-	pci_set_drvdata(pci, NULL);
-}
-
-static struct pci_driver driver = {
-	name: "VIA 82C686A/B",
-	id_table: snd_via686a_ids,
-	probe: snd_via686a_probe,
-	remove: __devexit_p(snd_via686a_remove),
-};
-
-static int __init alsa_card_via686a_init(void)
-{
-	int err;
-
-	if ((err = pci_module_init(&driver)) < 0) {
-#ifdef MODULE
-		printk(KERN_ERR "VIA 82C686A soundcard not found or device busy\n");
-#endif
-		return err;
-	}
-	return 0;
-}
-
-static void __exit alsa_card_via686a_exit(void)
-{
-	pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_via686a_init)
-module_exit(alsa_card_via686a_exit)
-
-#ifndef MODULE
-
-/* format is: snd-via686a=snd_enable,snd_index,snd_id,
-			  snd_mpu_port,snd_ac97_clock */
-
-static int __init alsa_card_via686a_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 &&
-	       get_option(&str,(int *)&snd_mpu_port[nr_dev]) == 2 &&
-	       get_option(&str,&snd_ac97_clock[nr_dev]) == 2);
-	nr_dev++;
-	return 1;
-}
-
-__setup("snd-via686a=", alsa_card_via686a_setup);
-
-#endif /* ifndef MODULE */
diff -Nru a/sound/pci/via8233.c b/sound/pci/via8233.c
--- a/sound/pci/via8233.c	Mon Oct  7 11:25:29 2002
+++ /dev/null	Wed Dec 31 16:00:00 1969
@@ -1,1041 +0,0 @@
-/*
- *   ALSA driver for VIA VT8233 (South Bridge)
- *
- *	Copyright (c) 2000 Tjeerd.Mulder@fujitsu-siemens.com
- *	This driver is based on VIA686 code by Jaroslav Kysela <perex@suse.cz>
- *
- *   This program is free software; you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License as published by
- *   the Free Software Foundation; either version 2 of the License, or
- *   (at your option) any later version.
- *
- *   This program is distributed in the hope that it will be useful,
- *   but WITHOUT ANY WARRANTY; without even the implied warranty of
- *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *   GNU General Public License for more details.
- *
- *   You should have received a copy of the GNU General Public License
- *   along with this program; if not, write to the Free Software
- *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
- *
- */      
-
-#include <sound/driver.h>
-#include <asm/io.h>
-#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>
-#include <sound/pcm_sgbuf.h>
-#include <sound/pcm_params.h>
-#include <sound/info.h>
-#include <sound/ac97_codec.h>
-#include <sound/mpu401.h>
-#define SNDRV_GET_ID
-#include <sound/initval.h>
-
-MODULE_AUTHOR("Tjeerd.Mulder@fujitsu-siemens.com");
-MODULE_DESCRIPTION("VIA VT8233");
-MODULE_LICENSE("GPL");
-MODULE_CLASSES("{sound}");
-MODULE_DEVICES("{{VIA,VT8233,pci}}");
-
-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 */
-static int snd_ac97_clock[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 48000};
-
-MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
-MODULE_PARM_DESC(snd_index, "Index value for VIA 8233 bridge.");
-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 VIA 8233 bridge.");
-MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC);
-MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
-MODULE_PARM_DESC(snd_enable, "Enable audio part of VIA 8233 bridge.");
-MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC);
-MODULE_PARM(snd_ac97_clock, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
-MODULE_PARM_DESC(snd_ac97_clock, "AC'97 codec clock (default 48000Hz).");
-MODULE_PARM_SYNTAX(snd_ac97_clock, SNDRV_ENABLED ",default:48000");
-
-
-/*
- *  Direct registers
- */
-
-#ifndef PCI_DEVICE_ID_VIA_8233_5
-#define PCI_DEVICE_ID_VIA_8233_5	0x3059
-#endif
-
-/* revision numbers */
-#define VIA_REV_PRE_8233	0x10	/* not in market */
-#define VIA_REV_8233C		0x20	/* 2 rec, 4 pb, 1 multi-pb */
-#define VIA_REV_8233		0x30	/* 2 rec, 4 pb, 1 multi-pb, spdif */
-#define VIA_REV_8233A		0x40	/* 1 rec, 1 multi-pb, spdf */
-#define VIA_REV_8235		0x50	/* 2 rec, 4 pb, 1 multi-pb, spdif */
-
-#define VIAREG(via, x) ((via)->port + VIA_REG_##x)
-
-/* offsets */
-#define VIA_REG_OFFSET_STATUS		0x00	/* byte - channel status */
-#define   VIA_REG_STAT_ACTIVE		0x80	/* RO */
-#define   VIA_REG_STAT_TRIGGER_QUEUED	0x08	/* RO */
-#define   VIA_REG_STAT_STOPPED		0x04	/* RWC */
-#define   VIA_REG_STAT_EOL		0x02	/* RWC */
-#define   VIA_REG_STAT_FLAG		0x01	/* RWC */
-#define VIA_REG_OFFSET_CONTROL		0x01	/* byte - channel control */
-#define   VIA_REG_CTRL_START		0x80	/* WO */
-#define   VIA_REG_CTRL_TERMINATE	0x40	/* WO */
-#define   VIA_REG_CTRL_AUTOSTART	0x20
-#define   VIA_REG_CTRL_PAUSE		0x08	/* RW */
-#define   VIA_REG_CTRL_INT_STOP		0x04		
-#define   VIA_REG_CTRL_INT_EOL		0x02
-#define   VIA_REG_CTRL_INT_FLAG		0x01
-#define   VIA_REG_CTRL_INT (VIA_REG_CTRL_INT_FLAG | VIA_REG_CTRL_INT_EOL | VIA_REG_CTRL_AUTOSTART)
-#define VIA_REG_OFFSET_TABLE_PTR	0x04	/* dword - channel table pointer */
-#define VIA_REG_OFFSET_CURR_PTR		0x04	/* dword - channel current pointer */
-#define VIA_REG_OFFSET_STOP_IDX		0x08	/* dword - stop index, channel type, sample rate */
-#define   VIA_REG_TYPE_16BIT		0x00200000	/* RW */
-#define   VIA_REG_TYPE_STEREO		0x00100000	/* RW */
-#define VIA_REG_OFFSET_CURR_COUNT	0x0c	/* dword - channel current count (24 bit) */
-#define VIA_REG_OFFSET_CURR_INDEX	0x0f	/* byte - channel current index */
-
-#define VIA_NUM_OF_DMA_CHANNELS	2
-
-/* playback block (VT8233/8233C) - channels 0-3 (0-0x3f) */
-#define VIA_REG_PLAYBACK_STATUS		0x00	/* byte - channel status */
-#define VIA_REG_PLAYBACK_CONTROL	0x01	/* byte - channel control */
-#define VIA_REG_PLAYBACK_VOLUME_L	0x02	/* byte */
-#define VIA_REG_PLAYBACK_VOLUME_R	0x03	/* byte */
-#define VIA_REG_PLAYBACK_TABLE_PTR	0x04	/* dword - channel table pointer */
-#define VIA_REG_PLAYBACK_CURR_PTR	0x04	/* dword - channel current pointer */
-#define VIA_REG_PLAYBACK_STOP_IDX	0x08    /* dword - stop index, channel type, sample rate */
-#define VIA_REG_PLAYBACK_CURR_COUNT	0x0c	/* dword - channel current count (24 bit) */
-#define VIA_REG_PLAYBACK_CURR_INDEX	0x0f	/* byte - channel current index */
-
-/* multi-channel playback */
-#define VIA_REG_MULTPLAY_STATUS		0x40	/* byte - channel status */
-#define VIA_REG_MULTPLAY_CONTROL	0x41	/* byte - channel control */
-#define VIA_REG_MULTPLAY_FORMAT		0x42	/* byte - format and channels */
-#define   VIA_REG_MULTPLAY_FMT_8BIT	0x00
-#define   VIA_REG_MULTPLAY_FMT_16BIT	0x80
-#define   VIA_REG_MULTPLAY_FMT_CH_MASK	0x70	/* # channels << 4 (valid = 1,2,4,6) */
-#define VIA_REG_MULTPLAY_SCRATCH	0x43	/* byte - nop */
-#define VIA_REG_MULTPLAY_TABLE_PTR	0x44	/* dword - channel table pointer */
-#define VIA_REG_MULTPLAY_CURR_PTR	0x44	/* dword - channel current pointer */
-#define VIA_REG_MULTPLAY_STOP_IDX	0x48    /* dword - stop index, slots */
-#define VIA_REG_MULTPLAY_CURR_COUNT	0x4c	/* dword - channel current count (24 bit) */
-#define VIA_REG_MULTIPLAY_CURR_INDEX	0x4f	/* byte - channel current index */
-
-/* capture block */
-#define VIA_REG_CAPTURE_STATUS		0x60	/* byte - channel status */
-#define VIA_REG_CAPTURE_CONTROL		0x61	/* byte - channel control */
-#define VIA_REG_CAPTURE_FIFO		0x62	/* byte - bit 6 = fifo  enable */
-#define   VIA_REG_CAPTURE_FIFO_ENABLE	0x40
-#define VIA_REG_CAPTURE_CHANNEL		0x63	/* byte - input select */
-#define   VIA_REG_CAPTURE_CHANNEL_MIC	0x4
-#define   VIA_REG_CAPTURE_CHANNEL_LINE	0
-#define   VIA_REG_CAPTURE_SELECT_CODEC	0x03	/* recording source codec (0 = primary) */
-#define VIA_REG_CAPTURE_TABLE_PTR	0x64	/* dword - channel table pointer */
-#define VIA_REG_CAPTURE_CURR_PTR	0x64	/* dword - channel current pointer */
-#define VIA_REG_CAPTURE_STOP_IDX	0x68	/* dword - stop index */
-#define VIA_REG_CAPTURE_CURR_COUNT	0x6c	/* dword - channel current count (24 bit) */
-#define VIA_REG_CAPTURE_CURR_INDEX	0x6f	/* byte - channel current index */
-/* AC'97 */
-#define VIA_REG_AC97			0x80	/* dword */
-#define   VIA_REG_AC97_CODEC_ID_MASK	(3<<30)
-#define   VIA_REG_AC97_CODEC_ID_SHIFT	30
-#define   VIA_REG_AC97_SECONDARY_VALID	(1<<27)
-#define   VIA_REG_AC97_PRIMARY_VALID	(1<<25)
-#define   VIA_REG_AC97_ANY_VALID	(VIA_REG_AC97_PRIMARY_VALID | VIA_REG_AC97_SECONDARY_VALID | (1<<28)| (1<<29))
-#define   VIA_REG_AC97_BUSY		(1<<24)
-#define   VIA_REG_AC97_READ		(1<<23)
-#define   VIA_REG_AC97_CMD_SHIFT	16
-#define   VIA_REG_AC97_CMD_MASK		0x7e
-#define   VIA_REG_AC97_DATA_SHIFT	0
-#define   VIA_REG_AC97_DATA_MASK	0xffff
-#define VIA_REG_SGD_SHADOW		0x84	/* dword */
-
-#define VIA_TBL_BIT_FLAG	0x40000000
-#define VIA_TBL_BIT_EOL		0x80000000
-
-/*
- *  
- */
-
-typedef struct {
-	unsigned long reg_offset;
-        snd_pcm_substream_t *substream;
-	int running;
-        unsigned int size;
-        unsigned int fragsize;
-	unsigned int frags;
-	unsigned int page_per_frag;
-	unsigned int curidx;
-	unsigned int tbl_entries;	/* number of descriptor table entries */
-	unsigned int tbl_size;		/* size of a table entry */
-	u32 *table; /* physical address + flag */
-	dma_addr_t table_addr;
-} viadev_t;
-
-static int build_via_table(viadev_t *dev, snd_pcm_substream_t *substream,
-			   struct pci_dev *pci)
-{
-	int i, size;
-	struct snd_sg_buf *sgbuf = snd_magic_cast(snd_pcm_sgbuf_t, substream->dma_private, return -EINVAL);
-
-	if (dev->table) {
-		snd_free_pci_pages(pci, PAGE_ALIGN(dev->tbl_entries * 8), dev->table, dev->table_addr);
-		dev->table = NULL;
-	}
-
-	/* allocate buffer descriptor lists */
-	if (dev->fragsize < PAGE_SIZE) {
-		dev->tbl_size = dev->fragsize;
-		dev->tbl_entries = dev->frags;
-		dev->page_per_frag = 1;
-	} else {
-		dev->tbl_size = PAGE_SIZE;
-		dev->tbl_entries = sgbuf->pages;
-		dev->page_per_frag = dev->fragsize >> PAGE_SHIFT;
-	}
-	/* the start of each lists must be aligned to 8 bytes,
-	 * but the kernel pages are much bigger, so we don't care
-	 */
-	dev->table = (u32*)snd_malloc_pci_pages(pci, PAGE_ALIGN(dev->tbl_entries * 8), &dev->table_addr);
-	if (! dev->table)
-		return -ENOMEM;
-
-	if (dev->tbl_size < PAGE_SIZE) {
-		for (i = 0; i < dev->tbl_entries; i++)
-			dev->table[i << 1] = cpu_to_le32((u32)sgbuf->table[0].addr + dev->fragsize * i);
-	} else {
-		for (i = 0; i < dev->tbl_entries; i++)
-			dev->table[i << 1] = cpu_to_le32((u32)sgbuf->table[i].addr);
-	}
-	size = dev->size;
-	for (i = 0; i < dev->tbl_entries - 1; i++) {
-		dev->table[(i << 1) + 1] = cpu_to_le32(VIA_TBL_BIT_FLAG | dev->tbl_size);
-		size -= dev->tbl_size;
-	}
-	dev->table[(dev->tbl_entries << 1) - 1] = cpu_to_le32(VIA_TBL_BIT_EOL | size);
-
-	return 0;
-}
-
-
-static void clean_via_table(viadev_t *dev, snd_pcm_substream_t *substream,
-			    struct pci_dev *pci)
-{
-	if (dev->table) {
-		snd_free_pci_pages(pci, PAGE_ALIGN(dev->tbl_entries * 8), dev->table, dev->table_addr);
-		dev->table = NULL;
-	}
-}
-
-
-/*
- */
-
-typedef struct _snd_via8233 via8233_t;
-#define chip_t via8233_t
-
-struct _snd_via8233 {
-	int irq;
-
-	unsigned long port;
-	struct resource *res_port;
-	unsigned char revision;
-
-	struct pci_dev *pci;
-	snd_card_t *card;
-
-	snd_pcm_t *pcm;
-	viadev_t playback;
-	viadev_t capture;
-
-	ac97_t *ac97;
-	unsigned int ac97_clock;
-
-	spinlock_t reg_lock;
-	snd_info_entry_t *proc_entry;
-};
-
-static struct pci_device_id snd_via8233_ids[] __devinitdata = {
-	{ 0x1106, 0x3059, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, },	/* VT8233 */
-	{ 0, }
-};
-
-MODULE_DEVICE_TABLE(pci, snd_via8233_ids);
-
-/*
- *  Basic I/O
- */
-
-static inline unsigned int snd_via8233_codec_xread(via8233_t *chip)
-{
-	/* this acces should be atomic */
-	return inl(VIAREG(chip, AC97));
-}
- 
-static inline void snd_via8233_codec_xwrite(via8233_t *chip, unsigned int val)
-{
-	/* this acces should be atomic */
-	outl(val, VIAREG(chip, AC97));
-}
- 
-static int snd_via8233_codec_ready(via8233_t *chip, int secondary)
-{
-	int time;
-
-	time = 1000;
-	do {
-		udelay(1);
-		if ((snd_via8233_codec_xread(chip) & VIA_REG_AC97_BUSY) == 0)
-			return 0;
-	} while (time--);
-	snd_printk("codec_ready: codec %i is not ready [0x%x]\n", secondary, snd_via8233_codec_xread(chip));
-	return -EIO;
-}
- 
-static void snd_via8233_codec_write(ac97_t *ac97,
-				    unsigned short reg,
-				    unsigned short val)
-{
-	via8233_t *chip = snd_magic_cast(via8233_t, ac97->private_data, return);
-	unsigned int xval;
-	
-	xval = (ac97->num) << VIA_REG_AC97_CODEC_ID_SHIFT;
-	xval |= ac97->num ? VIA_REG_AC97_SECONDARY_VALID : VIA_REG_AC97_PRIMARY_VALID;
-	xval |= (reg & 0x7f) << VIA_REG_AC97_CMD_SHIFT;
-	xval |= val << VIA_REG_AC97_DATA_SHIFT;
-	spin_lock(&chip->reg_lock);
-	snd_via8233_codec_ready(chip, ac97->num);
-	snd_via8233_codec_xwrite(chip, xval);
-	snd_via8233_codec_ready(chip, ac97->num);
-	spin_unlock(&chip->reg_lock);
-}
-
-static unsigned short snd_via8233_codec_read(ac97_t *ac97, unsigned short reg)
-{
-	via8233_t *chip = snd_magic_cast(via8233_t, ac97->private_data, return ~0);
-	unsigned int val;
-	int valid = ac97->num ? VIA_REG_AC97_SECONDARY_VALID : VIA_REG_AC97_PRIMARY_VALID;
-	int i;
-	
-	val = (ac97->num) << VIA_REG_AC97_CODEC_ID_SHIFT;
-	val |= valid;
-	val |= VIA_REG_AC97_READ;
-	val |= (reg & 0x7f) << VIA_REG_AC97_CMD_SHIFT;
-	spin_lock(&chip->reg_lock);
-	snd_via8233_codec_ready(chip, ac97->num);
-	snd_via8233_codec_xwrite(chip, val);
-	snd_via8233_codec_ready(chip, ac97->num);
-	for (i=1000; i--;) {
-		val = snd_via8233_codec_xread(chip);
-		if (val & valid) {
-			spin_unlock(&chip->reg_lock);
-			return (unsigned short)val;
-		}
-	}
-	spin_unlock(&chip->reg_lock);
-	snd_printk("codec_read: codec %i is not valid [0x%x]\n", ac97->num, val);
-	/* have to return some value, this is better then 0 */
-	return ~0;
-}
-
-#if 0
-static void snd_via8233_channel_print(via8233_t *chip, viadev_t *viadev)
-{
-	unsigned long port = chip->port + viadev->reg_offset;
-
-	printk("[0x%x] status = 0x%x, control = 0x%x, type = 0x%x, ptr = 0x%x, count = 0x%x\n",
-			port,
-			inb(port + VIA_REG_OFFSET_STATUS),
-			inb(port + VIA_REG_OFFSET_CONTROL),
-			inl(port + VIA_REG_OFFSET_STOP_IDX),
-			inl(port + VIA_REG_OFFSET_CURR_PTR),
-			inl(port + VIA_REG_OFFSET_CURR_COUNT));
-}
-#endif
-
-static void snd_via8233_channel_reset(via8233_t *chip, viadev_t *viadev)
-{
-	unsigned long port = chip->port + viadev->reg_offset;
-
-	outb(VIA_REG_CTRL_TERMINATE, port + VIA_REG_OFFSET_CONTROL);
-	udelay(50);
-	/* disable interrupts */
-	outb(0, port + VIA_REG_OFFSET_CONTROL);
-	/* clear interrupts */
-	outb(0x3, port + VIA_REG_OFFSET_STATUS);
-}
-
-static int snd_via8233_trigger(via8233_t *chip, viadev_t *viadev, int cmd)
-{
-	unsigned char val;
-	unsigned long port = chip->port + viadev->reg_offset;
-
-	switch (cmd) {
-	case SNDRV_PCM_TRIGGER_START:
-		val = VIA_REG_CTRL_INT | VIA_REG_CTRL_START;
-		viadev->running = 1;
-		break;
-	case SNDRV_PCM_TRIGGER_STOP:
-		val = VIA_REG_CTRL_TERMINATE;
-		viadev->running = 0;
-		break;
-	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
-		val = VIA_REG_CTRL_INT | VIA_REG_CTRL_PAUSE;
-		viadev->running = 1;
-		break;
-	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
-		val = VIA_REG_CTRL_INT;
-		viadev->running = 0;
-		break;
-	default:
-		return -EINVAL;
-	}
-	outb(val, port + VIA_REG_OFFSET_CONTROL);
-	if (cmd == SNDRV_PCM_TRIGGER_STOP)
-		snd_via8233_channel_reset(chip, viadev);
-	return 0;
-}
-
-static int snd_via8233_setup_periods(via8233_t *chip, viadev_t *viadev,
-				     snd_pcm_substream_t *substream)
-{
-	snd_pcm_runtime_t *runtime = substream->runtime;
-	unsigned long port = chip->port + viadev->reg_offset;
-	int v, err;
-
-	viadev->size = snd_pcm_lib_buffer_bytes(substream);
-	viadev->fragsize = snd_pcm_lib_period_bytes(substream);
-	viadev->frags = runtime->periods;
-	viadev->curidx = 0;
-
-	/* the period size must be in power of 2 */
-	v = ld2(viadev->fragsize);
-	if (viadev->fragsize != (1 << v)) {
-		snd_printd(KERN_ERR "invalid fragment size %d\n", viadev->fragsize);
-		return -EINVAL;
-	}
-
-	snd_via8233_channel_reset(chip, viadev);
-
-	err = build_via_table(viadev, substream, chip->pci);
-	if (err < 0)
-		return err;
-
-	runtime->dma_bytes = viadev->size;
-	outl((u32)viadev->table_addr, port + VIA_REG_OFFSET_TABLE_PTR);
-	if (viadev->reg_offset == VIA_REG_MULTPLAY_STATUS) {
-		unsigned int slots;
-		int fmt = (runtime->format == SNDRV_PCM_FORMAT_S16_LE) ? VIA_REG_MULTPLAY_FMT_16BIT : VIA_REG_MULTPLAY_FMT_8BIT;
-		fmt |= runtime->channels << 4;
-		outb(fmt, chip->port + VIA_REG_MULTPLAY_FORMAT);
-		/* set sample number to slot 3, 4, 7, 8, 6, 9 */
-		switch (runtime->channels) {
-		case 1: slots = (1<<0) | (1<<4); break;
-		case 2: slots = (1<<0) | (2<<4); break;
-		case 4: slots = (1<<0) | (2<<4) | (3<<8) | (4<<12); break;
-		case 6: slots = (1<<0) | (2<<4) | (5<<8) | (6<<12) | (3<<16) | (4<<20); break;
-		default: slots = 0; break;
-		}
-		/* STOP index is never reached */
-		outl(0xff000000 | slots, chip->port + VIA_REG_MULTPLAY_STOP_IDX);
-	} else {
-		outl((runtime->format == SNDRV_PCM_FORMAT_S16_LE ? VIA_REG_TYPE_16BIT : 0) |
-		     (runtime->channels > 1 ? VIA_REG_TYPE_STEREO : 0) |
-		     0xff000000,    /* STOP index is never reached */
-		     port + VIA_REG_OFFSET_STOP_IDX);
-	}
-	return 0;
-}
-
-/*
- *  Interrupt handler
- */
-
-static inline void snd_via8233_update(via8233_t *chip, viadev_t *viadev)
-{
-	outb(VIA_REG_STAT_FLAG | VIA_REG_STAT_EOL, VIAREG(chip, OFFSET_STATUS) + viadev->reg_offset);
-	if (viadev->substream && viadev->running) {
-		viadev->curidx++;
-		if (viadev->curidx >= viadev->page_per_frag) {
-			viadev->curidx = 0;
-			spin_unlock(&chip->reg_lock);
-			snd_pcm_period_elapsed(viadev->substream);
-			spin_lock(&chip->reg_lock);
-		}
-	}
-}
-
-static void snd_via8233_interrupt(int irq, void *dev_id, struct pt_regs *regs)
-{
-	via8233_t *chip = snd_magic_cast(via8233_t, dev_id, return);
-
-	spin_lock(&chip->reg_lock);
-	if (inb(chip->port + chip->playback.reg_offset) & (VIA_REG_STAT_EOL|VIA_REG_STAT_FLAG))
-		snd_via8233_update(chip, &chip->playback);
-	if (inb(chip->port + chip->capture.reg_offset) & (VIA_REG_STAT_EOL|VIA_REG_STAT_FLAG))
-		snd_via8233_update(chip, &chip->capture);
-	spin_unlock(&chip->reg_lock);
-}
-
-/*
- *  PCM part
- */
-
-static int snd_via8233_playback_trigger(snd_pcm_substream_t * substream,
-					int cmd)
-{
-	via8233_t *chip = snd_pcm_substream_chip(substream);
-
-	return snd_via8233_trigger(chip, &chip->playback, cmd);
-}
-
-static int snd_via8233_capture_trigger(snd_pcm_substream_t * substream,
-				       int cmd)
-{
-	via8233_t *chip = snd_pcm_substream_chip(substream);
-	return snd_via8233_trigger(chip, &chip->capture, cmd);
-}
-
-static int snd_via8233_hw_params(snd_pcm_substream_t * substream,
-					  snd_pcm_hw_params_t * hw_params)
-{
-	return snd_pcm_sgbuf_alloc(substream, params_buffer_bytes(hw_params));
-}
-
-static int snd_via8233_hw_free(snd_pcm_substream_t * substream)
-{
-	return 0;
-}
-
-static int snd_via8233_playback_prepare(snd_pcm_substream_t * substream)
-{
-	via8233_t *chip = snd_pcm_substream_chip(substream);
-	snd_pcm_runtime_t *runtime = substream->runtime;
-
-	snd_ac97_set_rate(chip->ac97, AC97_PCM_FRONT_DAC_RATE, runtime->rate);
-	if (chip->playback.reg_offset != VIA_REG_MULTPLAY_STATUS) {
-		unsigned int tmp;
-		/* I don't understand this stuff but its from the documentation and this way it works */
-		outb(0 , VIAREG(chip, PLAYBACK_VOLUME_L));
-		outb(0 , VIAREG(chip, PLAYBACK_VOLUME_R));
-		tmp = inl(VIAREG(chip, PLAYBACK_STOP_IDX)) & ~0xfffff;
-		outl(tmp | (0xffff * runtime->rate)/(48000/16), VIAREG(chip, PLAYBACK_STOP_IDX));
-	}
-	return snd_via8233_setup_periods(chip, &chip->playback, substream);
-}
-
-static int snd_via8233_capture_prepare(snd_pcm_substream_t * substream)
-{
-	via8233_t *chip = snd_pcm_substream_chip(substream);
-	snd_pcm_runtime_t *runtime = substream->runtime;
-
-	snd_ac97_set_rate(chip->ac97, AC97_PCM_LR_ADC_RATE, runtime->rate);
-	outb(VIA_REG_CAPTURE_CHANNEL_LINE, VIAREG(chip, CAPTURE_CHANNEL));
-	outb(VIA_REG_CAPTURE_FIFO_ENABLE, VIAREG(chip, CAPTURE_FIFO));
-	return snd_via8233_setup_periods(chip, &chip->capture, substream);
-}
-
-static inline unsigned int snd_via8233_cur_ptr(via8233_t *chip, viadev_t *viadev)
-{
-	unsigned int val, count;
-
-	count = inl(VIAREG(chip, OFFSET_CURR_COUNT) + viadev->reg_offset) & 0xffffff;
-	/* The via686a does not have this current index register,
-	 * this register makes life easier for us here. */
-	val = inb(VIAREG(chip, OFFSET_CURR_INDEX) + viadev->reg_offset) % viadev->tbl_entries;
-	if (val < viadev->tbl_entries - 1) {
-		val *= viadev->tbl_size;
-		val += viadev->tbl_size - count;
-	} else {
-		val *= viadev->tbl_size;
-		val += (viadev->size % viadev->tbl_size) + 1 - count;
-	}
-	// printk("pointer: ptr = 0x%x, count = 0x%x, val = 0x%x\n", ptr, count, val);
-	return val;
-}
-
-static snd_pcm_uframes_t snd_via8233_playback_pointer(snd_pcm_substream_t * substream)
-{
-	via8233_t *chip = snd_pcm_substream_chip(substream);
-	return bytes_to_frames(substream->runtime, snd_via8233_cur_ptr(chip, &chip->playback));
-}
-
-static snd_pcm_uframes_t snd_via8233_capture_pointer(snd_pcm_substream_t * substream)
-{
-	via8233_t *chip = snd_pcm_substream_chip(substream);
-	return bytes_to_frames(substream->runtime, snd_via8233_cur_ptr(chip, &chip->capture));
-}
-
-static snd_pcm_hardware_t snd_via8233_playback =
-{
-	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
-				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
-				 SNDRV_PCM_INFO_MMAP_VALID |
-				 SNDRV_PCM_INFO_PAUSE),
-	formats:		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
-	rates:			0,
-	rate_min:		8000,
-	rate_max:		48000,
-	channels_min:		1,
-	channels_max:		6,
-	buffer_bytes_max:	128 * 1024,
-	period_bytes_min:	32,
-	period_bytes_max:	128 * 1024,
-	periods_min:		2,
-	periods_max:		1024,
-	fifo_size:		0,
-};
-
-static snd_pcm_hardware_t snd_via8233_capture =
-{
-	info:			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
-				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
-				 SNDRV_PCM_INFO_MMAP_VALID),
-	formats:		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
-	rates:			0,
-	rate_min:		8000,
-	rate_max:		48000,
-	channels_min:		1,
-	channels_max:		2,
-	buffer_bytes_max:	128 * 1024,
-	period_bytes_min:	32,
-	period_bytes_max:	128 * 1024,
-	periods_min:		2,
-	periods_max:		1024,
-	fifo_size:		0,
-};
-
-static unsigned int channels[] = {
-	1, 2, 4, 6
-};
-
-#define CHANNELS sizeof(channels) / sizeof(channels[0])
-
-static snd_pcm_hw_constraint_list_t hw_constraints_channels = {
-	count: CHANNELS,
-	list: channels,
-	mask: 0,
-};
-
-static int snd_via8233_playback_open(snd_pcm_substream_t * substream)
-{
-	via8233_t *chip = snd_pcm_substream_chip(substream);
-	snd_pcm_runtime_t *runtime = substream->runtime;
-	int err;
-
-	chip->playback.substream = substream;
-	runtime->hw = snd_via8233_playback;
-	runtime->hw.rates = chip->ac97->rates_front_dac;
-	if (!(runtime->hw.rates & SNDRV_PCM_RATE_8000))
-		runtime->hw.rate_min = 48000;
-	if ((err = snd_pcm_sgbuf_init(substream, chip->pci, 32)) < 0)
-		return err;
-	if ((err = snd_pcm_hw_constraint_pow2(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES)) < 0)
-		return err;
-#if 0
-	if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
-		return err;
-#endif
-	snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, &hw_constraints_channels);
-	return 0;
-}
-
-static int snd_via8233_capture_open(snd_pcm_substream_t * substream)
-{
-	via8233_t *chip = snd_pcm_substream_chip(substream);
-	snd_pcm_runtime_t *runtime = substream->runtime;
-	int err;
-
-	chip->capture.substream = substream;
-	runtime->hw = snd_via8233_capture;
-	runtime->hw.rates = chip->ac97->rates_adc;
-	if (!(runtime->hw.rates & SNDRV_PCM_RATE_8000))
-		runtime->hw.rate_min = 48000;
-	if ((err = snd_pcm_sgbuf_init(substream, chip->pci, 32)) < 0)
-		return err;
-	if ((err = snd_pcm_hw_constraint_pow2(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES)) < 0)
-		return err;
-#if 0
-	if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
-		return err;
-#endif
-	return 0;
-}
-
-static int snd_via8233_playback_close(snd_pcm_substream_t * substream)
-{
-	via8233_t *chip = snd_pcm_substream_chip(substream);
-
-	snd_via8233_channel_reset(chip, &chip->playback);
-	clean_via_table(&chip->playback, substream, chip->pci);
-	snd_pcm_sgbuf_delete(substream);
-	chip->playback.substream = NULL;
-	/* disable DAC power */
-	snd_ac97_update_bits(chip->ac97, AC97_POWERDOWN, 0x0200, 0x0200);
-	return 0;
-}
-
-static int snd_via8233_capture_close(snd_pcm_substream_t * substream)
-{
-	via8233_t *chip = snd_pcm_substream_chip(substream);
-
-	snd_via8233_channel_reset(chip, &chip->capture);
-	clean_via_table(&chip->capture, substream, chip->pci);
-	snd_pcm_sgbuf_delete(substream);
-	chip->capture.substream = NULL;
-	/* disable ADC power */
-	snd_ac97_update_bits(chip->ac97, AC97_POWERDOWN, 0x0100, 0x0100);
-	return 0;
-}
-
-static snd_pcm_ops_t snd_via8233_playback_ops = {
-	open:		snd_via8233_playback_open,
-	close:		snd_via8233_playback_close,
-	ioctl:		snd_pcm_lib_ioctl,
-	hw_params:	snd_via8233_hw_params,
-	hw_free:	snd_via8233_hw_free,
-	prepare:	snd_via8233_playback_prepare,
-	trigger:	snd_via8233_playback_trigger,
-	pointer:	snd_via8233_playback_pointer,
-	copy:           snd_pcm_sgbuf_ops_copy_playback,
-	silence:        snd_pcm_sgbuf_ops_silence,
-	page:           snd_pcm_sgbuf_ops_page,
-};
-
-static snd_pcm_ops_t snd_via8233_capture_ops = {
-	open:		snd_via8233_capture_open,
-	close:		snd_via8233_capture_close,
-	ioctl:		snd_pcm_lib_ioctl,
-	hw_params:	snd_via8233_hw_params,
-	hw_free:	snd_via8233_hw_free,
-	prepare:	snd_via8233_capture_prepare,
-	trigger:	snd_via8233_capture_trigger,
-	pointer:	snd_via8233_capture_pointer,
-	copy:           snd_pcm_sgbuf_ops_copy_capture,
-	silence:        snd_pcm_sgbuf_ops_silence,
-	page:           snd_pcm_sgbuf_ops_page,
-};
-
-static void snd_via8233_pcm_free(snd_pcm_t *pcm)
-{
-	via8233_t *chip = snd_magic_cast(via8233_t, pcm->private_data, return);
-	chip->pcm = NULL;
-}
-
-static int __devinit snd_via8233_pcm(via8233_t *chip, int device, snd_pcm_t ** rpcm)
-{
-	snd_pcm_t *pcm;
-	int err;
-
-	if (rpcm)
-		*rpcm = NULL;
-	err = snd_pcm_new(chip->card, "VIA 8233", device, 1, 1, &pcm);
-	if (err < 0)
-		return err;
-
-	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_via8233_playback_ops);
-	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_via8233_capture_ops);
-
-	pcm->private_data = chip;
-	pcm->private_free = snd_via8233_pcm_free;
-	pcm->info_flags = 0;
-	strcpy(pcm->name, "VIA 8233");
-	chip->pcm = pcm;
-
-	if (rpcm)
-		*rpcm = NULL;
-	return 0;
-}
-
-/*
- *  Mixer part
- */
-
-static void snd_via8233_codec_init(ac97_t *ac97)
-{
-	// via8233_t *chip = snd_magic_cast(via8233_t, ac97->private_data, return);
-
-	/* disable DAC & ADC power */
-	snd_ac97_update_bits(ac97, AC97_POWERDOWN, 0x0300, 0x0300);
-	/* disable center DAC/surround DAC/LFE DAC/MIC ADC */
-	snd_ac97_update_bits(ac97, AC97_EXTENDED_STATUS, 0xe800, 0xe800);
-}
-
-static void snd_via8233_mixer_free_ac97(ac97_t *ac97)
-{
-	via8233_t *chip = snd_magic_cast(via8233_t, ac97->private_data, return);
-	chip->ac97 = NULL;
-}
-
-static int __devinit snd_via8233_mixer(via8233_t *chip)
-{
-	ac97_t ac97;
-	int err;
-
-	memset(&ac97, 0, sizeof(ac97));
-	ac97.write = snd_via8233_codec_write;
-	ac97.read = snd_via8233_codec_read;
-	ac97.init = snd_via8233_codec_init;
-	ac97.private_data = chip;
-	ac97.private_free = snd_via8233_mixer_free_ac97;
-	ac97.clock = chip->ac97_clock;
-	if ((err = snd_ac97_mixer(chip->card, &ac97, &chip->ac97)) < 0)
-		return err;
-	return 0;
-}
-
-/*
- *
- */
-
-static int __devinit snd_via8233_chip_init(via8233_t *chip)
-{
-	ac97_t ac97;
-	unsigned char stat;
-	int i;
-	
-	memset(&ac97, 0, sizeof(ac97));
-	ac97.private_data = chip;
-
-	/* deassert ACLink reset */
-	pci_write_config_byte(chip->pci, 0x41, 0x40);
-	udelay(100);
-	/* deassert ACLink reset, force SYNC (warm AC'97 reset) */
-	pci_write_config_byte(chip->pci, 0x41, 0x60);
-	udelay(2);
-	/* ACLink on, deassert ACLink reset, VSR, SGD data out */
-	pci_write_config_byte(chip->pci, 0x41, 0xcc);
-
-	/* Wait for codec ready to be accessed. */
-	for (i=HZ; i--; ) {
-		pci_read_config_byte(chip->pci, 0x40, &stat);
-		if (stat & 1)
-			break;
-		if (!i) {
-			snd_printk("chip_init: failed to access primary codec.\n");
-			return ~0;
-		}
-		set_current_state(TASK_UNINTERRUPTIBLE);
-		schedule_timeout(1);
-	}
-	snd_via8233_codec_ready(chip, 0);
-	snd_via8233_codec_write(&ac97, AC97_RESET, 0x0000);
-	snd_via8233_codec_read(&ac97, 0);
-
-	/* disable interrupts */
-	snd_via8233_channel_reset(chip, &chip->playback);
-	snd_via8233_channel_reset(chip, &chip->capture);
-	return 0;
-}
-
-static int snd_via8233_free(via8233_t *chip)
-{
-	if (chip->irq < 0)
-		goto __end_hw;
-	/* disable interrupts */
-	snd_via8233_channel_reset(chip, &chip->playback);
-	snd_via8233_channel_reset(chip, &chip->capture);
-	/* --- */
-	synchronize_irq(chip->irq);
-      __end_hw:
-	if (chip->res_port) {
-		release_resource(chip->res_port);
-		kfree_nocheck(chip->res_port);
-	}
-	if (chip->irq >= 0)
-		free_irq(chip->irq, (void *)chip);
-	snd_magic_kfree(chip);
-	return 0;
-}
-
-static int snd_via8233_dev_free(snd_device_t *device)
-{
-	via8233_t *chip = snd_magic_cast(via8233_t, device->device_data, return -ENXIO);
-	return snd_via8233_free(chip);
-}
-
-static int __devinit snd_via8233_create(snd_card_t * card,
-				     struct pci_dev *pci,
-				     unsigned int ac97_clock,
-				     via8233_t ** r_via)
-{
-	via8233_t *chip;
-	int err;
-        static snd_device_ops_t ops = {
-		dev_free:	snd_via8233_dev_free,
-        };
-
-	if ((err = pci_enable_device(pci)) < 0)
-		return err;
-
-	if ((chip = snd_magic_kcalloc(via8233_t, 0, GFP_KERNEL)) == NULL)
-		return -ENOMEM;
-
-	spin_lock_init(&chip->reg_lock);
-	chip->card = card;
-	chip->pci = pci;
-	chip->irq = -1;
-	chip->port = pci_resource_start(pci, 0);
-	if ((chip->res_port = request_region(chip->port, 256, "VIA8233")) == NULL) {
-		snd_via8233_free(chip);
-		snd_printk("unable to grab ports 0x%lx-0x%lx\n", chip->port, chip->port + 256 - 1);
-		return -EBUSY;
-	}
-	if (request_irq(pci->irq, snd_via8233_interrupt, SA_INTERRUPT|SA_SHIRQ, "VIA8233", (void *)chip)) {
-		snd_via8233_free(chip);
-		snd_printk("unable to grab IRQ %d\n", chip->irq);
-		return -EBUSY;
-	}
-	chip->irq = pci->irq;
-	if (ac97_clock >= 8000 && ac97_clock <= 48000)
-		chip->ac97_clock = ac97_clock;
-	pci_read_config_byte(pci, PCI_REVISION_ID, &chip->revision);
-	synchronize_irq(chip->irq);
-
-	/* initialize offsets */
-#if 0
-	chip->playback.reg_offset = VIA_REG_PLAYBACK_STATUS; /* this doesn't work on VIA8233A */
-#endif
-	/* we use multi-channel playback mode, since this mode is supported
-	 * by all VIA8233 models (and obviously suitable for our purpose).
-	 */
-	chip->playback.reg_offset = VIA_REG_MULTPLAY_STATUS;
-
-	chip->capture.reg_offset = VIA_REG_CAPTURE_STATUS;
-
-	if ((err = snd_via8233_chip_init(chip)) < 0) {
-		snd_via8233_free(chip);
-		return err;
-	}
-
-	if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
-		snd_via8233_free(chip);
-		return err;
-	}
-
-	/* The 8233 ac97 controller does not implement the master bit
-	 * in the pci command register. IMHO this is a violation of the PCI spec.
-	 * We call pci_set_master here because it does not hurt. */
-	pci_set_master(pci);
-
-	*r_via = chip;
-	return 0;
-}
-
-static int __devinit snd_via8233_probe(struct pci_dev *pci,
-				       const struct pci_device_id *id)
-{
-	static int dev;
-	snd_card_t *card;
-	via8233_t *chip;
-	int pcm_dev = 0;
-	int err;
-
-	if (dev >= SNDRV_CARDS)
-		return -ENODEV;
-	if (!snd_enable[dev]) {
-		dev++;
-		return -ENOENT;
-	}
-
-	card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, 0);
-	if (card == NULL)
-		return -ENOMEM;
-
-	if ((err = snd_via8233_create(card,
-				      pci,
-				      snd_ac97_clock[dev],
-				      &chip)) < 0) {
-		snd_card_free(card);
-		return err;
-	}
-
-
-	if (snd_via8233_mixer(chip) < 0) {
-		snd_card_free(card);
-		return err;
-	}
-	if (snd_via8233_pcm(chip, pcm_dev++, NULL) < 0) {
-		snd_card_free(card);
-		return err;
-	}
-
-	strcpy(card->driver, "VIA8233");
-	strcpy(card->shortname, "VIA 8233");
-	
-	sprintf(card->longname, "%s at 0x%lx, irq %d",
-		card->shortname, chip->port, chip->irq);
-
-	if ((err = snd_card_register(card)) < 0) {
-		snd_card_free(card);
-		return err;
-	}
-	pci_set_drvdata(pci, card);
-	dev++;
-	return 0;
-}
-
-static void __devexit snd_via8233_remove(struct pci_dev *pci)
-{
-	snd_card_free(pci_get_drvdata(pci));
-	pci_set_drvdata(pci, NULL);
-}
-
-static struct pci_driver driver = {
-	name: "VIA 8233",
-	id_table: snd_via8233_ids,
-	probe: snd_via8233_probe,
-	remove: __devexit_p(snd_via8233_remove),
-};
-
-static int __init alsa_card_via8233_init(void)
-{
-	int err;
-
-	if ((err = pci_module_init(&driver)) < 0) {
-#ifdef MODULE
-		printk(KERN_ERR "VIA 8233 soundcard not found or device busy\n");
-#endif
-		return err;
-	}
-	return 0;
-}
-
-static void __exit alsa_card_via8233_exit(void)
-{
-	pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_via8233_init)
-module_exit(alsa_card_via8233_exit)
-
-#ifndef MODULE
-
-/* format is: snd-via8233=snd_enable,snd_index,snd_id,snd_ac97_clock */
-
-static int __init alsa_card_via8233_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 &&
-	       get_option(&str,&snd_ac97_clock[nr_dev]) == 2);
-	nr_dev++;
-	return 1;
-}
-
-__setup("snd-via8233=", alsa_card_via8233_setup);
-
-#endif /* ifndef MODULE */
diff -Nru a/sound/pci/via82xx.c b/sound/pci/via82xx.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/sound/pci/via82xx.c	Mon Oct  7 11:25:29 2002
@@ -0,0 +1,1379 @@
+/*
+ *   ALSA driver for VIA VT82xx (South Bridge)
+ *
+ *   VT82C686A/B/C, VT8233A/C, VT8235
+ *
+ *	Copyright (c) 2000 Jaroslav Kysela <perex@suse.cz>
+ *	                   Tjeerd.Mulder <Tjeerd.Mulder@fujitsu-siemens.com>
+ *                    2002 Takashi Iwai <tiwai@suse.de>
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <asm/io.h>
+#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>
+#include <sound/pcm_sgbuf.h>
+#include <sound/pcm_params.h>
+#include <sound/info.h>
+#include <sound/ac97_codec.h>
+#include <sound/mpu401.h>
+#define SNDRV_GET_ID
+#include <sound/initval.h>
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
+MODULE_DESCRIPTION("VIA VT82xx audio");
+MODULE_LICENSE("GPL");
+MODULE_CLASSES("{sound}");
+MODULE_DEVICES("{{VIA,VT82C686A/B/C,pci},{VIA,VT8233A/B/C}}");
+
+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 */
+static long snd_mpu_port[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = -1};
+static int snd_ac97_clock[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 48000};
+
+MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_index, "Index value for VIA 82xx bridge.");
+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 VIA 82xx bridge.");
+MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC);
+MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_enable, "Enable audio part of VIA 82xx bridge.");
+MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC);
+MODULE_PARM(snd_mpu_port, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_mpu_port, "MPU-401 port.");
+MODULE_PARM_SYNTAX(snd_mpu_port, SNDRV_PORT_DESC);
+MODULE_PARM(snd_ac97_clock, "1-" __MODULE_STRING(SNDRV_CARDS) "l");
+MODULE_PARM_DESC(snd_ac97_clock, "AC'97 codec clock (default 48000Hz).");
+MODULE_PARM_SYNTAX(snd_ac97_clock, SNDRV_ENABLED ",default:48000");
+
+
+/* pci ids */
+#ifndef PCI_DEVICE_ID_VIA_82C686_5
+#define PCI_DEVICE_ID_VIA_82C686_5	0x3058
+#endif
+#ifndef PCI_DEVICE_ID_VIA_8233_5
+#define PCI_DEVICE_ID_VIA_8233_5	0x3059
+#endif
+
+/* revision numbers for via8233 */
+#define VIA_REV_PRE_8233	0x10	/* not in market */
+#define VIA_REV_8233C		0x20	/* 2 rec, 4 pb, 1 multi-pb */
+#define VIA_REV_8233		0x30	/* 2 rec, 4 pb, 1 multi-pb, spdif */
+#define VIA_REV_8233A		0x40	/* 1 rec, 1 multi-pb, spdf */
+#define VIA_REV_8235		0x50	/* 2 rec, 4 pb, 1 multi-pb, spdif */
+
+/*
+ *  Direct registers
+ */
+
+#define VIAREG(via, x) ((via)->port + VIA_REG_##x)
+
+/* common offsets */
+#define VIA_REG_OFFSET_STATUS		0x00	/* byte - channel status */
+#define   VIA_REG_STAT_ACTIVE		0x80	/* RO */
+#define   VIA_REG_STAT_PAUSED		0x40	/* RO */
+#define   VIA_REG_STAT_TRIGGER_QUEUED	0x08	/* RO */
+#define   VIA_REG_STAT_STOPPED		0x04	/* RWC */
+#define   VIA_REG_STAT_EOL		0x02	/* RWC */
+#define   VIA_REG_STAT_FLAG		0x01	/* RWC */
+#define VIA_REG_OFFSET_CONTROL		0x01	/* byte - channel control */
+#define   VIA_REG_CTRL_START		0x80	/* WO */
+#define   VIA_REG_CTRL_TERMINATE	0x40	/* WO */
+#define   VIA_REG_CTRL_AUTOSTART	0x20
+#define   VIA_REG_CTRL_PAUSE		0x08	/* RW */
+#define   VIA_REG_CTRL_INT_STOP		0x04		
+#define   VIA_REG_CTRL_INT_EOL		0x02
+#define   VIA_REG_CTRL_INT_FLAG		0x01
+#define   VIA_REG_CTRL_RESET		0x01	/* RW - probably reset? undocumented */
+#define   VIA_REG_CTRL_INT (VIA_REG_CTRL_INT_FLAG | VIA_REG_CTRL_INT_EOL | VIA_REG_CTRL_AUTOSTART)
+#define VIA_REG_OFFSET_TYPE		0x02	/* byte - channel type (686 only) */
+#define   VIA_REG_TYPE_AUTOSTART	0x80	/* RW - autostart at EOL */
+#define   VIA_REG_TYPE_16BIT		0x20	/* RW */
+#define   VIA_REG_TYPE_STEREO		0x10	/* RW */
+#define   VIA_REG_TYPE_INT_LLINE	0x00
+#define   VIA_REG_TYPE_INT_LSAMPLE	0x04
+#define   VIA_REG_TYPE_INT_LESSONE	0x08
+#define   VIA_REG_TYPE_INT_MASK		0x0c
+#define   VIA_REG_TYPE_INT_EOL		0x02
+#define   VIA_REG_TYPE_INT_FLAG		0x01
+#define VIA_REG_OFFSET_TABLE_PTR	0x04	/* dword - channel table pointer */
+#define VIA_REG_OFFSET_CURR_PTR		0x04	/* dword - channel current pointer */
+#define VIA_REG_OFFSET_STOP_IDX		0x08	/* dword - stop index, channel type, sample rate */
+#define   VIA8233_REG_TYPE_16BIT	0x00200000	/* RW */
+#define   VIA8233_REG_TYPE_STEREO	0x00100000	/* RW */
+#define VIA_REG_OFFSET_CURR_COUNT	0x0c	/* dword - channel current count (24 bit) */
+#define VIA_REG_OFFSET_CURR_INDEX	0x0f	/* byte - channel current index (for via8233 only) */
+
+#define DEFINE_VIA_REGSET(name,val) \
+enum {\
+	VIA_REG_##name##_STATUS		= (val),\
+	VIA_REG_##name##_CONTROL	= (val) + 0x01,\
+	VIA_REG_##name##_TYPE		= (val) + 0x02,\
+	VIA_REG_##name##_TABLE_PTR	= (val) + 0x04,\
+	VIA_REG_##name##_CURR_PTR	= (val) + 0x04,\
+	VIA_REG_##name##_STOP_IDX	= (val) + 0x08,\
+	VIA_REG_##name##_CURR_COUNT	= (val) + 0x0c,\
+}
+
+/* playback block */
+DEFINE_VIA_REGSET(PLAYBACK, 0x00);
+DEFINE_VIA_REGSET(CAPTURE, 0x10);
+DEFINE_VIA_REGSET(FM, 0x20);
+
+/* AC'97 */
+#define VIA_REG_AC97			0x80	/* dword */
+#define   VIA_REG_AC97_CODEC_ID_MASK	(3<<30)
+#define   VIA_REG_AC97_CODEC_ID_SHIFT	30
+#define   VIA_REG_AC97_CODEC_ID_PRIMARY	0x00
+#define   VIA_REG_AC97_CODEC_ID_SECONDARY 0x01
+#define   VIA_REG_AC97_SECONDARY_VALID	(1<<27)
+#define   VIA_REG_AC97_PRIMARY_VALID	(1<<25)
+#define   VIA_REG_AC97_BUSY		(1<<24)
+#define   VIA_REG_AC97_READ		(1<<23)
+#define   VIA_REG_AC97_CMD_SHIFT	16
+#define   VIA_REG_AC97_CMD_MASK		0x7e
+#define   VIA_REG_AC97_DATA_SHIFT	0
+#define   VIA_REG_AC97_DATA_MASK	0xffff
+#define VIA_REG_SGD_SHADOW		0x84	/* dword */
+
+/* multi-channel and capture registers for via8233 */
+DEFINE_VIA_REGSET(MULTPLAY, 0x40);
+DEFINE_VIA_REGSET(CAPTURE_8233, 0x60);
+
+/* via8233-specific registers */
+#define VIA_REG_PLAYBACK_VOLUME_L	0x02	/* byte */
+#define VIA_REG_PLAYBACK_VOLUME_R	0x03	/* byte */
+#define VIA_REG_MULTPLAY_FORMAT		0x42	/* byte - format and channels */
+#define   VIA_REG_MULTPLAY_FMT_8BIT	0x00
+#define   VIA_REG_MULTPLAY_FMT_16BIT	0x80
+#define   VIA_REG_MULTPLAY_FMT_CH_MASK	0x70	/* # channels << 4 (valid = 1,2,4,6) */
+#define VIA_REG_CAPTURE_FIFO		0x62	/* byte - bit 6 = fifo  enable */
+#define   VIA_REG_CAPTURE_FIFO_ENABLE	0x40
+#define VIA_REG_CAPTURE_CHANNEL		0x63	/* byte - input select */
+#define   VIA_REG_CAPTURE_CHANNEL_MIC	0x4
+#define   VIA_REG_CAPTURE_CHANNEL_LINE	0
+#define   VIA_REG_CAPTURE_SELECT_CODEC	0x03	/* recording source codec (0 = primary) */
+
+#define VIA_TBL_BIT_FLAG	0x40000000
+#define VIA_TBL_BIT_EOL		0x80000000
+
+/*
+ * pcm stream
+ */
+
+struct snd_via_sg_table {
+	unsigned int offset;
+	unsigned int size;
+} ;
+
+#define VIA_TABLE_SIZE	255
+
+typedef struct {
+	unsigned long reg_offset;
+        snd_pcm_substream_t *substream;
+	int running;
+	unsigned int tbl_entries; /* # descriptors */
+	u32 *table; /* physical address + flag */
+	dma_addr_t table_addr;
+	struct snd_via_sg_table *idx_table;
+} viadev_t;
+
+
+static int build_via_table(viadev_t *dev, snd_pcm_substream_t *substream,
+			   struct pci_dev *pci)
+{
+	int i, idx, ofs, rest, fragsize;
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	struct snd_sg_buf *sgbuf = snd_magic_cast(snd_pcm_sgbuf_t, substream->dma_private, return -EINVAL);
+
+	if (! dev->table) {
+		/* the start of each lists must be aligned to 8 bytes,
+		 * but the kernel pages are much bigger, so we don't care
+		 */
+		dev->table = (u32*)snd_malloc_pci_pages(pci, PAGE_ALIGN(VIA_TABLE_SIZE * 2 * 8), &dev->table_addr);
+		if (! dev->table)
+			return -ENOMEM;
+	}
+	if (! dev->idx_table) {
+		dev->idx_table = kmalloc(sizeof(*dev->idx_table) * VIA_TABLE_SIZE, GFP_KERNEL);
+		if (! dev->idx_table)
+			return -ENOMEM;
+	}
+
+	/* fill the entries */
+	fragsize = snd_pcm_lib_period_bytes(substream);
+	idx = 0;
+	ofs = 0;
+	for (i = 0; i < runtime->periods; i++) {
+		rest = fragsize;
+		/* fill descriptors for a period.
+		 * a period can be split to several descriptors if it's
+		 * over page boundary.
+		 */
+		do {
+			int r;
+			unsigned int flag;
+			dev->table[idx << 1] = cpu_to_le32((u32)snd_pcm_sgbuf_get_addr(sgbuf, ofs));
+			r = PAGE_SIZE - (ofs % PAGE_SIZE);
+			if (rest < r)
+				r = rest;
+			rest -= r;
+			if (! rest) {
+				if (i == runtime->periods - 1)
+					flag = VIA_TBL_BIT_EOL; /* buffer boundary */
+				else
+					flag = VIA_TBL_BIT_FLAG; /* period boundary */
+			} else
+				flag = 0; /* period continues to the next */
+			// printk("via: tbl %d: at %d  size %d (rest %d)\n", idx, ofs, r, rest);
+			dev->table[(idx<<1) + 1] = cpu_to_le32(r | flag);
+			dev->idx_table[idx].offset = ofs;
+			dev->idx_table[idx].size = r;
+			ofs += r;
+			idx++;
+			if (idx >= VIA_TABLE_SIZE) {
+				snd_printk(KERN_ERR "via82xx: too much table size!\n");
+				return -EINVAL;
+			}
+		} while (rest > 0);
+	}
+	dev->tbl_entries = idx;
+	return 0;
+}
+
+
+static void clean_via_table(viadev_t *dev, snd_pcm_substream_t *substream,
+			    struct pci_dev *pci)
+{
+	if (dev->table) {
+		snd_free_pci_pages(pci, PAGE_ALIGN(VIA_TABLE_SIZE * 2 * 8), dev->table, dev->table_addr);
+		dev->table = NULL;
+	}
+	if (dev->idx_table) {
+		kfree(dev->idx_table);
+		dev->idx_table = NULL;
+	}
+}
+
+
+/*
+ */
+
+enum { TYPE_VIA686 = 1, TYPE_VIA8233 };
+
+typedef struct _snd_via82xx via82xx_t;
+#define chip_t via82xx_t
+
+struct _snd_via82xx {
+	int irq;
+
+	unsigned long port;
+	struct resource *res_port;
+	int chip_type;
+	unsigned char revision;
+
+	unsigned char old_legacy;
+	unsigned char old_legacy_cfg;
+
+	struct pci_dev *pci;
+	snd_card_t *card;
+
+	snd_pcm_t *pcm;
+	viadev_t playback;
+	viadev_t capture;
+
+	snd_rawmidi_t *rmidi;
+
+	ac97_t *ac97;
+	unsigned int ac97_clock;
+	unsigned int ac97_secondary;	/* secondary AC'97 codec is present */
+
+	spinlock_t reg_lock;
+	spinlock_t ac97_lock;
+	snd_info_entry_t *proc_entry;
+};
+
+static struct pci_device_id snd_via82xx_ids[] __devinitdata = {
+	{ 0x1106, 0x3058, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TYPE_VIA686, },	/* 686A */
+	{ 0x1106, 0x3059, PCI_ANY_ID, PCI_ANY_ID, 0, 0, TYPE_VIA8233, },	/* VT8233 */
+	{ 0, }
+};
+
+MODULE_DEVICE_TABLE(pci, snd_via82xx_ids);
+
+/*
+ *  Basic I/O
+ */
+
+static inline unsigned int snd_via82xx_codec_xread(via82xx_t *chip)
+{
+	return inl(VIAREG(chip, AC97));
+}
+ 
+static inline void snd_via82xx_codec_xwrite(via82xx_t *chip, unsigned int val)
+{
+	outl(val, VIAREG(chip, AC97));
+}
+ 
+static int snd_via82xx_codec_ready(via82xx_t *chip, int secondary)
+{
+	unsigned int timeout = 1000;	/* 1ms */
+	unsigned int val;
+	
+	while (timeout-- > 0) {
+		udelay(1);
+		if (!((val = snd_via82xx_codec_xread(chip)) & VIA_REG_AC97_BUSY))
+			return val & 0xffff;
+	}
+	snd_printk("codec_ready: codec %i is not ready [0x%x]\n", secondary, snd_via82xx_codec_xread(chip));
+	return -EIO;
+}
+ 
+static int snd_via82xx_codec_valid(via82xx_t *chip, int secondary)
+{
+	unsigned int timeout = 1000;	/* 1ms */
+	unsigned int val;
+	unsigned int stat = !secondary ? VIA_REG_AC97_PRIMARY_VALID :
+					 VIA_REG_AC97_SECONDARY_VALID;
+	
+	while (timeout-- > 0) {
+		udelay(1);
+		if ((val = snd_via82xx_codec_xread(chip)) & stat)
+			return val & 0xffff;
+	}
+	snd_printk("codec_valid: codec %i is not valid [0x%x]\n", secondary, snd_via82xx_codec_xread(chip));
+	return -EIO;
+}
+ 
+static void snd_via82xx_codec_wait(ac97_t *ac97)
+{
+	via82xx_t *chip = snd_magic_cast(via82xx_t, ac97->private_data, return);
+	int err;
+	err = snd_via82xx_codec_ready(chip, ac97->num);
+	/* here we need to wait fairly for long time.. */
+	set_current_state(TASK_UNINTERRUPTIBLE);
+	schedule_timeout(HZ/2);
+}
+
+static void snd_via82xx_codec_write(ac97_t *ac97,
+				    unsigned short reg,
+				    unsigned short val)
+{
+	via82xx_t *chip = snd_magic_cast(via82xx_t, ac97->private_data, return);
+	unsigned int xval;
+	
+	xval = !ac97->num ? VIA_REG_AC97_CODEC_ID_PRIMARY : VIA_REG_AC97_CODEC_ID_SECONDARY;
+	xval <<= VIA_REG_AC97_CODEC_ID_SHIFT;
+	xval |= reg << VIA_REG_AC97_CMD_SHIFT;
+	xval |= val << VIA_REG_AC97_DATA_SHIFT;
+	spin_lock(&chip->ac97_lock);
+	snd_via82xx_codec_xwrite(chip, xval);
+	snd_via82xx_codec_ready(chip, ac97->num);
+	spin_unlock(&chip->ac97_lock);
+}
+
+static unsigned short snd_via82xx_codec_read(ac97_t *ac97, unsigned short reg)
+{
+	via82xx_t *chip = snd_magic_cast(via82xx_t, ac97->private_data, return ~0);
+	unsigned int xval, val = 0xffff;
+	int again = 0;
+
+	xval = ac97->num << VIA_REG_AC97_CODEC_ID_SHIFT;
+	xval |= ac97->num ? VIA_REG_AC97_SECONDARY_VALID : VIA_REG_AC97_PRIMARY_VALID;
+	xval |= VIA_REG_AC97_READ;
+	xval |= (reg & 0x7f) << VIA_REG_AC97_CMD_SHIFT;
+	spin_lock(&chip->ac97_lock);
+      	while (1) {
+      		if (again++ > 3) {
+		        spin_unlock(&chip->ac97_lock);
+		      	return 0xffff;
+		}
+		snd_via82xx_codec_xwrite(chip, xval);
+		if (snd_via82xx_codec_ready(chip, ac97->num) < 0)
+			continue;
+		if (snd_via82xx_codec_valid(chip, ac97->num) >= 0) {
+			udelay(25);
+			val = snd_via82xx_codec_xread(chip);
+			break;
+		}
+	}
+	spin_unlock(&chip->ac97_lock);
+	return val & 0xffff;
+}
+
+static void snd_via82xx_channel_reset(via82xx_t *chip, viadev_t *viadev)
+{
+	unsigned long port = chip->port + viadev->reg_offset;
+
+	outb(VIA_REG_CTRL_PAUSE | VIA_REG_CTRL_TERMINATE | VIA_REG_CTRL_RESET, port + VIA_REG_OFFSET_CONTROL);
+	udelay(50);
+	/* disable interrupts */
+	outb(0x00, port + VIA_REG_OFFSET_CONTROL);
+	/* clear interrupts */
+	outb(0x03, port + VIA_REG_OFFSET_STATUS);
+	outb(0x00, port + VIA_REG_OFFSET_TYPE); /* for via686 */
+	outl(0, port + VIA_REG_OFFSET_CURR_PTR);
+}
+
+static int snd_via82xx_trigger(via82xx_t *chip, viadev_t *viadev, int cmd)
+{
+	unsigned char val;
+	unsigned long port = chip->port + viadev->reg_offset;
+	
+	if (chip->chip_type == TYPE_VIA8233)
+		val = VIA_REG_CTRL_INT;
+	else
+		val = 0;
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		val |= VIA_REG_CTRL_START;
+		viadev->running = 1;
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+		val = VIA_REG_CTRL_TERMINATE;
+		viadev->running = 0;
+		break;
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		val |= VIA_REG_CTRL_PAUSE;
+		viadev->running = 0;
+		break;
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		viadev->running = 1;
+		break;
+	default:
+		return -EINVAL;
+	}
+	outb(val, port + VIA_REG_OFFSET_CONTROL);
+	if (cmd == SNDRV_PCM_TRIGGER_STOP)
+		snd_via82xx_channel_reset(chip, viadev);
+	return 0;
+}
+
+
+static int snd_via82xx_setup_periods(via82xx_t *chip, viadev_t *viadev,
+				     snd_pcm_substream_t *substream)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	unsigned long port = chip->port + viadev->reg_offset;
+	int err;
+
+	snd_via82xx_channel_reset(chip, viadev);
+
+	err = build_via_table(viadev, substream, chip->pci);
+	if (err < 0)
+		return err;
+
+	outl((u32)viadev->table_addr, port + VIA_REG_OFFSET_TABLE_PTR);
+	switch (chip->chip_type) {
+	case TYPE_VIA686:
+		outb(VIA_REG_TYPE_AUTOSTART |
+		     (runtime->format == SNDRV_PCM_FORMAT_S16_LE ? VIA_REG_TYPE_16BIT : 0) |
+		     (runtime->channels > 1 ? VIA_REG_TYPE_STEREO : 0) |
+		     ((viadev->reg_offset & 0x10) == 0 ? VIA_REG_TYPE_INT_LSAMPLE : 0) |
+		     VIA_REG_TYPE_INT_EOL |
+		     VIA_REG_TYPE_INT_FLAG, port + VIA_REG_OFFSET_TYPE);
+		break;
+	case TYPE_VIA8233:
+		if (viadev->reg_offset == VIA_REG_MULTPLAY_STATUS) {
+			unsigned int slots;
+			int fmt = (runtime->format == SNDRV_PCM_FORMAT_S16_LE) ? VIA_REG_MULTPLAY_FMT_16BIT : VIA_REG_MULTPLAY_FMT_8BIT;
+			fmt |= runtime->channels << 4;
+			outb(fmt, port + VIA_REG_OFFSET_TYPE);
+			/* set sample number to slot 3, 4, 7, 8, 6, 9 */
+			switch (runtime->channels) {
+			case 1: slots = (1<<0) | (1<<4); break;
+			case 2: slots = (1<<0) | (2<<4); break;
+			case 4: slots = (1<<0) | (2<<4) | (3<<8) | (4<<12); break;
+			case 6: slots = (1<<0) | (2<<4) | (5<<8) | (6<<12) | (3<<16) | (4<<20); break;
+			default: slots = 0; break;
+			}
+			/* STOP index is never reached */
+			outl(0xff000000 | slots, port + VIA_REG_OFFSET_STOP_IDX);
+		} else {
+			outl((runtime->format == SNDRV_PCM_FORMAT_S16_LE ? VIA8233_REG_TYPE_16BIT : 0) |
+			     (runtime->channels > 1 ? VIA8233_REG_TYPE_STEREO : 0) |
+			     0xff000000,    /* STOP index is never reached */
+			     port + VIA_REG_OFFSET_STOP_IDX);
+		}
+		break;
+	}
+	return 0;
+}
+
+/*
+ *  Interrupt handler
+ */
+
+static inline void snd_via82xx_update(via82xx_t *chip, viadev_t *viadev)
+{
+	outb(VIA_REG_STAT_FLAG | VIA_REG_STAT_EOL, VIAREG(chip, OFFSET_STATUS) + viadev->reg_offset);
+	if (viadev->substream && viadev->running) {
+		spin_unlock(&chip->reg_lock);
+		snd_pcm_period_elapsed(viadev->substream);
+		spin_lock(&chip->reg_lock);
+	}
+}
+
+static void snd_via82xx_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	via82xx_t *chip = snd_magic_cast(via82xx_t, dev_id, return);
+	unsigned int status;
+
+	spin_lock(&chip->reg_lock);
+	if (chip->chip_type == TYPE_VIA686) {
+		/* check mpu401 interrupt */
+		status = inl(VIAREG(chip, SGD_SHADOW));
+		if ((status & 0x00000077) == 0) {
+			spin_unlock(&chip->reg_lock);
+			if (chip->rmidi != NULL)
+				snd_mpu401_uart_interrupt(irq, chip->rmidi->private_data, regs);
+			return;
+		}
+	}
+	/* check status for each stream */
+	if (inb(chip->port + chip->playback.reg_offset) & (VIA_REG_STAT_EOL|VIA_REG_STAT_FLAG))
+		snd_via82xx_update(chip, &chip->playback);
+	if (inb(chip->port + chip->capture.reg_offset) & (VIA_REG_STAT_EOL|VIA_REG_STAT_FLAG))
+		snd_via82xx_update(chip, &chip->capture);
+	spin_unlock(&chip->reg_lock);
+}
+
+/*
+ *  PCM part
+ */
+
+static int snd_via82xx_playback_trigger(snd_pcm_substream_t * substream,
+					int cmd)
+{
+	via82xx_t *chip = snd_pcm_substream_chip(substream);
+
+	return snd_via82xx_trigger(chip, &chip->playback, cmd);
+}
+
+static int snd_via82xx_capture_trigger(snd_pcm_substream_t * substream,
+				       int cmd)
+{
+	via82xx_t *chip = snd_pcm_substream_chip(substream);
+
+	return snd_via82xx_trigger(chip, &chip->capture, cmd);
+}
+
+static int snd_via82xx_hw_params(snd_pcm_substream_t * substream,
+				 snd_pcm_hw_params_t * hw_params)
+{
+	return snd_pcm_sgbuf_alloc(substream, params_buffer_bytes(hw_params));
+}
+
+static int snd_via82xx_hw_free(snd_pcm_substream_t * substream)
+{
+	return 0;
+}
+
+static int snd_via82xx_playback_prepare(snd_pcm_substream_t * substream)
+{
+	via82xx_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+
+	snd_ac97_set_rate(chip->ac97, AC97_PCM_FRONT_DAC_RATE, runtime->rate);
+	if (chip->chip_type == TYPE_VIA8233 &&
+	    chip->playback.reg_offset != VIA_REG_MULTPLAY_STATUS) {
+		unsigned int tmp;
+		/* I don't understand this stuff but its from the documentation and this way it works */
+		outb(0 , VIAREG(chip, PLAYBACK_VOLUME_L));
+		outb(0 , VIAREG(chip, PLAYBACK_VOLUME_R));
+		tmp = inl(VIAREG(chip, PLAYBACK_STOP_IDX)) & ~0xfffff;
+		outl(tmp | (0xffff * runtime->rate)/(48000/16), VIAREG(chip, PLAYBACK_STOP_IDX));
+	}
+	return snd_via82xx_setup_periods(chip, &chip->playback, substream);
+}
+
+static int snd_via82xx_capture_prepare(snd_pcm_substream_t * substream)
+{
+	via82xx_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+
+	snd_ac97_set_rate(chip->ac97, AC97_PCM_LR_ADC_RATE, runtime->rate);
+	if (chip->chip_type == TYPE_VIA8233)
+		outb(VIA_REG_CAPTURE_FIFO_ENABLE, VIAREG(chip, CAPTURE_FIFO));
+	return snd_via82xx_setup_periods(chip, &chip->capture, substream);
+}
+
+static inline unsigned int snd_via82xx_cur_ptr(via82xx_t *chip, viadev_t *viadev)
+{
+	unsigned int val, ptr, count;
+	
+	snd_assert(viadev->tbl_entries, return 0);
+	if (!(inb(VIAREG(chip, OFFSET_STATUS) + viadev->reg_offset) & VIA_REG_STAT_ACTIVE))
+		return 0;
+
+	count = inl(VIAREG(chip, OFFSET_CURR_COUNT) + viadev->reg_offset) & 0xffffff;
+	switch (chip->chip_type) {
+	case TYPE_VIA686:
+		/* The via686a does not have the current index register,
+		 * so we need to calculate the index from CURR_PTR.
+		 */
+		ptr = inl(VIAREG(chip, OFFSET_CURR_PTR) + viadev->reg_offset);
+		if (ptr <= (unsigned int)viadev->table_addr)
+			val = 0;
+		else /* CURR_PTR holds the address + 8 */
+			val = ((ptr - (unsigned int)viadev->table_addr) / 8 - 1) % viadev->tbl_entries;
+		break;
+
+	case TYPE_VIA8233:
+	default:
+		/* ah, this register makes life easier for us here. */
+		val = inb(VIAREG(chip, OFFSET_CURR_INDEX) + viadev->reg_offset) % viadev->tbl_entries;
+		break;
+	}
+
+	/* convert to the linear position */
+	return viadev->idx_table[val].offset +
+		viadev->idx_table[val].size - count;
+}
+
+static snd_pcm_uframes_t snd_via82xx_playback_pointer(snd_pcm_substream_t * substream)
+{
+	via82xx_t *chip = snd_pcm_substream_chip(substream);
+	return bytes_to_frames(substream->runtime, snd_via82xx_cur_ptr(chip, &chip->playback));
+}
+
+static snd_pcm_uframes_t snd_via82xx_capture_pointer(snd_pcm_substream_t * substream)
+{
+	via82xx_t *chip = snd_pcm_substream_chip(substream);
+	return bytes_to_frames(substream->runtime, snd_via82xx_cur_ptr(chip, &chip->capture));
+}
+
+static snd_pcm_hardware_t snd_via82xx_playback =
+{
+	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
+				 SNDRV_PCM_INFO_MMAP_VALID |
+				 SNDRV_PCM_INFO_PAUSE),
+	.formats =		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
+	.rates =		0,
+	.rate_min =		8000,
+	.rate_max =		48000,
+	.channels_min =		1,
+	.channels_max =		2,
+	.buffer_bytes_max =	128 * 1024,
+	.period_bytes_min =	32,
+	.period_bytes_max =	128 * 1024,
+	.periods_min =		2,
+	.periods_max =		VIA_TABLE_SIZE / 2,
+	.fifo_size =		0,
+};
+
+static snd_pcm_hardware_t snd_via82xx_capture =
+{
+	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
+				 SNDRV_PCM_INFO_MMAP_VALID),
+	.formats =		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
+	.rates =		0,
+	.rate_min =		8000,
+	.rate_max =		48000,
+	.channels_min =		1,
+	.channels_max =		2,
+	.buffer_bytes_max =	128 * 1024,
+	.period_bytes_min =	32,
+	.period_bytes_max =	128 * 1024,
+	.periods_min =		2,
+	.periods_max =		VIA_TABLE_SIZE / 2,
+	.fifo_size =		0,
+};
+
+static unsigned int channels[] = {
+	1, 2, 4, 6
+};
+
+#define CHANNELS sizeof(channels) / sizeof(channels[0])
+
+static snd_pcm_hw_constraint_list_t hw_constraints_channels = {
+	.count = CHANNELS,
+	.list = channels,
+	.mask = 0,
+};
+
+static int snd_via82xx_playback_open(snd_pcm_substream_t * substream)
+{
+	via82xx_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	int err;
+
+	chip->playback.substream = substream;
+	runtime->hw = snd_via82xx_playback;
+	runtime->hw.rates = chip->ac97->rates[AC97_RATES_FRONT_DAC];
+	if (!(runtime->hw.rates & SNDRV_PCM_RATE_8000))
+		runtime->hw.rate_min = 48000;
+	if ((err = snd_pcm_sgbuf_init(substream, chip->pci, 32)) < 0)
+		return err;
+	/* we may remove following constaint when we modify table entries
+	   in interrupt */
+	if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
+		return err;
+	if (chip->chip_type == TYPE_VIA8233) {
+		runtime->hw.channels_max = 6;
+		snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, &hw_constraints_channels);
+	}
+	return 0;
+}
+
+static int snd_via82xx_capture_open(snd_pcm_substream_t * substream)
+{
+	via82xx_t *chip = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	int err;
+
+	chip->capture.substream = substream;
+	runtime->hw = snd_via82xx_capture;
+	runtime->hw.rates = chip->ac97->rates[AC97_RATES_ADC];
+	if (!(runtime->hw.rates & SNDRV_PCM_RATE_8000))
+		runtime->hw.rate_min = 48000;
+	if ((err = snd_pcm_sgbuf_init(substream, chip->pci, 32)) < 0)
+		return err;
+	if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
+		return err;
+	return 0;
+}
+
+static int snd_via82xx_playback_close(snd_pcm_substream_t * substream)
+{
+	via82xx_t *chip = snd_pcm_substream_chip(substream);
+
+	clean_via_table(&chip->playback, substream, chip->pci);
+	snd_pcm_sgbuf_delete(substream);
+	chip->playback.substream = NULL;
+	return 0;
+}
+
+static int snd_via82xx_capture_close(snd_pcm_substream_t * substream)
+{
+	via82xx_t *chip = snd_pcm_substream_chip(substream);
+
+	clean_via_table(&chip->capture, substream, chip->pci);
+	snd_pcm_sgbuf_delete(substream);
+	chip->capture.substream = NULL;
+	return 0;
+}
+
+static snd_pcm_ops_t snd_via82xx_playback_ops = {
+	.open =		snd_via82xx_playback_open,
+	.close =	snd_via82xx_playback_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	snd_via82xx_hw_params,
+	.hw_free =	snd_via82xx_hw_free,
+	.prepare =	snd_via82xx_playback_prepare,
+	.trigger =	snd_via82xx_playback_trigger,
+	.pointer =	snd_via82xx_playback_pointer,
+	.copy =		snd_pcm_sgbuf_ops_copy_playback,
+	.silence =	snd_pcm_sgbuf_ops_silence,
+	.page =		snd_pcm_sgbuf_ops_page,
+};
+
+static snd_pcm_ops_t snd_via82xx_capture_ops = {
+	.open =		snd_via82xx_capture_open,
+	.close =	snd_via82xx_capture_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	snd_via82xx_hw_params,
+	.hw_free =	snd_via82xx_hw_free,
+	.prepare =	snd_via82xx_capture_prepare,
+	.trigger =	snd_via82xx_capture_trigger,
+	.pointer =	snd_via82xx_capture_pointer,
+	.copy =		snd_pcm_sgbuf_ops_copy_capture,
+	.silence =	snd_pcm_sgbuf_ops_silence,
+	.page =		snd_pcm_sgbuf_ops_page,
+};
+
+static void snd_via82xx_pcm_free(snd_pcm_t *pcm)
+{
+	via82xx_t *chip = snd_magic_cast(via82xx_t, pcm->private_data, return);
+	chip->pcm = NULL;
+}
+
+static int __devinit snd_via82xx_pcm(via82xx_t *chip, int device, snd_pcm_t ** rpcm)
+{
+	snd_pcm_t *pcm;
+	int err;
+
+	if (rpcm)
+		*rpcm = NULL;
+	err = snd_pcm_new(chip->card, chip->card->shortname, device, 1, 1, &pcm);
+	if (err < 0)
+		return err;
+
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_via82xx_playback_ops);
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_via82xx_capture_ops);
+
+	pcm->private_data = chip;
+	pcm->private_free = snd_via82xx_pcm_free;
+	pcm->info_flags = 0;
+	strcpy(pcm->name, chip->card->shortname);
+	chip->pcm = pcm;
+
+	if (rpcm)
+		*rpcm = NULL;
+	return 0;
+}
+
+
+/*
+ *  Mixer part
+ */
+
+static int snd_via8233_capture_source_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+	static char *texts[2] = {
+		"Line", "Mic"
+	};
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 1;
+	uinfo->value.enumerated.items = 2;
+	if (uinfo->value.enumerated.item >= 2)
+		uinfo->value.enumerated.item = 1;
+	strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
+	return 0;
+}
+
+static int snd_via8233_capture_source_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	via82xx_t *chip = snd_kcontrol_chip(kcontrol);
+	ucontrol->value.enumerated.item[0] = inb(VIAREG(chip, CAPTURE_CHANNEL)) & VIA_REG_CAPTURE_CHANNEL_MIC ? 1 : 0;
+	return 0;
+}
+
+static int snd_via8233_capture_source_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	via82xx_t *chip = snd_kcontrol_chip(kcontrol);
+	unsigned long flags;
+	u8 val, oval;
+
+	spin_lock_irqsave(&chip->reg_lock, flags);
+	oval = inb(VIAREG(chip, CAPTURE_CHANNEL));
+	val = oval & ~VIA_REG_CAPTURE_CHANNEL_MIC;
+	if (ucontrol->value.enumerated.item[0])
+		val |= VIA_REG_CAPTURE_CHANNEL_MIC;
+	if (val != oval)
+		outb(val, VIAREG(chip, CAPTURE_CHANNEL));
+	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	return val != oval;
+}
+
+static snd_kcontrol_new_t snd_via8233_capture_source __devinitdata = {
+	.name = "Input Source Select",
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.info = snd_via8233_capture_source_info,
+	.get = snd_via8233_capture_source_get,
+	.put = snd_via8233_capture_source_put,
+};
+
+static void snd_via82xx_mixer_free_ac97(ac97_t *ac97)
+{
+	via82xx_t *chip = snd_magic_cast(via82xx_t, ac97->private_data, return);
+	chip->ac97 = NULL;
+}
+
+static int __devinit snd_via82xx_mixer(via82xx_t *chip)
+{
+	ac97_t ac97;
+	int err;
+
+	memset(&ac97, 0, sizeof(ac97));
+	ac97.write = snd_via82xx_codec_write;
+	ac97.read = snd_via82xx_codec_read;
+	ac97.wait = snd_via82xx_codec_wait;
+	ac97.private_data = chip;
+	ac97.private_free = snd_via82xx_mixer_free_ac97;
+	ac97.clock = chip->ac97_clock;
+	if ((err = snd_ac97_mixer(chip->card, &ac97, &chip->ac97)) < 0)
+		return err;
+	return 0;
+}
+
+/*
+ * joystick
+ */
+
+static int snd_via82xx_joystick_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_via82xx_joystick_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	via82xx_t *chip = snd_kcontrol_chip(kcontrol);
+	u16 val;
+
+	pci_read_config_word(chip->pci, 0x42, &val);
+	ucontrol->value.integer.value[0] = (val & 0x08) ? 1 : 0;
+	return 0;
+}
+
+static int snd_via82xx_joystick_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	via82xx_t *chip = snd_kcontrol_chip(kcontrol);
+	u16 val, oval;
+
+	pci_read_config_word(chip->pci, 0x42, &oval);
+	val = oval & ~0x08;
+	if (ucontrol->value.integer.value[0])
+		val |= 0x08;
+	if (val != oval) {
+		pci_write_config_word(chip->pci, 0x42, val);
+		return 1;
+	}
+	return 0;
+}
+
+static snd_kcontrol_new_t snd_via82xx_joystick_control __devinitdata = {
+	.name = "Joystick",
+	.iface = SNDRV_CTL_ELEM_IFACE_CARD,
+	.info = snd_via82xx_joystick_info,
+	.get = snd_via82xx_joystick_get,
+	.put = snd_via82xx_joystick_put,
+};
+
+/*
+ *
+ */
+
+static int __devinit snd_via82xx_chip_init(via82xx_t *chip)
+{
+	ac97_t ac97;
+	unsigned int val;
+	int max_count;
+	unsigned char pval;
+
+	memset(&ac97, 0, sizeof(ac97));
+	ac97.private_data = chip;
+
+#if 0 /* broken on K7M? */
+	if (chip->chip_type == TYPE_VIA686)
+		/* disable all legacy ports */
+		pci_write_config_byte(chip->pci, 0x42, 0);
+#endif
+	pci_read_config_byte(chip->pci, 0x40, &pval);
+	if (! (pval & 0x01)) { /* codec not ready? */
+		/* deassert ACLink reset, force SYNC */
+		pci_write_config_byte(chip->pci, 0x41, 0xe0);
+		udelay(100);
+		/* deassert ACLink reset, force SYNC (warm AC'97 reset) */
+		pci_write_config_byte(chip->pci, 0x41, 0x60);
+		udelay(2);
+		/* pci_write_config_byte(chip->pci, 0x41, 0x00);
+		   udelay(100);
+		*/
+		/* ACLink on, deassert ACLink reset, VSR, SGD data out */
+		/* note - FM data out has trouble with non VRA codecs !! */
+		pci_write_config_byte(chip->pci, 0x41, 0xcc);
+		udelay(100);
+	}
+	
+	/* Make sure VRA is enabled, in case we didn't do a
+	 * complete codec reset, above */
+	pci_read_config_byte(chip->pci, 0x41, &pval);
+	if ((pval & 0xcc) != 0xcc) {
+		/* ACLink on, deassert ACLink reset, VSR, SGD data out */
+		/* note - FM data out has trouble with non VRA codecs !! */
+		pci_write_config_byte(chip->pci, 0x41, 0xcc);
+		udelay(100);
+	}
+
+	/* wait until codec ready */
+	max_count = ((3 * HZ) / 4) + 1;
+	do {
+		pci_read_config_byte(chip->pci, 0x40, &pval);
+		if (pval & 0x01) /* primary codec ready */
+			break;
+		set_current_state(TASK_UNINTERRUPTIBLE);
+		schedule_timeout(1);
+	} while (--max_count > 0);
+
+	if ((val = snd_via82xx_codec_xread(chip)) & VIA_REG_AC97_BUSY)
+		snd_printk("AC'97 codec is not ready [0x%x]\n", val);
+
+	/* and then reset codec.. */
+	snd_via82xx_codec_ready(chip, 0);
+	snd_via82xx_codec_write(&ac97, AC97_RESET, 0x0000);
+	snd_via82xx_codec_read(&ac97, 0);
+
+#if 0 /* FIXME: we don't support the second codec yet so skip the detection now.. */
+	snd_via82xx_codec_xwrite(chip, VIA_REG_AC97_READ |
+				 VIA_REG_AC97_SECONDARY_VALID |
+				 (VIA_REG_AC97_CODEC_ID_SECONDARY << VIA_REG_AC97_CODEC_ID_SHIFT));
+	max_count = ((3 * HZ) / 4) + 1;
+	snd_via82xx_codec_xwrite(chip, VIA_REG_AC97_READ |
+				 VIA_REG_AC97_SECONDARY_VALID |
+				 (VIA_REG_AC97_CODEC_ID_SECONDARY << VIA_REG_AC97_CODEC_ID_SHIFT));
+	do {
+		if ((val = snd_via82xx_codec_xread(chip)) & VIA_REG_AC97_SECONDARY_VALID) {
+			chip->ac97_secondary = 1;
+			goto __ac97_ok2;
+		}
+		set_current_state(TASK_INTERRUPTIBLE);
+		schedule_timeout(1);
+	} while (--max_count > 0);
+	/* This is ok, the most of motherboards have only one codec */
+
+      __ac97_ok2:
+#endif
+
+	if (chip->chip_type == TYPE_VIA686) {
+		/* route FM trap to IRQ, disable FM trap */
+		pci_write_config_byte(chip->pci, 0x48, 0);
+		/* disable all GPI interrupts */
+		outl(0, chip->port + 0x8c);
+	}
+
+	/* disable interrupts */
+	snd_via82xx_channel_reset(chip, &chip->playback);
+	snd_via82xx_channel_reset(chip, &chip->capture);
+	return 0;
+}
+
+static int snd_via82xx_free(via82xx_t *chip)
+{
+	if (chip->irq < 0)
+		goto __end_hw;
+	/* disable interrupts */
+	snd_via82xx_channel_reset(chip, &chip->playback);
+	snd_via82xx_channel_reset(chip, &chip->capture);
+	/* --- */
+	synchronize_irq(chip->irq);
+      __end_hw:
+	if (chip->res_port) {
+		release_resource(chip->res_port);
+		kfree_nocheck(chip->res_port);
+	}
+	if (chip->irq >= 0)
+		free_irq(chip->irq, (void *)chip);
+	if (chip->chip_type == TYPE_VIA686) {
+		pci_write_config_byte(chip->pci, 0x42, chip->old_legacy);
+		pci_write_config_byte(chip->pci, 0x43, chip->old_legacy_cfg);
+	}
+	snd_magic_kfree(chip);
+	return 0;
+}
+
+static int snd_via82xx_dev_free(snd_device_t *device)
+{
+	via82xx_t *chip = snd_magic_cast(via82xx_t, device->device_data, return -ENXIO);
+	return snd_via82xx_free(chip);
+}
+
+static int __devinit snd_via82xx_create(snd_card_t * card,
+					struct pci_dev *pci,
+					int chip_type,
+					unsigned int ac97_clock,
+					via82xx_t ** r_via)
+{
+	via82xx_t *chip;
+	int err;
+        static snd_device_ops_t ops = {
+		.dev_free =	snd_via82xx_dev_free,
+        };
+
+	if ((err = pci_enable_device(pci)) < 0)
+		return err;
+
+	if ((chip = snd_magic_kcalloc(via82xx_t, 0, GFP_KERNEL)) == NULL)
+		return -ENOMEM;
+
+	chip->chip_type = chip_type;
+
+	spin_lock_init(&chip->reg_lock);
+	spin_lock_init(&chip->ac97_lock);
+	chip->card = card;
+	chip->pci = pci;
+	chip->irq = -1;
+
+	pci_read_config_byte(pci, 0x42, &chip->old_legacy);
+	pci_read_config_byte(pci, 0x43, &chip->old_legacy_cfg);
+
+	chip->port = pci_resource_start(pci, 0);
+	if ((chip->res_port = request_region(chip->port, 256, card->driver)) == NULL) {
+		snd_via82xx_free(chip);
+		snd_printk("unable to grab ports 0x%lx-0x%lx\n", chip->port, chip->port + 256 - 1);
+		return -EBUSY;
+	}
+	if (request_irq(pci->irq, snd_via82xx_interrupt, SA_INTERRUPT|SA_SHIRQ,
+			card->driver, (void *)chip)) {
+		snd_via82xx_free(chip);
+		snd_printk("unable to grab IRQ %d\n", chip->irq);
+		return -EBUSY;
+	}
+	chip->irq = pci->irq;
+	if (ac97_clock >= 8000 && ac97_clock <= 48000)
+		chip->ac97_clock = ac97_clock;
+	pci_read_config_byte(pci, PCI_REVISION_ID, &chip->revision);
+	synchronize_irq(chip->irq);
+
+	/* initialize offsets */
+	switch (chip->chip_type) {
+	case TYPE_VIA686:
+		chip->playback.reg_offset = VIA_REG_PLAYBACK_STATUS;
+		chip->capture.reg_offset = VIA_REG_CAPTURE_STATUS;
+		break;
+	case TYPE_VIA8233:
+		/* we use multi-channel playback mode, since this mode is supported
+		 * by all VIA8233 models (and obviously suitable for our purpose).
+		 */
+		chip->playback.reg_offset = VIA_REG_MULTPLAY_STATUS;
+		chip->capture.reg_offset = VIA_REG_CAPTURE_8233_STATUS;
+		break;
+	}
+
+	if ((err = snd_via82xx_chip_init(chip)) < 0) {
+		snd_via82xx_free(chip);
+		return err;
+	}
+
+	if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
+		snd_via82xx_free(chip);
+		return err;
+	}
+
+	/* The 8233 ac97 controller does not implement the master bit
+	 * in the pci command register. IMHO this is a violation of the PCI spec.
+	 * We call pci_set_master here because it does not hurt. */
+	pci_set_master(pci);
+
+	*r_via = chip;
+	return 0;
+}
+
+static int __devinit snd_via82xx_probe(struct pci_dev *pci,
+				       const struct pci_device_id *id)
+{
+	static int dev;
+	snd_card_t *card;
+	via82xx_t *chip;
+	int pcm_dev = 0;
+	int chip_type;
+	int err;
+
+	if (dev >= SNDRV_CARDS)
+		return -ENODEV;
+	if (!snd_enable[dev]) {
+		dev++;
+		return -ENOENT;
+	}
+
+	card = snd_card_new(snd_index[dev], snd_id[dev], THIS_MODULE, 0);
+	if (card == NULL)
+		return -ENOMEM;
+
+	chip_type = id->driver_data;
+	switch (chip_type) {
+	case TYPE_VIA686:
+		strcpy(card->driver, "VIA686A");
+		strcpy(card->shortname, "VIA 82C686A/B");
+		break;
+	case TYPE_VIA8233:
+		strcpy(card->driver, "VIA8233");
+		strcpy(card->shortname, "VIA 8233A/C");
+		break;
+	default:
+		snd_printk(KERN_ERR "invalid chip type %d\n", chip_type);
+		snd_card_free(card);
+		return -EINVAL;
+	}
+		
+	if ((err = snd_via82xx_create(card, pci, chip_type, snd_ac97_clock[dev], &chip)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+
+
+	if (snd_via82xx_mixer(chip) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	if (snd_via82xx_pcm(chip, pcm_dev++, NULL) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+#if 0
+	if (snd_via82xx_pcm_fm(chip, pcm_dev++, NULL) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+#endif
+
+	if (chip->chip_type == TYPE_VIA686) {
+		unsigned char legacy, legacy_cfg;
+		int rev_h = 0;
+		legacy = chip->old_legacy;
+		legacy_cfg = chip->old_legacy_cfg;
+		legacy |= 0x40;		/* disable MIDI */
+		legacy &= ~0x08;	/* disable joystick */
+		if (chip->revision >= 0x20) {
+			if (check_region(pci_resource_start(pci, 2), 4)) {
+				rev_h = 0;
+				legacy &= ~0x80;	/* disable PCI I/O 2 */
+			} else {
+				rev_h = 1;
+				legacy |= 0x80;		/* enable PCI I/O 2 */
+			}
+		}
+		pci_write_config_byte(pci, 0x42, legacy);
+		pci_write_config_byte(pci, 0x43, legacy_cfg);
+		if (rev_h && snd_mpu_port[dev] >= 0x200) {	/* force MIDI */
+			legacy |= 0x02;	/* enable MPU */
+			pci_write_config_dword(pci, 0x18, (snd_mpu_port[dev] & 0xfffc) | 0x01);
+		} else {
+			if (rev_h && (legacy & 0x02)) {
+				snd_mpu_port[dev] = pci_resource_start(pci, 2);
+				if (snd_mpu_port[dev] < 0x200)	/* bad value */
+					legacy &= ~0x02;	/* disable MIDI */
+			} else {
+				switch (snd_mpu_port[dev]) {	/* force MIDI */
+				case 0x300:
+				case 0x310:
+				case 0x320:
+				case 0x330:
+					legacy_cfg &= ~(3 << 2);
+					legacy_cfg |= (snd_mpu_port[dev] & 0x0030) >> 2;
+					legacy |= 0x02;
+					break;
+				default:			/* no, use BIOS settings */
+					if (legacy & 0x02)
+						snd_mpu_port[dev] = 0x300 + ((legacy_cfg & 0x000c) << 2);
+				}
+			}
+		}
+		pci_write_config_byte(pci, 0x42, legacy);
+		pci_write_config_byte(pci, 0x43, legacy_cfg);
+		if (legacy & 0x02) {
+			if (check_region(snd_mpu_port[dev], 2)) {
+				printk(KERN_WARNING "unable to get MPU-401 port at 0x%lx, skipping\n", snd_mpu_port[dev]);
+				legacy &= ~0x02;
+				pci_write_config_byte(pci, 0x42, legacy);
+				goto __skip_mpu;
+			}
+			if (snd_mpu401_uart_new(card, 0, MPU401_HW_VIA686A,
+						snd_mpu_port[dev], 0,
+						pci->irq, 0,
+						&chip->rmidi) < 0) {
+				printk(KERN_WARNING "unable to initialize MPU-401 at 0x%lx, skipping\n", snd_mpu_port[dev]);
+				legacy &= ~0x02;
+				pci_write_config_byte(pci, 0x42, legacy);
+				goto __skip_mpu;
+			}
+			legacy &= ~0x40;	/* enable MIDI interrupt */
+			pci_write_config_byte(pci, 0x42, legacy);
+		__skip_mpu:
+			;
+		}
+	
+		/* card switches */
+		err = snd_ctl_add(card, snd_ctl_new1(&snd_via82xx_joystick_control, chip));
+		if (err < 0) {
+			snd_card_free(card);
+			return err;
+		}
+
+	} else {
+		/* VIA8233 */
+		err = snd_ctl_add(card, snd_ctl_new1(&snd_via8233_capture_source, chip));
+		if (err < 0) {
+			snd_card_free(card);
+			return err;
+		}
+	}
+
+	sprintf(card->longname, "%s at 0x%lx, irq %d",
+		card->shortname, chip->port, chip->irq);
+
+	if ((err = snd_card_register(card)) < 0) {
+		snd_card_free(card);
+		return err;
+	}
+	pci_set_drvdata(pci, card);
+	dev++;
+	return 0;
+}
+
+static void __devexit snd_via82xx_remove(struct pci_dev *pci)
+{
+	snd_card_free(pci_get_drvdata(pci));
+	pci_set_drvdata(pci, NULL);
+}
+
+static struct pci_driver driver = {
+	.name = "VIA 82xx Audio",
+	.id_table = snd_via82xx_ids,
+	.probe = snd_via82xx_probe,
+	.remove = __devexit_p(snd_via82xx_remove),
+};
+
+static int __init alsa_card_via82xx_init(void)
+{
+	int err;
+
+	if ((err = pci_module_init(&driver)) < 0) {
+#ifdef MODULE
+		printk(KERN_ERR "VIA 82xx soundcard not found or device busy\n");
+#endif
+		return err;
+	}
+	return 0;
+}
+
+static void __exit alsa_card_via82xx_exit(void)
+{
+	pci_unregister_driver(&driver);
+}
+
+module_init(alsa_card_via82xx_init)
+module_exit(alsa_card_via82xx_exit)
+
+#ifndef MODULE
+
+/* format is: snd-via82xx=snd_enable,snd_index,snd_id,
+			  snd_mpu_port,snd_ac97_clock */
+
+static int __init alsa_card_via82xx_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 &&
+	       get_option(&str,(int *)&snd_mpu_port[nr_dev]) == 2 &&
+	       get_option(&str,&snd_ac97_clock[nr_dev]) == 2);
+	nr_dev++;
+	return 1;
+}
+
+__setup("snd-via82xx=", alsa_card_via82xx_setup);
+
+#endif /* ifndef MODULE */
+ 
diff -Nru a/sound/pci/ymfpci/ymfpci.c b/sound/pci/ymfpci/ymfpci.c
--- a/sound/pci/ymfpci/ymfpci.c	Mon Oct  7 11:25:33 2002
+++ b/sound/pci/ymfpci/ymfpci.c	Mon Oct  7 11:25:33 2002
@@ -279,13 +279,13 @@
 }
 
 static struct pci_driver driver = {
-	name: "Yamaha DS-XG PCI",
-	id_table: snd_ymfpci_ids,
-	probe: snd_card_ymfpci_probe,
-	remove: __devexit_p(snd_card_ymfpci_remove),
+	.name = "Yamaha DS-XG PCI",
+	.id_table = snd_ymfpci_ids,
+	.probe = snd_card_ymfpci_probe,
+	.remove = __devexit_p(snd_card_ymfpci_remove),
 #ifdef CONFIG_PM
-	suspend: snd_card_ymfpci_suspend,
-	resume: snd_card_ymfpci_resume,
+	.suspend = snd_card_ymfpci_suspend,
+	.resume = snd_card_ymfpci_resume,
 #endif	
 };
 
diff -Nru a/sound/pci/ymfpci/ymfpci_main.c b/sound/pci/ymfpci/ymfpci_main.c
--- a/sound/pci/ymfpci/ymfpci_main.c	Mon Oct  7 11:25:37 2002
+++ b/sound/pci/ymfpci/ymfpci_main.c	Mon Oct  7 11:25:37 2002
@@ -24,7 +24,6 @@
  *
  */
 
-#define __NO_VERSION__
 #include <sound/driver.h>
 #include <asm/io.h>
 #include <linux/delay.h>
@@ -788,46 +787,46 @@
 
 static snd_pcm_hardware_t snd_ymfpci_playback =
 {
-	info:			(SNDRV_PCM_INFO_MMAP |
+	.info =			(SNDRV_PCM_INFO_MMAP |
 				 SNDRV_PCM_INFO_MMAP_VALID | 
 				 SNDRV_PCM_INFO_INTERLEAVED |
 				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
 				 SNDRV_PCM_INFO_PAUSE |
 				 SNDRV_PCM_INFO_RESUME),
-	formats:		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
-	rates:			SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
-	rate_min:		8000,
-	rate_max:		48000,
-	channels_min:		1,
-	channels_max:		2,
-	buffer_bytes_max:	256 * 1024, /* FIXME: enough? */
-	period_bytes_min:	64,
-	period_bytes_max:	256 * 1024, /* FIXME: enough? */
-	periods_min:		3,
-	periods_max:		1024,
-	fifo_size:		0,
+	.formats =		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
+	.rates =		SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+	.rate_min =		8000,
+	.rate_max =		48000,
+	.channels_min =		1,
+	.channels_max =		2,
+	.buffer_bytes_max =	256 * 1024, /* FIXME: enough? */
+	.period_bytes_min =	64,
+	.period_bytes_max =	256 * 1024, /* FIXME: enough? */
+	.periods_min =		3,
+	.periods_max =		1024,
+	.fifo_size =		0,
 };
 
 static snd_pcm_hardware_t snd_ymfpci_capture =
 {
-	info:			(SNDRV_PCM_INFO_MMAP |
+	.info =			(SNDRV_PCM_INFO_MMAP |
 				 SNDRV_PCM_INFO_MMAP_VALID |
 				 SNDRV_PCM_INFO_INTERLEAVED |
 				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
 				 SNDRV_PCM_INFO_PAUSE |
 				 SNDRV_PCM_INFO_RESUME),
-	formats:		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
-	rates:			SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
-	rate_min:		8000,
-	rate_max:		48000,
-	channels_min:		1,
-	channels_max:		2,
-	buffer_bytes_max:	256 * 1024, /* FIXME: enough? */
-	period_bytes_min:	64,
-	period_bytes_max:	256 * 1024, /* FIXME: enough? */
-	periods_min:		3,
-	periods_max:		1024,
-	fifo_size:		0,
+	.formats =		SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE,
+	.rates =		SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000,
+	.rate_min =		8000,
+	.rate_max =		48000,
+	.channels_min =		1,
+	.channels_max =		2,
+	.buffer_bytes_max =	256 * 1024, /* FIXME: enough? */
+	.period_bytes_min =	64,
+	.period_bytes_max =	256 * 1024, /* FIXME: enough? */
+	.periods_min =		3,
+	.periods_max =		1024,
+	.fifo_size =		0,
 };
 
 static void snd_ymfpci_pcm_free_substream(snd_pcm_runtime_t *runtime)
@@ -1053,25 +1052,25 @@
 }
 
 static snd_pcm_ops_t snd_ymfpci_playback_ops = {
-	open:			snd_ymfpci_playback_open,
-	close:			snd_ymfpci_playback_close,
-	ioctl:			snd_pcm_lib_ioctl,
-	hw_params:		snd_ymfpci_playback_hw_params,
-	hw_free:		snd_ymfpci_playback_hw_free,
-	prepare:		snd_ymfpci_playback_prepare,
-	trigger:		snd_ymfpci_playback_trigger,
-	pointer:		snd_ymfpci_playback_pointer,
+	.open =			snd_ymfpci_playback_open,
+	.close =		snd_ymfpci_playback_close,
+	.ioctl =		snd_pcm_lib_ioctl,
+	.hw_params =		snd_ymfpci_playback_hw_params,
+	.hw_free =		snd_ymfpci_playback_hw_free,
+	.prepare =		snd_ymfpci_playback_prepare,
+	.trigger =		snd_ymfpci_playback_trigger,
+	.pointer =		snd_ymfpci_playback_pointer,
 };
 
 static snd_pcm_ops_t snd_ymfpci_capture_rec_ops = {
-	open:			snd_ymfpci_capture_rec_open,
-	close:			snd_ymfpci_capture_close,
-	ioctl:			snd_pcm_lib_ioctl,
-	hw_params:		snd_ymfpci_capture_hw_params,
-	hw_free:		snd_ymfpci_capture_hw_free,
-	prepare:		snd_ymfpci_capture_prepare,
-	trigger:		snd_ymfpci_capture_trigger,
-	pointer:		snd_ymfpci_capture_pointer,
+	.open =			snd_ymfpci_capture_rec_open,
+	.close =		snd_ymfpci_capture_close,
+	.ioctl =		snd_pcm_lib_ioctl,
+	.hw_params =		snd_ymfpci_capture_hw_params,
+	.hw_free =		snd_ymfpci_capture_hw_free,
+	.prepare =		snd_ymfpci_capture_prepare,
+	.trigger =		snd_ymfpci_capture_trigger,
+	.pointer =		snd_ymfpci_capture_pointer,
 };
 
 static void snd_ymfpci_pcm_free(snd_pcm_t *pcm)
@@ -1109,14 +1108,14 @@
 }
 
 static snd_pcm_ops_t snd_ymfpci_capture_ac97_ops = {
-	open:			snd_ymfpci_capture_ac97_open,
-	close:			snd_ymfpci_capture_close,
-	ioctl:			snd_pcm_lib_ioctl,
-	hw_params:		snd_ymfpci_capture_hw_params,
-	hw_free:		snd_ymfpci_capture_hw_free,
-	prepare:		snd_ymfpci_capture_prepare,
-	trigger:		snd_ymfpci_capture_trigger,
-	pointer:		snd_ymfpci_capture_pointer,
+	.open =			snd_ymfpci_capture_ac97_open,
+	.close =		snd_ymfpci_capture_close,
+	.ioctl =		snd_pcm_lib_ioctl,
+	.hw_params =		snd_ymfpci_capture_hw_params,
+	.hw_free =		snd_ymfpci_capture_hw_free,
+	.prepare =		snd_ymfpci_capture_prepare,
+	.trigger =		snd_ymfpci_capture_trigger,
+	.pointer =		snd_ymfpci_capture_pointer,
 };
 
 static void snd_ymfpci_pcm2_free(snd_pcm_t *pcm)
@@ -1153,14 +1152,14 @@
 }
 
 static snd_pcm_ops_t snd_ymfpci_playback_spdif_ops = {
-	open:			snd_ymfpci_playback_spdif_open,
-	close:			snd_ymfpci_playback_spdif_close,
-	ioctl:			snd_pcm_lib_ioctl,
-	hw_params:		snd_ymfpci_playback_hw_params,
-	hw_free:		snd_ymfpci_playback_hw_free,
-	prepare:		snd_ymfpci_playback_prepare,
-	trigger:		snd_ymfpci_playback_trigger,
-	pointer:		snd_ymfpci_playback_pointer,
+	.open =			snd_ymfpci_playback_spdif_open,
+	.close =		snd_ymfpci_playback_spdif_close,
+	.ioctl =		snd_pcm_lib_ioctl,
+	.hw_params =		snd_ymfpci_playback_hw_params,
+	.hw_free =		snd_ymfpci_playback_hw_free,
+	.prepare =		snd_ymfpci_playback_prepare,
+	.trigger =		snd_ymfpci_playback_trigger,
+	.pointer =		snd_ymfpci_playback_pointer,
 };
 
 static void snd_ymfpci_pcm_spdif_free(snd_pcm_t *pcm)
@@ -1193,14 +1192,14 @@
 }
 
 static snd_pcm_ops_t snd_ymfpci_playback_4ch_ops = {
-	open:			snd_ymfpci_playback_4ch_open,
-	close:			snd_ymfpci_playback_4ch_close,
-	ioctl:			snd_pcm_lib_ioctl,
-	hw_params:		snd_ymfpci_playback_hw_params,
-	hw_free:		snd_ymfpci_playback_hw_free,
-	prepare:		snd_ymfpci_playback_prepare,
-	trigger:		snd_ymfpci_playback_trigger,
-	pointer:		snd_ymfpci_playback_pointer,
+	.open =			snd_ymfpci_playback_4ch_open,
+	.close =		snd_ymfpci_playback_4ch_close,
+	.ioctl =		snd_pcm_lib_ioctl,
+	.hw_params =		snd_ymfpci_playback_hw_params,
+	.hw_free =		snd_ymfpci_playback_hw_free,
+	.prepare =		snd_ymfpci_playback_prepare,
+	.trigger =		snd_ymfpci_playback_trigger,
+	.pointer =		snd_ymfpci_playback_pointer,
 };
 
 static void snd_ymfpci_pcm_4ch_free(snd_pcm_t *pcm)
@@ -1273,11 +1272,11 @@
 
 static snd_kcontrol_new_t snd_ymfpci_spdif_default __devinitdata =
 {
-	iface:		SNDRV_CTL_ELEM_IFACE_PCM,
-	name:           SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
-	info:		snd_ymfpci_spdif_default_info,
-	get:		snd_ymfpci_spdif_default_get,
-	put:		snd_ymfpci_spdif_default_put
+	.iface =	SNDRV_CTL_ELEM_IFACE_PCM,
+	.name =         SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
+	.info =		snd_ymfpci_spdif_default_info,
+	.get =		snd_ymfpci_spdif_default_get,
+	.put =		snd_ymfpci_spdif_default_put
 };
 
 static int snd_ymfpci_spdif_mask_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
@@ -1302,11 +1301,11 @@
 
 static snd_kcontrol_new_t snd_ymfpci_spdif_mask __devinitdata =
 {
-	access:		SNDRV_CTL_ELEM_ACCESS_READ,
-	iface:		SNDRV_CTL_ELEM_IFACE_PCM,
-	name:           SNDRV_CTL_NAME_IEC958("",PLAYBACK,CON_MASK),
-	info:		snd_ymfpci_spdif_mask_info,
-	get:		snd_ymfpci_spdif_mask_get,
+	.access =	SNDRV_CTL_ELEM_ACCESS_READ,
+	.iface =		SNDRV_CTL_ELEM_IFACE_PCM,
+	.name =           SNDRV_CTL_NAME_IEC958("",PLAYBACK,CON_MASK),
+	.info =		snd_ymfpci_spdif_mask_info,
+	.get =		snd_ymfpci_spdif_mask_get,
 };
 
 static int snd_ymfpci_spdif_stream_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
@@ -1350,12 +1349,12 @@
 
 static snd_kcontrol_new_t snd_ymfpci_spdif_stream __devinitdata =
 {
-	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_ymfpci_spdif_stream_info,
-	get:		snd_ymfpci_spdif_stream_get,
-	put:		snd_ymfpci_spdif_stream_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_ymfpci_spdif_stream_info,
+	.get =		snd_ymfpci_spdif_stream_get,
+	.put =		snd_ymfpci_spdif_stream_put
 };
 
 /*
@@ -1363,10 +1362,10 @@
  */
 
 #define YMFPCI_SINGLE(xname, xindex, reg) \
-{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \
-  info: snd_ymfpci_info_single, \
-  get: snd_ymfpci_get_single, put: snd_ymfpci_put_single, \
-  private_value: reg }
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
+  .info = snd_ymfpci_info_single, \
+  .get = snd_ymfpci_get_single, .put = snd_ymfpci_put_single, \
+  .private_value = reg }
 
 static int snd_ymfpci_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
 {
@@ -1429,10 +1428,10 @@
 }
 
 #define YMFPCI_DOUBLE(xname, xindex, reg) \
-{ iface: SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex, \
-  info: snd_ymfpci_info_double, \
-  get: snd_ymfpci_get_double, put: snd_ymfpci_put_double, \
-  private_value: reg }
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
+  .info = snd_ymfpci_info_double, \
+  .get = snd_ymfpci_get_double, .put = snd_ymfpci_put_double, \
+  .private_value = reg }
 
 static int snd_ymfpci_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo)
 {
@@ -1546,11 +1545,11 @@
 YMFPCI_SINGLE(SNDRV_CTL_NAME_IEC958("",PLAYBACK,SWITCH), 0, YDSXGR_SPDIFOUTCTRL),
 YMFPCI_SINGLE(SNDRV_CTL_NAME_IEC958("",CAPTURE,SWITCH), 0, YDSXGR_SPDIFINCTRL),
 {
-	iface: SNDRV_CTL_ELEM_IFACE_MIXER,
-	name: "4ch Duplication",
-	info: snd_ymfpci_info_dup4ch,
-	get: snd_ymfpci_get_dup4ch,
-	put: snd_ymfpci_put_dup4ch,
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "4ch Duplication",
+	.info = snd_ymfpci_info_dup4ch,
+	.get = snd_ymfpci_get_dup4ch,
+	.put = snd_ymfpci_put_dup4ch,
 },
 };
 
@@ -1627,12 +1626,12 @@
 }
 
 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,
+	.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,
 };
 
 
@@ -1779,19 +1778,19 @@
 }
 
 static snd_kcontrol_new_t snd_ymfpci_control_joystick __devinitdata = {
-	name: "Joystick",
-	iface: SNDRV_CTL_ELEM_IFACE_CARD,
-	info: snd_ymfpci_joystick_info,
-	get: snd_ymfpci_joystick_get,
-	put: snd_ymfpci_joystick_put,
+	.name = "Joystick",
+	.iface = SNDRV_CTL_ELEM_IFACE_CARD,
+	.info = snd_ymfpci_joystick_info,
+	.get = snd_ymfpci_joystick_get,
+	.put = snd_ymfpci_joystick_put,
 };
 
 static snd_kcontrol_new_t snd_ymfpci_control_joystick_addr __devinitdata = {
-	name: "Joystick Address",
-	iface: SNDRV_CTL_ELEM_IFACE_CARD,
-	info: snd_ymfpci_joystick_addr_info,
-	get: snd_ymfpci_joystick_addr_get,
-	put: snd_ymfpci_joystick_addr_put,
+	.name = "Joystick Address",
+	.iface = SNDRV_CTL_ELEM_IFACE_CARD,
+	.info = snd_ymfpci_joystick_addr_info,
+	.get = snd_ymfpci_joystick_addr_get,
+	.put = snd_ymfpci_joystick_addr_put,
 };
 
 int __devinit snd_ymfpci_joystick(ymfpci_t *chip)
@@ -2200,7 +2199,7 @@
 	ymfpci_t *chip;
 	int err;
 	static snd_device_ops_t ops = {
-		dev_free:	snd_ymfpci_dev_free,
+		.dev_free =	snd_ymfpci_dev_free,
 	};
 	
 	*rchip = NULL;
diff -Nru a/sound/ppc/awacs.c b/sound/ppc/awacs.c
--- a/sound/ppc/awacs.c	Mon Oct  7 11:25:35 2002
+++ b/sound/ppc/awacs.c	Mon Oct  7 11:25:35 2002
@@ -20,12 +20,12 @@
  */
 
 
-#define __NO_VERSION__
 #include <sound/driver.h>
 #include <asm/io.h>
 #include <asm/nvram.h>
 #include <linux/init.h>
 #include <linux/delay.h>
+#include <linux/slab.h>
 #include <sound/core.h>
 #include "pmac.h"
 
@@ -82,9 +82,18 @@
 	snd_pmac_awacs_write(chip, val | (reg << 12));
 }
 
+static void do_mdelay(int msec, int can_schedule)
+{
+	if (can_schedule) {
+		set_current_state(TASK_UNINTERRUPTIBLE);
+		schedule_timeout((msec * HZ + 999) / 1000);
+	} else
+		mdelay(msec);
+}
+
 #ifdef CONFIG_PMAC_PBOOK
 /* Recalibrate chip */
-static void screamer_recalibrate(pmac_t *chip)
+static void screamer_recalibrate(pmac_t *chip, int can_schedule)
 {
 	if (chip->model != PMAC_SCREAMER)
 		return;
@@ -92,15 +101,15 @@
 	/* Sorry for the horrible delays... I hope to get that improved
 	 * by making the whole PM process asynchronous in a future version
 	 */
-	mdelay(750);
+	do_mdelay(750, can_schedule);
 	snd_pmac_awacs_write_noreg(chip, 1,
 				   chip->awacs_reg[1] | MASK_RECALIBRATE | MASK_CMUTE | MASK_AMUTE);
-	mdelay(1000);
+	do_mdelay(1000, can_schedule);
 	snd_pmac_awacs_write_noreg(chip, 1, chip->awacs_reg[1]);
 }
 
 #else
-#define screamer_recalibrate(chip) /* NOP */
+#define screamer_recalibrate(chip, can_schedule) /* NOP */
 #endif
 
 
@@ -135,12 +144,20 @@
 	pmac_t *chip = snd_kcontrol_chip(kcontrol);
 	int reg = kcontrol->private_value & 0xff;
 	int lshift = (kcontrol->private_value >> 8) & 0xff;
+	int inverted = (kcontrol->private_value >> 16) & 1;
 	unsigned long flags;
+	int vol[2];
 
 	spin_lock_irqsave(&chip->reg_lock, flags);
-	ucontrol->value.integer.value[0] = 0x0f - ((chip->awacs_reg[reg] >> lshift) & 0xf);
-	ucontrol->value.integer.value[1] = 0x0f - (chip->awacs_reg[reg] & 0xf);
+	vol[0] = (chip->awacs_reg[reg] >> lshift) & 0xf;
+	vol[1] = chip->awacs_reg[reg] & 0xf;
 	spin_unlock_irqrestore(&chip->reg_lock, flags);
+	if (inverted) {
+		vol[0] = 0x0f - vol[0];
+		vol[1] = 0x0f - vol[1];
+	}
+	ucontrol->value.integer.value[0] = vol[0];
+	ucontrol->value.integer.value[1] = vol[1];
 	return 0;
 }
 
@@ -149,14 +166,24 @@
 	pmac_t *chip = snd_kcontrol_chip(kcontrol);
 	int reg = kcontrol->private_value & 0xff;
 	int lshift = (kcontrol->private_value >> 8) & 0xff;
+	int inverted = (kcontrol->private_value >> 16) & 1;
 	int val, oldval;
 	unsigned long flags;
+	int vol[2];
 
+	vol[0] = ucontrol->value.integer.value[0];
+	vol[1] = ucontrol->value.integer.value[1];
+	if (inverted) {
+		vol[0] = 0x0f - vol[0];
+		vol[1] = 0x0f - vol[1];
+	}
+	vol[0] &= 0x0f;
+	vol[1] &= 0x0f;
 	spin_lock_irqsave(&chip->reg_lock, flags);
 	oldval = chip->awacs_reg[reg];
 	val = oldval & ~(0xf | (0xf << lshift));
-	val |= ((0x0f - (ucontrol->value.integer.value[0] & 0xf)) << lshift);
-	val |= 0x0f - (ucontrol->value.integer.value[1] & 0xf);
+	val |= vol[0] << lshift;
+	val |= vol[1];
 	if (oldval != val)
 		snd_pmac_awacs_write_reg(chip, reg, val);
 	spin_unlock_irqrestore(&chip->reg_lock, flags);
@@ -164,12 +191,12 @@
 }
 
 
-#define AWACS_VOLUME(xname, xreg, xshift) \
-{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: 0, \
+#define AWACS_VOLUME(xname, xreg, xshift, xinverted) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = 0, \
   .info = snd_pmac_awacs_info_volume, \
   .get = snd_pmac_awacs_get_volume, \
   .put = snd_pmac_awacs_put_volume, \
-  private_value: (xreg) | ((xshift) << 8) }
+  .private_value = (xreg) | ((xshift) << 8) | ((xinverted) << 16) }
 
 /*
  * mute master/ogain for AWACS: mono
@@ -214,11 +241,11 @@
 }
 
 #define AWACS_SWITCH(xname, xreg, xshift, xinvert) \
-{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: 0, \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = 0, \
   .info = snd_pmac_boolean_mono_info, \
   .get = snd_pmac_awacs_get_switch, \
   .put = snd_pmac_awacs_put_switch, \
-  private_value: (xreg) | ((xshift) << 8) | ((xinvert) << 16) }
+  .private_value = (xreg) | ((xshift) << 8) | ((xinvert) << 16) }
 
 
 #ifdef PMAC_AMP_AVAIL
@@ -540,9 +567,9 @@
  * lists of mixer elements
  */
 static snd_kcontrol_new_t snd_pmac_awacs_mixers[] __initdata = {
-	AWACS_VOLUME("Master Playback Volume", 2, 6),
+	AWACS_VOLUME("Master Playback Volume", 2, 6, 1),
 	AWACS_SWITCH("Master Capture Switch", 1, SHIFT_LOOPTHRU, 0),
-	AWACS_VOLUME("Capture Volume", 0, 4),
+	AWACS_VOLUME("Capture Volume", 0, 4, 0),
 	AWACS_SWITCH("Line Capture Switch", 0, SHIFT_MUX_LINE, 0),
 	AWACS_SWITCH("CD Capture Switch", 0, SHIFT_MUX_CD, 0),
 	AWACS_SWITCH("Mic Capture Switch", 0, SHIFT_MUX_MIC, 0),
@@ -565,7 +592,7 @@
 };
 
 static snd_kcontrol_new_t snd_pmac_awacs_speaker_vol[] __initdata = {
-	AWACS_VOLUME("PC Speaker Playback Volume", 4, 6),
+	AWACS_VOLUME("PC Speaker Playback Volume", 4, 6, 1),
 };
 static snd_kcontrol_new_t snd_pmac_awacs_speaker_sw __initdata =
 AWACS_SWITCH("PC Speaker Playback Switch", 1, SHIFT_SPKMUTE, 1);
@@ -591,7 +618,7 @@
 /*
  * restore all registers
  */
-static void awacs_restore_all_regs(pmac_t *chip)
+static void awacs_restore_all_regs(pmac_t *chip, int can_schedule)
 {
 	snd_pmac_awacs_write_noreg(chip, 0, chip->awacs_reg[0]);
 	snd_pmac_awacs_write_noreg(chip, 1, chip->awacs_reg[1]);
@@ -599,7 +626,7 @@
 	snd_pmac_awacs_write_noreg(chip, 4, chip->awacs_reg[4]);
 	if (chip->model == PMAC_SCREAMER) {
 		snd_pmac_awacs_write_noreg(chip, 5, chip->awacs_reg[5]);
-		mdelay(100);
+		do_mdelay(100, can_schedule);
 		snd_pmac_awacs_write_noreg(chip, 6, chip->awacs_reg[6]);
 		mdelay(2);
 		snd_pmac_awacs_write_noreg(chip, 1, chip->awacs_reg[1]);
@@ -611,8 +638,8 @@
 #ifdef CONFIG_PMAC_PBOOK
 static void snd_pmac_awacs_resume(pmac_t *chip)
 {
-	awacs_restore_all_regs(chip);
-	screamer_recalibrate(chip);
+	awacs_restore_all_regs(chip, 0);
+	screamer_recalibrate(chip, 0);
 #ifdef PMAC_AMP_AVAIL
 	if (chip->mixer_data) {
 		awacs_amp_t *amp = chip->mixer_data;
@@ -720,8 +747,8 @@
 		chip->awacs_reg[7] = 0;
 	}
 
-	awacs_restore_all_regs(chip);
-	screamer_recalibrate(chip);
+	awacs_restore_all_regs(chip, 1);
+	screamer_recalibrate(chip, 1);
 
 	chip->revision = (in_le32(&chip->awacs->codec_stat) >> 12) & 0xf;
 #ifdef PMAC_AMP_AVAIL
@@ -831,6 +858,10 @@
 	chip->update_automute = snd_pmac_awacs_update_automute;
 	snd_pmac_awacs_update_automute(chip, 0); /* update the status only */
 #endif
+	if (chip->model == PMAC_SCREAMER) {
+		snd_pmac_awacs_write_noreg(chip, 6, chip->awacs_reg[6]);
+		snd_pmac_awacs_write_noreg(chip, 0, chip->awacs_reg[0]);
+	}
 
 	return 0;
 }
diff -Nru a/sound/ppc/burgundy.c b/sound/ppc/burgundy.c
--- a/sound/ppc/burgundy.c	Mon Oct  7 11:25:29 2002
+++ b/sound/ppc/burgundy.c	Mon Oct  7 11:25:29 2002
@@ -19,10 +19,10 @@
  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  */
 
-#define __NO_VERSION__
 #include <sound/driver.h>
 #include <asm/io.h>
 #include <linux/init.h>
+#include <linux/slab.h>
 #include <sound/core.h>
 #include "pmac.h"
 #include "burgundy.h"
@@ -196,11 +196,11 @@
 }
 
 #define BURGUNDY_VOLUME(xname, xindex, addr, shift) \
-{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex,\
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex,\
   .info = snd_pmac_burgundy_info_volume,\
   .get = snd_pmac_burgundy_get_volume,\
   .put = snd_pmac_burgundy_put_volume,\
-  private_value: ((ADDR2BASE(addr) & 0xff) | ((shift) << 8)) }
+  .private_value = ((ADDR2BASE(addr) & 0xff) | ((shift) << 8)) }
 
 /* lineout/speaker */
 
@@ -245,11 +245,11 @@
 }
 
 #define BURGUNDY_OUTPUT_SWITCH(xname, xindex, lmask, rmask, stereo) \
-{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex,\
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex,\
   .info = snd_pmac_burgundy_info_switch_out,\
   .get = snd_pmac_burgundy_get_switch_out,\
   .put = snd_pmac_burgundy_put_switch_out,\
-  private_value: ((lmask) | ((rmask) << 8) | ((stereo) << 24)) }
+  .private_value = ((lmask) | ((rmask) << 8) | ((stereo) << 24)) }
 
 /* line/speaker output volume */
 static int snd_pmac_burgundy_info_volume_out(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
@@ -295,11 +295,11 @@
 }
 
 #define BURGUNDY_OUTPUT_VOLUME(xname, xindex, addr, stereo) \
-{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, name: xname, index: xindex,\
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex,\
   .info = snd_pmac_burgundy_info_volume_out,\
   .get = snd_pmac_burgundy_get_volume_out,\
   .put = snd_pmac_burgundy_put_volume_out,\
-  private_value: (ADDR2BASE(addr) | ((stereo) << 24)) }
+  .private_value = (ADDR2BASE(addr) | ((stereo) << 24)) }
 
 static snd_kcontrol_new_t snd_pmac_burgundy_mixers[] __initdata = {
 	BURGUNDY_VOLUME("Master Playback Volume", 0, MASK_ADDR_BURGUNDY_MASTER_VOLUME, 8),
diff -Nru a/sound/ppc/daca.c b/sound/ppc/daca.c
--- a/sound/ppc/daca.c	Mon Oct  7 11:25:26 2002
+++ b/sound/ppc/daca.c	Mon Oct  7 11:25:26 2002
@@ -19,12 +19,12 @@
  */
 
 
-#define __NO_VERSION__
 #include <sound/driver.h>
 #include <linux/init.h>
 #include <linux/i2c.h>
 #include <linux/i2c-dev.h>
 #include <linux/kmod.h>
+#include <linux/slab.h>
 #include <sound/core.h>
 #include "pmac.h"
 
@@ -201,19 +201,19 @@
 	  .name = "Deemphasis Switch",
 	  .info = daca_info_deemphasis,
 	  .get = daca_get_deemphasis,
-	  put: daca_put_deemphasis
+	  .put = daca_put_deemphasis
 	},
 	{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 	  .name = "Master Playback Volume",
 	  .info = daca_info_volume,
 	  .get = daca_get_volume,
-	  put: daca_put_volume
+	  .put = daca_put_volume
 	},
 	{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 	  .name = "Power Amplifier Switch",
 	  .info = daca_info_amp,
 	  .get = daca_get_amp,
-	  put: daca_put_amp
+	  .put = daca_put_amp
 	},
 };
 
diff -Nru a/sound/ppc/keywest.c b/sound/ppc/keywest.c
--- a/sound/ppc/keywest.c	Mon Oct  7 11:25:33 2002
+++ b/sound/ppc/keywest.c	Mon Oct  7 11:25:33 2002
@@ -19,11 +19,11 @@
  */
 
 
-#define __NO_VERSION__
 #include <sound/driver.h>
 #include <linux/init.h>
 #include <linux/i2c.h>
 #include <linux/i2c-dev.h>
+#include <linux/slab.h>
 #include <sound/core.h>
 #include "pmac.h"
 
@@ -57,7 +57,7 @@
 		return -EINVAL;
 
 	if (strncmp(adapter->name, "mac-io", 6))
-		return 0;
+		return 0; /* ignored */
 
 	new_client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL);
 	if (! new_client)
@@ -74,11 +74,14 @@
 	new_client->id = keywest_ctx->id++; /* Automatically unique */
 	keywest_ctx->client = new_client;
 
-	if ((err = keywest_ctx->init_client(keywest_ctx)) < 0)
+	if ((err = keywest_ctx->init_client(keywest_ctx)) < 0) {
+		snd_printk(KERN_ERR "tumbler: cannot initialize the MCS\n");
 		goto __err;
+	}
 
 	/* Tell the i2c layer a new client has arrived */
 	if (i2c_attach_client(new_client)) {
+		snd_printk(KERN_ERR "tumbler: cannot attach i2c client\n");
 		err = -ENODEV;
 		goto __err;
 	}
@@ -120,10 +123,11 @@
 	if (keywest_ctx)
 		return -EBUSY;
 
+	keywest_ctx = i2c;
+
 	if ((err = i2c_add_driver(&keywest_driver))) {
 		snd_printk(KERN_ERR "cannot register keywest i2c driver\n");
 		return err;
 	}
-	keywest_ctx = i2c;
 	return 0;
 }
diff -Nru a/sound/ppc/pmac.c b/sound/ppc/pmac.c
--- a/sound/ppc/pmac.c	Mon Oct  7 11:25:35 2002
+++ b/sound/ppc/pmac.c	Mon Oct  7 11:25:35 2002
@@ -20,12 +20,12 @@
  */
 
 
-#define __NO_VERSION__
 #include <sound/driver.h>
 #include <asm/io.h>
 #include <asm/irq.h>
 #include <linux/init.h>
 #include <linux/delay.h>
+#include <linux/slab.h>
 #include <sound/core.h>
 #include "pmac.h"
 #include <sound/pcm_params.h>
@@ -244,7 +244,10 @@
 		snd_pmac_dma_set_command(rec, &chip->extra_dma);
 		snd_pmac_dma_run(rec, RUN);
 	}
-	offset = runtime->dma_addr;
+	/* continuous DMA memory type doesn't provide the physical address,
+	 * so we need to resolve the address here...
+	 */
+	offset = virt_to_bus(runtime->dma_area);
 	for (i = 0, cp = rec->cmd.cmds; i < rec->nperiods; i++, cp++) {
 		st_le32(&cp->phy_addr, offset);
 		st_le16(&cp->req_count, rec->period_size);
@@ -438,40 +441,40 @@
 
 static snd_pcm_hardware_t snd_pmac_playback =
 {
-	info:		(SNDRV_PCM_INFO_INTERLEAVED |
-			 SNDRV_PCM_INFO_MMAP |
-			 SNDRV_PCM_INFO_MMAP_VALID |
-			 SNDRV_PCM_INFO_RESUME),
-	.formats	= SNDRV_PCM_FMTBIT_S16_BE | SNDRV_PCM_FMTBIT_S16_LE,
-	.rates		= SNDRV_PCM_RATE_8000_44100,
-	.rate_min	= 7350,
-	.rate_max	= 44100,
-	.channels_min	= 2,
-	.channels_max	= 2,
-	.buffer_bytes_max	= 32768,
-	.period_bytes_min	= 256,
-	.period_bytes_max	= 16384,
-	.periods_min		= 1,
-	.periods_max		= PMAC_MAX_FRAGS,
+	.info =			(SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_MMAP |
+				 SNDRV_PCM_INFO_MMAP_VALID |
+				 SNDRV_PCM_INFO_RESUME),
+	.formats =		SNDRV_PCM_FMTBIT_S16_BE | SNDRV_PCM_FMTBIT_S16_LE,
+	.rates =		SNDRV_PCM_RATE_8000_44100,
+	.rate_min =		7350,
+	.rate_max =		44100,
+	.channels_min =		2,
+	.channels_max =		2,
+	.buffer_bytes_max =	32768,
+	.period_bytes_min =	256,
+	.period_bytes_max =	16384,
+	.periods_min =		1,
+	.periods_max =		PMAC_MAX_FRAGS,
 };
 
 static snd_pcm_hardware_t snd_pmac_capture =
 {
-	info:		(SNDRV_PCM_INFO_INTERLEAVED |
-			 SNDRV_PCM_INFO_MMAP |
-			 SNDRV_PCM_INFO_MMAP_VALID |
-			 SNDRV_PCM_INFO_RESUME),
-	.formats	= SNDRV_PCM_FMTBIT_S16_BE | SNDRV_PCM_FMTBIT_S16_LE,
-	.rates		= SNDRV_PCM_RATE_8000_44100,
-	.rate_min	= 7350,
-	.rate_max	= 44100,
-	.channels_min	= 2,
-	.channels_max	= 2,
-	.buffer_bytes_max	= 32768,
-	.period_bytes_min	= 256,
-	.period_bytes_max	= 16384,
-	.periods_min		= 1,
-	.periods_max		= PMAC_MAX_FRAGS,
+	.info =			(SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_MMAP |
+				 SNDRV_PCM_INFO_MMAP_VALID |
+				 SNDRV_PCM_INFO_RESUME),
+	.formats =		SNDRV_PCM_FMTBIT_S16_BE | SNDRV_PCM_FMTBIT_S16_LE,
+	.rates =		SNDRV_PCM_RATE_8000_44100,
+	.rate_min =		7350,
+	.rate_max =		44100,
+	.channels_min =		2,
+	.channels_max =		2,
+	.buffer_bytes_max =	32768,
+	.period_bytes_min =	256,
+	.period_bytes_max =	16384,
+	.periods_min =		1,
+	.periods_max =		PMAC_MAX_FRAGS,
 };
 
 
@@ -628,32 +631,30 @@
  */
 
 static snd_pcm_ops_t snd_pmac_playback_ops = {
-	.open		= snd_pmac_playback_open,
-	.close		= snd_pmac_playback_close,
-	.ioctl		= snd_pcm_lib_ioctl,
-	.hw_params	= snd_pmac_pcm_hw_params,
-	.hw_free	= snd_pmac_pcm_hw_free,
-	.prepare	= snd_pmac_playback_prepare,
-	.trigger	= snd_pmac_playback_trigger,
-	.pointer	= snd_pmac_playback_pointer,
+	.open =		snd_pmac_playback_open,
+	.close =	snd_pmac_playback_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	snd_pmac_pcm_hw_params,
+	.hw_free =	snd_pmac_pcm_hw_free,
+	.prepare =	snd_pmac_playback_prepare,
+	.trigger =	snd_pmac_playback_trigger,
+	.pointer =	snd_pmac_playback_pointer,
 };
 
 static snd_pcm_ops_t snd_pmac_capture_ops = {
-	.open		= snd_pmac_capture_open,
-	.close		= snd_pmac_capture_close,
-	.ioctl		= snd_pcm_lib_ioctl,
-	.hw_params	= snd_pmac_pcm_hw_params,
-	.hw_free	= snd_pmac_pcm_hw_free,
-	.prepare	= snd_pmac_capture_prepare,
-	.trigger	= snd_pmac_capture_trigger,
-	.pointer	= snd_pmac_capture_pointer,
+	.open =		snd_pmac_capture_open,
+	.close =	snd_pmac_capture_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	snd_pmac_pcm_hw_params,
+	.hw_free =	snd_pmac_pcm_hw_free,
+	.prepare =	snd_pmac_capture_prepare,
+	.trigger =	snd_pmac_capture_trigger,
+	.pointer =	snd_pmac_capture_pointer,
 };
 
 static void pmac_pcm_free(snd_pcm_t *pcm)
 {
-#if 0
 	snd_pcm_lib_preallocate_free_for_all(pcm);
-#endif
 }
 
 int __init snd_pmac_pcm_new(pmac_t *chip)
@@ -687,10 +688,8 @@
 	chip->playback.cur_freqs = chip->freqs_ok;
 	chip->capture.cur_freqs = chip->freqs_ok;
 
-#if 0
 	/* preallocate 64k buffer */
 	snd_pcm_lib_preallocate_pages_for_all(pcm, 64 * 1024, 64 * 1024, GFP_KERNEL);
-#endif
 
 	return 0;
 }
@@ -1171,8 +1170,7 @@
 		// chip->can_byte_swap = 0; /* FIXME: check this */
 		chip->control_mask = MASK_IEPC | 0x11; /* disable IEE */
 	}
-	if (device_is_compatible(sound, "tumbler") ||
-	    device_is_compatible(sound, "snapper")) {
+	if (device_is_compatible(sound, "tumbler")) {
 		chip->model = PMAC_TUMBLER;
 		chip->can_capture = 0;  /* no capture */
 		chip->can_duplex = 0;
@@ -1181,6 +1179,15 @@
 		chip->freq_table = tumbler_freqs;
 		chip->control_mask = MASK_IEPC | 0x11; /* disable IEE */
 	}
+	if (device_is_compatible(sound, "snapper")) {
+		chip->model = PMAC_SNAPPER;
+		chip->can_capture = 0;  /* no capture */
+		chip->can_duplex = 0;
+		// chip->can_byte_swap = 0; /* FIXME: check this */
+		chip->num_freqs = 2;
+		chip->freq_table = tumbler_freqs;
+		chip->control_mask = MASK_IEPC | 0x11; /* disable IEE */
+	}
 	prop = (unsigned int *)get_property(sound, "device-id", 0);
 	if (prop)
 		chip->device_id = *prop;
@@ -1306,7 +1313,7 @@
 	struct device_node *np;
 	int i, err;
 	static snd_device_ops_t ops = {
-		.dev_free	= snd_pmac_dev_free,
+		.dev_free =	snd_pmac_dev_free,
 	};
 
 	snd_runtime_check(chip_return, return -EINVAL);
diff -Nru a/sound/ppc/pmac.h b/sound/ppc/pmac.h
--- a/sound/ppc/pmac.h	Mon Oct  7 11:25:36 2002
+++ b/sound/ppc/pmac.h	Mon Oct  7 11:25:36 2002
@@ -40,6 +40,7 @@
 #endif
 #endif
 #include <linux/nvram.h>
+#include <linux/tty.h>
 #include <linux/vt_kern.h>
 #include <asm/dbdma.h>
 #include <asm/prom.h>
@@ -115,7 +116,7 @@
  */
 
 enum snd_pmac_model {
-	PMAC_AWACS, PMAC_SCREAMER, PMAC_BURGUNDY, PMAC_DACA, PMAC_TUMBLER
+	PMAC_AWACS, PMAC_SCREAMER, PMAC_BURGUNDY, PMAC_DACA, PMAC_TUMBLER, PMAC_SNAPPER
 };
 
 struct snd_pmac {
diff -Nru a/sound/ppc/powermac.c b/sound/ppc/powermac.c
--- a/sound/ppc/powermac.c	Mon Oct  7 11:25:40 2002
+++ b/sound/ppc/powermac.c	Mon Oct  7 11:25:40 2002
@@ -94,8 +94,10 @@
 			goto __error;
 		break;
 	case PMAC_TUMBLER:
-		strcpy(card->driver, "PMac Tumbler");
-		strcpy(card->shortname, "PowerMac Tumbler");
+	case PMAC_SNAPPER:
+		name_ext = chip->model == PMAC_TUMBLER ? "Tumbler" : "Snapper";
+		sprintf(card->driver, "PMac %s", name_ext);
+		sprintf(card->shortname, "PowerMac %s", name_ext);
 		sprintf(card->longname, "%s (Dev %d) Sub-frame %d",
 			card->shortname, chip->device_id, chip->subframe);
 		if ((err = snd_pmac_tumbler_init(chip)) < 0)
diff -Nru a/sound/ppc/tumbler.c b/sound/ppc/tumbler.c
--- a/sound/ppc/tumbler.c	Mon Oct  7 11:25:25 2002
+++ b/sound/ppc/tumbler.c	Mon Oct  7 11:25:25 2002
@@ -1,5 +1,5 @@
 /*
- * PMac Tumbler lowlevel functions
+ * PMac Tumbler/Snapper lowlevel functions
  *
  * Copyright (c) by Takashi Iwai <tiwai@suse.de>
  *
@@ -19,13 +19,13 @@
  */
 
 
-#define __NO_VERSION__
 #include <sound/driver.h>
 #include <linux/init.h>
 #include <linux/delay.h>
 #include <linux/i2c.h>
 #include <linux/i2c-dev.h>
 #include <linux/kmod.h>
+#include <linux/slab.h>
 #include <sound/core.h>
 #include <asm/io.h>
 #include <asm/irq.h>
@@ -46,17 +46,27 @@
 #define TAS_REG_VOL	0x04
 #define TAS_REG_TREBLE	0x05
 #define TAS_REG_BASS	0x06
-#define TAS_REG_INPUT1	0x07	/* pcm */
-#define TAS_REG_INPUT2	0x08	/* ??? */
+#define TAS_REG_INPUT1	0x07
+#define TAS_REG_INPUT2	0x08
 
+/* tas3001c */
 #define TAS_REG_PCM	TAS_REG_INPUT1
+ 
+/* tas3004 */
+#define TAS_REG_LMIX	TAS_REG_INPUT1
+#define TAS_REG_RMIX	TAS_REG_INPUT2
 
-#define TAS_MIXER_VOL_MAX	200
+/* mono volumes for tas3001c/tas3004 */
+enum {
+	VOL_IDX_PCM_MONO, /* tas3001c only */
+	VOL_IDX_BASS, VOL_IDX_TREBLE,
+	VOL_IDX_LAST_MONO
+};
 
+/* stereo volumes for tas3004 */
 enum {
-	VOL_IDX_PCM, VOL_IDX_BASS, VOL_IDX_TREBLE,
-	//VOL_IDX_ALTPCM,
-	VOL_IDX_LAST
+	VOL_IDX_PCM, VOL_IDX_PCM2, VOL_IDX_ADC,
+	VOL_IDX_LAST_MIX
 };
 
 typedef struct pmac_gpio {
@@ -77,7 +87,8 @@
 	int headphone_irq;
 	unsigned int master_vol[2];
 	unsigned int master_switch[2];
-	unsigned int mono_vol[VOL_IDX_LAST];
+	unsigned int mono_vol[VOL_IDX_LAST_MONO];
+	unsigned int mix_vol[VOL_IDX_LAST_MIX][2]; /* stereo volumes for tas3004 */
 	int drc_range;
 	int drc_enable;
 } pmac_tumbler_t;
@@ -90,9 +101,16 @@
 
 static int tumbler_init_client(pmac_keywest_t *i2c)
 {
-       /* normal operation, SCLK=64fps, i2s output, i2s input, 16bit width */
-       return snd_pmac_keywest_write_byte(i2c, TAS_REG_MCS,
-                                          (1<<6)+(2<<4)+(2<<2)+0);
+	int err, count = 10;
+	do {
+		/* normal operation, SCLK=64fps, i2s output, i2s input, 16bit width */
+		err =  snd_pmac_keywest_write_byte(i2c, TAS_REG_MCS,
+						   (1<<6)+(2<<4)+(2<<2)+0);
+		if (err >= 0)
+			return err;
+		mdelay(10);
+	} while (count--);
+	return -ENXIO;
 }
 
 
@@ -245,8 +263,11 @@
 
 
 /*
- * dynamic range compression
+ * TAS3001c dynamic range compression
  */
+
+#define TAS3001_DRC_MAX		0x5f
+
 static int tumbler_set_drc(pmac_tumbler_t *mix)
 {
 	unsigned char val[2];
@@ -256,7 +277,7 @@
   
 	if (mix->drc_enable) {
 		val[0] = 0xc1; /* enable, 3:1 compression */
-		if (mix->drc_range > 0x5f)
+		if (mix->drc_range > TAS3001_DRC_MAX)
 			val[1] = 0xf0;
 		else if (mix->drc_range < 0)
 			val[1] = 0x91;
@@ -274,12 +295,49 @@
 	return 0;
 }
 
+/*
+ * TAS3004
+ */
+
+#define TAS3004_DRC_MAX		0xef
+
+static int snapper_set_drc(pmac_tumbler_t *mix)
+{
+	unsigned char val[6];
+
+	if (! mix->i2c.client)
+		return -ENODEV;
+  
+	if (mix->drc_enable)
+		val[0] = 0x50; /* 3:1 above threshold */
+	else
+		val[0] = 0x51; /* disabled */
+	val[1] = 0x02; /* 1:1 below threshold */
+	if (mix->drc_range > 0xef)
+		val[2] = 0xef;
+	else if (mix->drc_range < 0)
+		val[2] = 0x00;
+	else
+		val[2] = mix->drc_range;
+	val[3] = 0xb0;
+	val[4] = 0x60;
+	val[5] = 0xa0;
+
+	if (snd_pmac_keywest_write(&mix->i2c, TAS_REG_DRC, 6, val) < 0) {
+		snd_printk("failed to set DRC\n");  
+		return -EINVAL; 
+	}
+	return 0;
+}
+
 static int tumbler_info_drc_value(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
 {
+	pmac_t *chip = snd_kcontrol_chip(kcontrol);
 	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
 	uinfo->count = 1;
 	uinfo->value.integer.min = 0;
-	uinfo->value.integer.max = 0x5f;
+	uinfo->value.integer.max =
+		chip->model == PMAC_TUMBLER ? TAS3001_DRC_MAX : TAS3004_DRC_MAX;
 	return 0;
 }
 
@@ -304,7 +362,10 @@
 	change = mix->drc_range != ucontrol->value.integer.value[0];
 	if (change) {
 		mix->drc_range = ucontrol->value.integer.value[0];
-		tumbler_set_drc(mix);
+		if (chip->model == PMAC_TUMBLER)
+			tumbler_set_drc(mix);
+		else
+			snapper_set_drc(mix);
 	}
 	return change;
 }
@@ -330,7 +391,10 @@
 	change = mix->drc_enable != ucontrol->value.integer.value[0];
 	if (change) {
 		mix->drc_enable = !!ucontrol->value.integer.value[0];
-		tumbler_set_drc(mix);
+		if (chip->model == PMAC_TUMBLER)
+			tumbler_set_drc(mix);
+		else
+			snapper_set_drc(mix);
 	}
 	return change;
 }
@@ -409,24 +473,15 @@
 	return change;
 }
 
+/* TAS3001c mono volumes */
 static struct tumbler_mono_vol tumbler_pcm_vol_info = {
-	.index = VOL_IDX_PCM,
+	.index = VOL_IDX_PCM_MONO,
 	.reg = TAS_REG_PCM,
 	.bytes = 3,
 	.max = number_of(mixer_volume_table),
 	.table = mixer_volume_table,
 };
 
-#if 0 // for what?
-static struct tumbler_mono_vol tumbler_altpcm_vol_info = {
-	.index = VOL_IDX_ALTPCM,
-	.reg = TAS_REG_INPUT2,
-	.bytes = 3,
-	.max = number_of(mixer_volume_table),
-	.table = mixer_volume_table,
-};
-#endif
-
 static struct tumbler_mono_vol tumbler_bass_vol_info = {
 	.index = VOL_IDX_BASS,
 	.reg = TAS_REG_BASS,
@@ -443,6 +498,24 @@
 	.table = treble_volume_table,
 };
 
+/* TAS3004 mono volumes */
+static struct tumbler_mono_vol snapper_bass_vol_info = {
+	.index = VOL_IDX_BASS,
+	.reg = TAS_REG_BASS,
+	.bytes = 1,
+	.max = number_of(snapper_bass_volume_table),
+	.table = snapper_bass_volume_table,
+};
+
+static struct tumbler_mono_vol snapper_treble_vol_info = {
+	.index = VOL_IDX_TREBLE,
+	.reg = TAS_REG_TREBLE,
+	.bytes = 1,
+	.max = number_of(snapper_treble_volume_table),
+	.table = snapper_treble_volume_table,
+};
+
+
 #define DEFINE_MONO(xname,type) { \
 	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,\
 	.name = xname, \
@@ -452,6 +525,95 @@
 	.private_value = (unsigned long)(&tumbler_##type##_vol_info), \
 }
 
+#define DEFINE_SNAPPER_MONO(xname,type) { \
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,\
+	.name = xname, \
+	.info = tumbler_info_mono, \
+	.get = tumbler_get_mono, \
+	.put = tumbler_put_mono, \
+	.private_value = (unsigned long)(&snapper_##type##_vol_info), \
+}
+
+
+/*
+ * snapper mixer volumes
+ */
+
+static int snapper_set_mix_vol1(pmac_tumbler_t *mix, int idx, int ch, int reg)
+{
+	int i, j, vol;
+	unsigned char block[9];
+
+	vol = mix->mix_vol[idx][ch];
+	if (vol >= number_of(mixer_volume_table)) {
+		vol = number_of(mixer_volume_table) - 1;
+		mix->mix_vol[idx][ch] = vol;
+	}
+
+	for (i = 0; i < 3; i++) {
+		vol = mix->mix_vol[i][ch];
+		vol = mixer_volume_table[vol];
+		for (j = 0; j < 3; j++)
+			block[i * 3 + j] = (vol >> ((2 - j) * 8)) & 0xff;
+	}
+	if (snd_pmac_keywest_write(&mix->i2c, reg, 9, block) < 0) {
+		snd_printk("failed to set mono volume %d\n", reg);  
+		return -EINVAL; 
+	}
+	return 0;
+}
+
+static int snapper_set_mix_vol(pmac_tumbler_t *mix, int idx)
+{
+	if (! mix->i2c.client)
+		return -ENODEV;
+	if (snapper_set_mix_vol1(mix, idx, 0, TAS_REG_LMIX) < 0 ||
+	    snapper_set_mix_vol1(mix, idx, 1, TAS_REG_RMIX) < 0)
+		return -EINVAL;
+	return 0;
+}
+
+static int snapper_info_mix(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 2;
+	uinfo->value.integer.min = 0;
+	uinfo->value.integer.max = number_of(mixer_volume_table) - 1;
+	return 0;
+}
+
+static int snapper_get_mix(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	int idx = (int)kcontrol->private_value;
+	pmac_t *chip = snd_kcontrol_chip(kcontrol);
+	pmac_tumbler_t *mix;
+	if (! (mix = chip->mixer_data))
+		return -ENODEV;
+	ucontrol->value.integer.value[0] = mix->mix_vol[idx][0];
+	ucontrol->value.integer.value[1] = mix->mix_vol[idx][1];
+	return 0;
+}
+
+static int snapper_put_mix(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	int idx = (int)kcontrol->private_value;
+	pmac_t *chip = snd_kcontrol_chip(kcontrol);
+	pmac_tumbler_t *mix;
+	int change;
+
+	if (! (mix = chip->mixer_data))
+		return -ENODEV;
+	change = mix->mix_vol[idx][0] != ucontrol->value.integer.value[0] ||
+		mix->mix_vol[idx][1] != ucontrol->value.integer.value[1];
+	if (change) {
+		mix->mix_vol[idx][0] = ucontrol->value.integer.value[0];
+		mix->mix_vol[idx][1] = ucontrol->value.integer.value[1];
+		snapper_set_mix_vol(mix, idx);
+	}
+	return change;
+}
+
+
 /*
  * mute switches
  */
@@ -487,6 +649,16 @@
 	return 0;
 }
 
+#define DEFINE_SNAPPER_MIX(xname,idx,ofs) { \
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,\
+	.name = xname, \
+	.info = snapper_info_mix, \
+	.get = snapper_get_mix, \
+	.put = snapper_put_mix, \
+	.index = idx,\
+	.private_value = ofs, \
+}
+
 
 /*
  */
@@ -495,29 +667,60 @@
 	  .name = "Master Playback Volume",
 	  .info = tumbler_info_master_volume,
 	  .get = tumbler_get_master_volume,
-	  put: tumbler_put_master_volume
+	  .put = tumbler_put_master_volume
 	},
 	{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 	  .name = "Master Playback Switch",
 	  .info = snd_pmac_boolean_stereo_info,
 	  .get = tumbler_get_master_switch,
-	  put: tumbler_put_master_switch
+	  .put = tumbler_put_master_switch
 	},
 	DEFINE_MONO("Tone Control - Bass", bass),
 	DEFINE_MONO("Tone Control - Treble", treble),
 	DEFINE_MONO("PCM Playback Volume", pcm),
-	//  DEFINE_MONO("Mixer2 Playback Volume", altpcm),
 	{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 	  .name = "DRC Switch",
 	  .info = snd_pmac_boolean_mono_info,
 	  .get = tumbler_get_drc_switch,
-	  put: tumbler_put_drc_switch
+	  .put = tumbler_put_drc_switch
 	},
 	{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
 	  .name = "DRC Range",
 	  .info = tumbler_info_drc_value,
 	  .get = tumbler_get_drc_value,
-	  put: tumbler_put_drc_value
+	  .put = tumbler_put_drc_value
+	},
+};
+
+static snd_kcontrol_new_t snapper_mixers[] __initdata = {
+	{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	  .name = "Master Playback Volume",
+	  .info = tumbler_info_master_volume,
+	  .get = tumbler_get_master_volume,
+	  .put = tumbler_put_master_volume
+	},
+	{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	  .name = "Master Playback Switch",
+	  .info = snd_pmac_boolean_stereo_info,
+	  .get = tumbler_get_master_switch,
+	  .put = tumbler_put_master_switch
+	},
+	DEFINE_SNAPPER_MIX("PCM Playback Volume", 0, VOL_IDX_PCM),
+	DEFINE_SNAPPER_MIX("PCM Playback Volume", 1, VOL_IDX_PCM2),
+	DEFINE_SNAPPER_MIX("Monitor Mix Volume", 0, VOL_IDX_ADC),
+	DEFINE_SNAPPER_MONO("Tone Control - Bass", bass),
+	DEFINE_SNAPPER_MONO("Tone Control - Treble", treble),
+	{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	  .name = "DRC Switch",
+	  .info = snd_pmac_boolean_mono_info,
+	  .get = tumbler_get_drc_switch,
+	  .put = tumbler_put_drc_switch
+	},
+	{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	  .name = "DRC Range",
+	  .info = tumbler_info_drc_value,
+	  .get = tumbler_get_drc_value,
+	  .put = tumbler_put_drc_value
 	},
 };
 
@@ -675,10 +878,17 @@
 	tumbler_reset_audio(chip);
 	if (mix->i2c.client)
 		tumbler_init_client(&mix->i2c);
-	tumbler_set_mono_volume(mix, &tumbler_pcm_vol_info);
-	tumbler_set_mono_volume(mix, &tumbler_bass_vol_info);
-	tumbler_set_mono_volume(mix, &tumbler_treble_vol_info);
-	// tumbler_set_mono_volume(mix, &tumbler_altpcm_vol_info);
+	if (chip->model == PMAC_TUMBLER) {
+		tumbler_set_mono_volume(mix, &tumbler_pcm_vol_info);
+		tumbler_set_mono_volume(mix, &tumbler_bass_vol_info);
+		tumbler_set_mono_volume(mix, &tumbler_treble_vol_info);
+	} else {
+		snapper_set_mix_vol(mix, VOL_IDX_PCM);
+		snapper_set_mix_vol(mix, VOL_IDX_PCM2);
+		snapper_set_mix_vol(mix, VOL_IDX_ADC);
+		tumbler_set_mono_volume(mix, &tumbler_bass_vol_info);
+		tumbler_set_mono_volume(mix, &tumbler_treble_vol_info);
+	}
 	tumbler_set_drc(mix);
 	tumbler_set_master_volume(mix);
 	if (chip->update_automute)
@@ -741,6 +951,7 @@
 	pmac_tumbler_t *mix;
 	u32 *paddr;
 	struct device_node *tas_node;
+	char *chipname;
 
 #ifdef CONFIG_KMOD
 	request_module("i2c-keywest");
@@ -770,18 +981,32 @@
 		mix->i2c.addr = TAS_I2C_ADDR;
 
 	mix->i2c.init_client = tumbler_init_client;
-	mix->i2c.name = "TAS3001c";
+	if (chip->model == PMAC_TUMBLER) {
+		mix->i2c.name = "TAS3001c";
+		chipname = "Tumbler";
+	} else {
+		mix->i2c.name = "TAS3004";
+		chipname = "Snapper";
+	}
+
 	if ((err = snd_pmac_keywest_init(&mix->i2c)) < 0)
 		return err;
 
 	/*
 	 * build mixers
 	 */
-	strcpy(chip->card->mixername, "PowerMac Tumbler");
+	sprintf(chip->card->mixername, "PowerMac %s", chipname);
 
-	for (i = 0; i < number_of(tumbler_mixers); i++) {
-		if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&tumbler_mixers[i], chip))) < 0)
-			return err;
+	if (chip->model == PMAC_TUMBLER) {
+		for (i = 0; i < number_of(tumbler_mixers); i++) {
+			if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&tumbler_mixers[i], chip))) < 0)
+				return err;
+		}
+	} else {
+		for (i = 0; i < number_of(snapper_mixers); i++) {
+			if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snapper_mixers[i], chip))) < 0)
+				return err;
+		}
 	}
 	chip->master_sw_ctl = snd_ctl_new1(&tumbler_hp_sw, chip);
 	if ((err = snd_ctl_add(chip->card, chip->master_sw_ctl)) < 0)
diff -Nru a/sound/ppc/tumbler_volume.h b/sound/ppc/tumbler_volume.h
--- a/sound/ppc/tumbler_volume.h	Mon Oct  7 11:25:39 2002
+++ b/sound/ppc/tumbler_volume.h	Mon Oct  7 11:25:39 2002
@@ -63,7 +63,7 @@
 	0x00071457, 0x00077fbb,	0x0007f17b,
 };
 
-/* treble table */
+/* treble table for TAS3001c */
 /* 0 = -18 dB, 72 = 18 dB in 0.5 dB step */
 static unsigned int treble_volume_table[] = {
 	0x96, 0x95, 0x94,
@@ -93,7 +93,7 @@
 	0x01,
 };
 
-/* bass table */
+/* bass table for TAS3001c */
 /* 0 = -18 dB, 72 = 18 dB in 0.5 dB step */
 static unsigned int bass_volume_table[] = {
 	0x86, 0x82, 0x7f,
@@ -186,3 +186,65 @@
 	0x5f4e52, 0x64f403, 0x6aef5d,
 	0x714575, 0x77fbaa, 0x7f17af,
 };
+
+
+/* treble table for TAS3004 */
+/* 0 = -18 dB, 72 = 18 dB in 0.5 dB step */
+static unsigned int snapper_treble_volume_table[] = {
+	0x96, 0x95, 0x94,
+	0x93, 0x92, 0x91,
+	0x90, 0x8f, 0x8e,
+	0x8d, 0x8c, 0x8b,
+	0x8a, 0x89, 0x88,
+	0x87, 0x86, 0x85,
+	0x84, 0x83, 0x82,
+	0x81, 0x80, 0x7f,
+	0x7e, 0x7d, 0x7c,
+	0x7b, 0x7a, 0x79,
+	0x78, 0x77, 0x76,
+	0x75, 0x74, 0x73,
+	0x72, 0x71, 0x70,
+	0x6f, 0x6d, 0x6c,
+	0x6b, 0x69, 0x68,
+	0x67, 0x65, 0x63,
+	0x62, 0x60, 0x5d,
+	0x5b, 0x59, 0x56,
+	0x53, 0x51, 0x4d,
+	0x4a, 0x47, 0x43,
+	0x3f, 0x3b, 0x36,
+	0x31, 0x2c, 0x26,
+	0x20, 0x1a, 0x13,
+	0x08, 0x04, 0x01,
+	0x01,
+};
+
+/* bass table for TAS3004 */
+/* 0 = -18 dB, 72 = 18 dB in 0.5 dB step */
+static unsigned int snapper_bass_volume_table[] = {
+	0x96, 0x95, 0x94,
+	0x93, 0x92, 0x91,
+	0x90, 0x8f, 0x8e,
+	0x8d, 0x8c, 0x8b,
+	0x8a, 0x89, 0x88,
+	0x87, 0x86, 0x85,
+	0x84, 0x83, 0x82,
+	0x81, 0x80, 0x7f,
+	0x7e, 0x7d, 0x7c,
+	0x7b, 0x7a, 0x79,
+	0x78, 0x77, 0x76,
+	0x75, 0x74, 0x73,
+	0x72, 0x71, 0x6f,
+	0x6e, 0x6d, 0x6b,
+	0x6a, 0x69, 0x67,
+	0x66, 0x65, 0x63,
+	0x62, 0x61, 0x5f,
+	0x5d, 0x5b, 0x58,
+	0x55, 0x52, 0x4f,
+	0x4c, 0x49, 0x46,
+	0x43, 0x3f, 0x3b,
+	0x37, 0x33, 0x2e,
+	0x29, 0x24, 0x1e,
+	0x18, 0x11, 0x0a,
+	0x01,
+};
+
diff -Nru a/sound/sound_core.c b/sound/sound_core.c
--- a/sound/sound_core.c	Mon Oct  7 11:25:33 2002
+++ b/sound/sound_core.c	Mon Oct  7 11:25:33 2002
@@ -122,7 +122,7 @@
  *	Remove a node from the chain. Called with the lock asserted
  */
  
-static void __sound_remove_unit(struct sound_unit **list, int unit)
+static struct sound_unit *__sound_remove_unit(struct sound_unit **list, int unit)
 {
 	while(*list)
 	{
@@ -130,13 +130,12 @@
 		if(p->unit_minor==unit)
 		{
 			*list=p->next;
-			devfs_unregister (p->de);
-			kfree(p);
-			return;
+			return p;
 		}
 		list=&(p->next);
 	}
 	printk(KERN_ERR "Sound device %d went missing!\n", unit);
+	return NULL;
 }
 
 /*
@@ -189,9 +188,15 @@
  	
 static void sound_remove_unit(struct sound_unit **list, int unit)
 {
+	struct sound_unit *p;
+
 	spin_lock(&sound_loader_lock);
-	__sound_remove_unit(list, unit);
+	p = __sound_remove_unit(list, unit);
 	spin_unlock(&sound_loader_lock);
+	if (p) {
+		devfs_unregister (p->de);
+		kfree(p);
+	}
 }
 
 /*
@@ -460,8 +465,8 @@
 static struct file_operations soundcore_fops=
 {
 	/* We must have an owner or the module locking fails */
-	owner:	THIS_MODULE,
-	open:	soundcore_open,
+	.owner	= THIS_MODULE,
+	.open	= soundcore_open,
 };
 
 static struct sound_unit *__look_for_unit(int chain, int unit)
diff -Nru a/sound/synth/emux/emux_effect.c b/sound/synth/emux/emux_effect.c
--- a/sound/synth/emux/emux_effect.c	Mon Oct  7 11:25:32 2002
+++ b/sound/synth/emux/emux_effect.c	Mon Oct  7 11:25:32 2002
@@ -22,7 +22,6 @@
  *
  */
 
-#define __NO_VERSION__
 #include "emux_voice.h"
 #include <linux/slab.h>
 
diff -Nru a/sound/synth/emux/emux_nrpn.c b/sound/synth/emux/emux_nrpn.c
--- a/sound/synth/emux/emux_nrpn.c	Mon Oct  7 11:25:25 2002
+++ b/sound/synth/emux/emux_nrpn.c	Mon Oct  7 11:25:25 2002
@@ -19,7 +19,6 @@
  *
  */
 
-#define __NO_VERSION__
 #include "emux_voice.h"
 #include <sound/asoundef.h>
 
diff -Nru a/sound/synth/emux/emux_oss.c b/sound/synth/emux/emux_oss.c
--- a/sound/synth/emux/emux_oss.c	Mon Oct  7 11:25:30 2002
+++ b/sound/synth/emux/emux_oss.c	Mon Oct  7 11:25:30 2002
@@ -22,7 +22,6 @@
  * 				midi emulation.
  */
 
-#define __NO_VERSION__
 #include <sound/driver.h>
 
 #ifdef CONFIG_SND_SEQUENCER_OSS
@@ -45,12 +44,12 @@
 
 /* operators */
 static snd_seq_oss_callback_t oss_callback = {
-	owner: THIS_MODULE,
-	open: snd_emux_open_seq_oss,
-	close: snd_emux_close_seq_oss,
-	ioctl: snd_emux_ioctl_seq_oss,
-	load_patch: snd_emux_load_patch_seq_oss,
-	reset: snd_emux_reset_seq_oss,
+	.owner = THIS_MODULE,
+	.open = snd_emux_open_seq_oss,
+	.close = snd_emux_close_seq_oss,
+	.ioctl = snd_emux_ioctl_seq_oss,
+	.load_patch = snd_emux_load_patch_seq_oss,
+	.reset = snd_emux_reset_seq_oss,
 };
 
 
diff -Nru a/sound/synth/emux/emux_proc.c b/sound/synth/emux/emux_proc.c
--- a/sound/synth/emux/emux_proc.c	Mon Oct  7 11:25:39 2002
+++ b/sound/synth/emux/emux_proc.c	Mon Oct  7 11:25:39 2002
@@ -18,7 +18,6 @@
  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  */
 
-#define __NO_VERSION__
 #include <sound/driver.h>
 #include <linux/wait.h>
 #include <linux/sched.h>
diff -Nru a/sound/synth/emux/emux_seq.c b/sound/synth/emux/emux_seq.c
--- a/sound/synth/emux/emux_seq.c	Mon Oct  7 11:25:33 2002
+++ b/sound/synth/emux/emux_seq.c	Mon Oct  7 11:25:33 2002
@@ -19,7 +19,6 @@
  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  */
 
-#define __NO_VERSION__
 #include "emux_voice.h"
 #include <linux/slab.h>
 
diff -Nru a/sound/synth/emux/emux_synth.c b/sound/synth/emux/emux_synth.c
--- a/sound/synth/emux/emux_synth.c	Mon Oct  7 11:25:35 2002
+++ b/sound/synth/emux/emux_synth.c	Mon Oct  7 11:25:35 2002
@@ -22,7 +22,6 @@
  *
  */
 
-#define __NO_VERSION__
 #include "emux_voice.h"
 #include <sound/asoundef.h>
 
diff -Nru a/sound/synth/emux/soundfont.c b/sound/synth/emux/soundfont.c
--- a/sound/synth/emux/soundfont.c	Mon Oct  7 11:25:36 2002
+++ b/sound/synth/emux/soundfont.c	Mon Oct  7 11:25:36 2002
@@ -25,7 +25,6 @@
  * of doing things so that the old sfxload utility can be used.
  * Everything may change when there is an alsa way of doing things.
  */
-#define __NO_VERSION__
 #include <sound/driver.h>
 #include <asm/uaccess.h>
 #include <linux/slab.h>
diff -Nru a/sound/usb/Config.help b/sound/usb/Config.help
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/sound/usb/Config.help	Mon Oct  7 11:26:06 2002
@@ -0,0 +1,4 @@
+CONFIG_SND_USB_AUDIO
+  Say 'Y' or 'M' to include support for USB audio devices.
+  To support USB MIDI devices, you need to enable ALSA sequencer support
+  (CONFIG_SND_SEQUENCER).
diff -Nru a/sound/usb/Config.in b/sound/usb/Config.in
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/sound/usb/Config.in	Mon Oct  7 11:25:40 2002
@@ -0,0 +1,8 @@
+# ALSA USB drivers
+
+mainmenu_option next_comment
+comment 'ALSA USB devices'
+
+dep_tristate 'USB Audio/MIDI driver' CONFIG_SND_USB_AUDIO $CONFIG_SND
+
+endmenu
diff -Nru a/sound/usb/Makefile b/sound/usb/Makefile
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/sound/usb/Makefile	Mon Oct  7 11:25:40 2002
@@ -0,0 +1,16 @@
+#
+# Makefile for ALSA
+#
+
+snd-usb-audio-objs := usbaudio.o usbmixer.o
+ifeq ($(subst m,y,$(CONFIG_SND_SEQUENCER)),y)
+snd-usb-midi-objs := usbmidi.o
+endif
+
+# Toplevel Module Dependency
+obj-$(CONFIG_SND_USB_AUDIO) += snd-usb-audio.o
+ifeq ($(subst m,y,$(CONFIG_SND_SEQUENCER)),y)
+obj-$(CONFIG_SND_USB_AUDIO) += snd-usb-midi.o
+endif
+
+include $(TOPDIR)/Rules.make
diff -Nru a/sound/usb/usbaudio.c b/sound/usb/usbaudio.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/sound/usb/usbaudio.c	Mon Oct  7 11:26:06 2002
@@ -0,0 +1,2265 @@
+/*
+ *   (Tentative) USB Audio Driver for ALSA
+ *
+ *   Main and PCM part
+ *
+ *   Copyright (c) 2002 by Takashi Iwai <tiwai@suse.de>
+ *
+ *   Many codes borrowed from audio.c by 
+ *	    Alan Cox (alan@lxorguk.ukuu.org.uk)
+ *	    Thomas Sailer (sailer@ife.ee.ethz.ch)
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+
+
+#include <sound/driver.h>
+#include <linux/bitops.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/usb.h>
+#include <sound/core.h>
+#include <sound/info.h>
+#include <sound/pcm.h>
+#include <sound/seq_device.h>
+#define SNDRV_GET_ID
+#include <sound/initval.h>
+
+#include "usbaudio.h"
+
+
+MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>");
+MODULE_DESCRIPTION("USB Audio");
+MODULE_LICENSE("GPL");
+MODULE_CLASSES("{sound}");
+MODULE_DEVICES("{{Generic,USB Audio}}");
+
+
+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 */
+static int snd_vid[SNDRV_CARDS] = { [0 ... (SNDRV_CARDS-1)] = -1 }; /* Vendor ID for this card */
+static int snd_pid[SNDRV_CARDS] = { [0 ... (SNDRV_CARDS-1)] = -1 }; /* Product ID for this card */
+
+MODULE_PARM(snd_index, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_index, "Index value for the USB audio adapter.");
+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 the USB audio adapter.");
+MODULE_PARM_SYNTAX(snd_id, SNDRV_ID_DESC);
+MODULE_PARM(snd_enable, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_enable, "Enable USB audio adapter.");
+MODULE_PARM_SYNTAX(snd_enable, SNDRV_ENABLE_DESC);
+MODULE_PARM(snd_vid, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_vid, "Vendor ID for the USB audio device.");
+MODULE_PARM_SYNTAX(snd_vid, SNDRV_ENABLED ",allows:{{-1,0xffff}},base:16");
+MODULE_PARM(snd_pid, "1-" __MODULE_STRING(SNDRV_CARDS) "i");
+MODULE_PARM_DESC(snd_pid, "Product ID for the USB audio device.");
+MODULE_PARM_SYNTAX(snd_pid, SNDRV_ENABLED ",allows:{{-1,0xffff}},base:16");
+
+
+/*
+ * for using ASYNC unlink mode, define the following.
+ * this will make the driver quicker response for request to STOP-trigger,
+ * but it may cause oops by some unknown reason (bug of usb driver?),
+ * so turning off might be sure.
+ */
+/* #define SND_USE_ASYNC_UNLINK */
+
+#ifdef SND_USB_ASYNC_UNLINK
+#define UNLINK_FLAGS	USB_ASYNC_UNLINK
+#else
+#define UNLINK_FLAGS	0
+#endif
+
+
+/*
+ *
+ */
+
+#define NRPACKS		4	/* 4ms per urb */
+#define MAX_URBS	5	/* max. 20ms long packets */
+#define SYNC_URBS	2	/* always two urbs for sync */
+#define MIN_PACKS_URB	1	/* minimum 1 packet per urb */
+
+typedef struct snd_usb_substream snd_usb_substream_t;
+typedef struct snd_usb_stream snd_usb_stream_t;
+typedef struct snd_urb_ctx snd_urb_ctx_t;
+
+struct audioformat {
+	struct list_head list;
+	snd_pcm_format_t format;	/* format type */
+	int channels;			/* # channels */
+	int iface;			/* interface number */
+	unsigned char altsetting;	/* corresponding alternate setting */
+	unsigned char altset_idx;	/* array index of altenate setting */
+	unsigned char attributes;	/* corresponding attributes of cs endpoint */
+	unsigned char endpoint;		/* endpoint */
+	unsigned char ep_attr;		/* endpoint attributes */
+	unsigned int rates;		/* rate bitmasks */
+	int rate_min, rate_max;		/* min/max rates */
+	int nr_rates;			/* number of rate table entries */
+	int *rate_table;		/* rate table */
+};
+
+struct snd_urb_ctx {
+	struct urb *urb;
+	snd_usb_substream_t *subs;
+	int index;	/* index for urb array */
+	int packets;	/* number of packets per urb */
+	int transfer;	/* transferred size */
+	char *buf;	/* buffer for capture */
+};
+
+struct snd_urb_ops {
+	int (*prepare)(snd_usb_substream_t *subs, snd_pcm_runtime_t *runtime, struct urb *u);
+	int (*retire)(snd_usb_substream_t *subs, snd_pcm_runtime_t *runtime, struct urb *u);
+	int (*prepare_sync)(snd_usb_substream_t *subs, snd_pcm_runtime_t *runtime, struct urb *u);
+	int (*retire_sync)(snd_usb_substream_t *subs, snd_pcm_runtime_t *runtime, struct urb *u);
+};
+
+struct snd_usb_substream {
+	snd_usb_stream_t *stream;
+	struct usb_device *dev;
+	snd_pcm_substream_t *pcm_substream;
+	int direction;	/* playback or capture */
+	int interface;	/* current interface */
+	int endpoint;	/* assigned endpoint */
+	unsigned int format;     /* USB data format */
+	unsigned int datapipe;   /* the data i/o pipe */
+	unsigned int syncpipe;   /* 1 - async out or adaptive in */
+	unsigned int syncinterval;  /* P for adaptive mode, 0 otherwise */
+	unsigned int freqn;      /* nominal sampling rate in USB format, i.e. fs/1000 in Q10.14 */
+	unsigned int freqm;      /* momentary sampling rate in USB format, i.e. fs/1000 in Q10.14 */
+	unsigned int freqmax;    /* maximum sampling rate, used for buffer management */
+	unsigned int phase;      /* phase accumulator */
+	unsigned int maxpacksize;	/* max packet size in bytes */
+	unsigned int maxframesize;	/* max packet size in frames */
+	unsigned int curpacksize;	/* current packet size in bytes (for capture) */
+	unsigned int curframesize;	/* current packet size in frames (for capture) */
+	unsigned int fill_max: 1;	/* fill max packet size always */
+
+	unsigned int running: 1;	/* running status */
+
+	int hwptr;			/* free frame position in the buffer (only for playback) */
+	int hwptr_done;			/* processed frame position in the buffer */
+	int transfer_sched;		/* scheduled frames since last period (for playback) */
+	int transfer_done;		/* processed frames since last period update */
+	unsigned long active_mask;	/* bitmask of active urbs */
+	unsigned long unlink_mask;	/* bitmask of unlinked urbs */
+
+	int nurbs;			/* # urbs */
+	snd_urb_ctx_t dataurb[MAX_URBS];	/* data urb table */
+	snd_urb_ctx_t syncurb[SYNC_URBS];	/* sync urb table */
+	char syncbuf[SYNC_URBS * NRPACKS * 3]; /* sync buffer; it's so small - let's get static */
+	char *tmpbuf;			/* temporary buffer for playback */
+
+	u64 formats;			/* format bitmasks (all or'ed) */
+	int num_formats;		/* number of supported audio formats (list) */
+	struct list_head fmt_list;	/* format list */
+	spinlock_t lock;
+
+	struct snd_urb_ops ops;		/* callbacks (must be filled at init) */
+};
+
+
+struct snd_usb_stream {
+	snd_usb_audio_t *chip;
+	snd_pcm_t *pcm;
+	int pcm_index;
+	snd_usb_substream_t substream[2];
+	struct list_head list;
+	snd_info_entry_t *proc_entry;
+};
+
+#define chip_t snd_usb_stream_t
+
+
+/*
+ * we keep the snd_usb_audio_t instances by ourselves for merging
+ * the all interfaces on the same card as one sound device.
+ */
+
+static DECLARE_MUTEX(register_mutex);
+static snd_usb_audio_t *usb_chip[SNDRV_CARDS];
+
+
+/*
+ * convert a sampling rate into USB format (fs/1000 in Q10.14)
+ * this will overflow at approx 2MSPS
+ */
+inline static unsigned get_usb_rate(unsigned int rate)
+{
+	return ((rate << 11) + 62) / 125;
+}
+
+
+/*
+ * prepare urb for capture sync pipe
+ *
+ * fill the length and offset of each urb descriptor.
+ * the fixed 10.14 frequency is passed through the pipe.
+ */
+static int prepare_capture_sync_urb(snd_usb_substream_t *subs,
+				    snd_pcm_runtime_t *runtime,
+				    struct urb *urb)
+{
+	unsigned char *cp = urb->transfer_buffer;
+	snd_urb_ctx_t *ctx = (snd_urb_ctx_t *)urb->context;
+	int i, offs;
+
+	urb->number_of_packets = ctx->packets;
+	urb->dev = ctx->subs->dev; /* we need to set this at each time */
+	for (i = offs = 0; i < urb->number_of_packets; i++, offs += 3, cp += 3) {
+		urb->iso_frame_desc[i].length = 3;
+		urb->iso_frame_desc[i].offset = offs;
+		cp[0] = subs->freqn;
+		cp[1] = subs->freqn >> 8;
+		cp[2] = subs->freqn >> 16;
+	}
+	urb->interval = 1;
+	return 0;
+}
+
+/*
+ * process after capture sync complete
+ * - nothing to do
+ */
+static int retire_capture_sync_urb(snd_usb_substream_t *subs,
+				   snd_pcm_runtime_t *runtime,
+				   struct urb *urb)
+{
+	return 0;
+}
+
+/*
+ * prepare urb for capture data pipe
+ *
+ * fill the offset and length of each descriptor.
+ *
+ * we use a temporary buffer to write the captured data.
+ * since the length of written data is determined by host, we cannot
+ * write onto the pcm buffer directly...  the data is thus copied
+ * later at complete callback to the global buffer.
+ */
+static int prepare_capture_urb(snd_usb_substream_t *subs,
+			       snd_pcm_runtime_t *runtime,
+			       struct urb *urb)
+{
+	int i, offs;
+	unsigned long flags;
+	snd_urb_ctx_t *ctx = (snd_urb_ctx_t *)urb->context;
+
+	offs = 0;
+	urb->dev = ctx->subs->dev; /* we need to set this at each time */
+	urb->number_of_packets = 0;
+	spin_lock_irqsave(&subs->lock, flags);
+	for (i = 0; i < ctx->packets; i++) {
+		urb->iso_frame_desc[i].offset = offs;
+		urb->iso_frame_desc[i].length = subs->curpacksize;
+		offs += subs->curpacksize;
+		urb->number_of_packets++;
+		subs->transfer_sched += subs->curframesize;
+		if (subs->transfer_sched >= runtime->period_size) {
+			subs->transfer_sched -= runtime->period_size;
+			break;
+		}
+	}
+	spin_unlock_irqrestore(&subs->lock, flags);
+	urb->transfer_buffer = ctx->buf;
+	urb->transfer_buffer_length = offs;
+	urb->interval = 1;
+#if 0 // for check
+	if (! urb->bandwidth) {
+		int bustime;
+		bustime = usb_check_bandwidth(urb->dev, urb);
+		if (bustime < 0) 
+			return bustime;
+		printk("urb %d: bandwidth = %d (packets = %d)\n", ctx->index, bustime, urb->number_of_packets);
+		usb_claim_bandwidth(urb->dev, urb, bustime, 1);
+	}
+#endif // for check
+	return 0;
+}
+
+/*
+ * process after capture complete
+ *
+ * copy the data from each desctiptor to the pcm buffer, and
+ * update the current position.
+ */
+static int retire_capture_urb(snd_usb_substream_t *subs,
+			      snd_pcm_runtime_t *runtime,
+			      struct urb *urb)
+{
+	unsigned long flags;
+	unsigned char *cp;
+	int stride, i, len, oldptr;
+
+	stride = runtime->frame_bits >> 3;
+
+	for (i = 0; i < urb->number_of_packets; i++) {
+		cp = (unsigned char *)urb->transfer_buffer + urb->iso_frame_desc[i].offset;
+		if (urb->iso_frame_desc[i].status) {
+			snd_printd(KERN_ERR "frame %d active: %d\n", i, urb->iso_frame_desc[i].status);
+			// continue;
+		}
+		len = urb->iso_frame_desc[i].actual_length / stride;
+		if (! len)
+			continue;
+		/* update the current pointer */
+		spin_lock_irqsave(&subs->lock, flags);
+		oldptr = subs->hwptr_done;
+		subs->hwptr_done += len;
+		if (subs->hwptr_done >= runtime->buffer_size)
+			subs->hwptr_done -= runtime->buffer_size;
+		subs->transfer_done += len;
+		spin_unlock_irqrestore(&subs->lock, flags);
+		/* copy a data chunk */
+		if (oldptr + len > runtime->buffer_size) {
+			int cnt = runtime->buffer_size - oldptr;
+			int blen = cnt * stride;
+			memcpy(runtime->dma_area + oldptr * stride, cp, blen);
+			memcpy(runtime->dma_area, cp + blen, len * stride - blen);
+		} else {
+			memcpy(runtime->dma_area + oldptr * stride, cp, len * stride);
+		}
+		/* update the pointer, call callback if necessary */
+		spin_lock_irqsave(&subs->lock, flags);
+		if (subs->transfer_done >= runtime->period_size) {
+			subs->transfer_done -= runtime->period_size;
+			spin_unlock_irqrestore(&subs->lock, flags);
+			snd_pcm_period_elapsed(subs->pcm_substream);
+		} else
+			spin_unlock_irqrestore(&subs->lock, flags);
+	}
+	return 0;
+}
+
+
+/*
+ * prepare urb for playback sync pipe
+ *
+ * set up the offset and length to receive the current frequency.
+ */
+
+static int prepare_playback_sync_urb(snd_usb_substream_t *subs,
+				     snd_pcm_runtime_t *runtime,
+				     struct urb *urb)
+{
+	int i, offs;
+	snd_urb_ctx_t *ctx = (snd_urb_ctx_t *)urb->context;
+
+	urb->number_of_packets = ctx->packets;
+	urb->dev = ctx->subs->dev; /* we need to set this at each time */
+	for (i = offs = 0; i < urb->number_of_packets; i++, offs += 3) {
+		urb->iso_frame_desc[i].length = 3;
+		urb->iso_frame_desc[i].offset = offs;
+	}
+	return 0;
+}
+
+/*
+ * process after playback sync complete
+ *
+ * retrieve the current 10.14 frequency from pipe, and set it.
+ * the value is referred in prepare_playback_urb().
+ */
+static int retire_playback_sync_urb(snd_usb_substream_t *subs,
+				    snd_pcm_runtime_t *runtime,
+				    struct urb *urb)
+{
+	unsigned int f, i, found;
+	unsigned char *cp = urb->transfer_buffer;
+	unsigned long flags;
+
+	found = 0;
+	for (i = 0; i < urb->number_of_packets; i++, cp += 3) {
+		if (urb->iso_frame_desc[i].status ||
+		    urb->iso_frame_desc[i].actual_length < 3)
+			continue;
+		f = combine_triple(cp);
+		if (f < subs->freqn - (subs->freqn>>3) || f > subs->freqmax) {
+			snd_printd(KERN_WARNING "requested frequency %u (nominal %u) out of range!\n", f, subs->freqn);
+			continue;
+		}
+		found = f;
+	}
+	if (found) {
+		spin_lock_irqsave(&subs->lock, flags);
+		subs->freqm = found;
+		spin_unlock_irqrestore(&subs->lock, flags);
+	}
+
+	return 0;
+}
+
+/*
+ * prepare urb for playback data pipe
+ *
+ * we copy the data directly from the pcm buffer.
+ * the current position to be copied is held in hwptr field.
+ * since a urb can handle only a single linear buffer, if the total
+ * transferred area overflows the buffer boundary, we cannot send
+ * it directly from the buffer.  thus the data is once copied to
+ * a temporary buffer and urb points to that.
+ */
+static int prepare_playback_urb(snd_usb_substream_t *subs,
+				snd_pcm_runtime_t *runtime,
+				struct urb *urb)
+{
+	int i, stride, offs;
+	int counts;
+	unsigned long flags;
+	snd_urb_ctx_t *ctx = (snd_urb_ctx_t *)urb->context;
+
+	stride = runtime->frame_bits >> 3;
+
+	offs = 0;
+	urb->dev = ctx->subs->dev; /* we need to set this at each time */
+	urb->number_of_packets = 0;
+	spin_lock_irqsave(&subs->lock, flags);
+	for (i = 0; i < ctx->packets; i++) {
+		/* calculate the size of a packet */
+		if (subs->fill_max)
+			counts = subs->maxframesize; /* fixed */
+		else {
+			subs->phase = (subs->phase & 0x3fff) + subs->freqm;
+			counts = subs->phase >> 14;
+			if (counts > subs->maxframesize)
+				counts = subs->maxframesize;
+		}
+		/* set up descriptor */
+		urb->iso_frame_desc[i].offset = offs * stride;
+		urb->iso_frame_desc[i].length = counts * stride;
+		offs += counts;
+		urb->number_of_packets++;
+		subs->transfer_sched += counts;
+		if (subs->transfer_sched >= runtime->period_size) {
+			subs->transfer_sched -= runtime->period_size;
+			break;
+ 		}
+	}
+	if (subs->hwptr + offs > runtime->buffer_size) {
+		/* err, the transferred area goes over buffer boundary.
+		 * copy the data to the temp buffer.
+		 */
+		int len;
+		len = runtime->buffer_size - subs->hwptr;
+		urb->transfer_buffer = subs->tmpbuf;
+		memcpy(subs->tmpbuf, runtime->dma_area + subs->hwptr * stride, len * stride);
+		memcpy(subs->tmpbuf + len * stride, runtime->dma_area, (offs - len) * stride);
+		subs->hwptr += offs;
+		subs->hwptr -= runtime->buffer_size;
+	} else {
+		/* set the buffer pointer */
+		urb->transfer_buffer = runtime->dma_area + subs->hwptr * stride;
+		subs->hwptr += offs;
+	}
+	spin_unlock_irqrestore(&subs->lock, flags);
+	urb->transfer_buffer_length = offs * stride;
+	ctx->transfer = offs;
+
+	return 0;
+}
+
+/*
+ * process after playback data complete
+ *
+ * update the current position and call callback if a period is processed.
+ */
+static int retire_playback_urb(snd_usb_substream_t *subs,
+			       snd_pcm_runtime_t *runtime,
+			       struct urb *urb)
+{
+	unsigned long flags;
+	snd_urb_ctx_t *ctx = (snd_urb_ctx_t *)urb->context;
+
+	spin_lock_irqsave(&subs->lock, flags);
+	subs->transfer_done += ctx->transfer;
+	subs->hwptr_done += ctx->transfer;
+	ctx->transfer = 0;
+	if (subs->hwptr_done >= runtime->buffer_size)
+		subs->hwptr_done -= runtime->buffer_size;
+	if (subs->transfer_done >= runtime->period_size) {
+		subs->transfer_done -= runtime->period_size;
+		spin_unlock_irqrestore(&subs->lock, flags);
+		snd_pcm_period_elapsed(subs->pcm_substream);
+	} else
+		spin_unlock_irqrestore(&subs->lock, flags);
+	return 0;
+}
+
+
+/*
+ */
+static struct snd_urb_ops audio_urb_ops[2] = {
+	{
+		.prepare =	prepare_playback_urb,
+		.retire =	retire_playback_urb,
+		.prepare_sync =	prepare_playback_sync_urb,
+		.retire_sync =	retire_playback_sync_urb,
+	},
+	{
+		.prepare =	prepare_capture_urb,
+		.retire =	retire_capture_urb,
+		.prepare_sync =	prepare_capture_sync_urb,
+		.retire_sync =	retire_capture_sync_urb,
+	},
+};
+
+/*
+ * complete callback from data urb
+ */
+static void snd_complete_urb(struct urb *urb)
+{
+	snd_urb_ctx_t *ctx = (snd_urb_ctx_t *)urb->context;
+	snd_usb_substream_t *subs = ctx->subs;
+	snd_pcm_substream_t *substream = ctx->subs->pcm_substream;
+	int err;
+
+	clear_bit(ctx->index, &subs->active_mask);
+	if (subs->running && subs->ops.retire(subs, substream->runtime, urb))
+		return;
+	if (! subs->running) /* can be stopped during retire callback */
+		return;
+	if ((err = subs->ops.prepare(subs, substream->runtime, urb) < 0) ||
+	    (err = usb_submit_urb(urb, GFP_ATOMIC)) < 0) {
+		snd_printd(KERN_ERR "cannot submit urb (err = %d)\n", err);
+		snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
+		return;
+	}
+	set_bit(ctx->index, &subs->active_mask);
+}
+
+
+/*
+ * complete callback from sync urb
+ */
+static void snd_complete_sync_urb(struct urb *urb)
+{
+	snd_urb_ctx_t *ctx = (snd_urb_ctx_t *)urb->context;
+	snd_usb_substream_t *subs = ctx->subs;
+	snd_pcm_substream_t *substream = ctx->subs->pcm_substream;
+	int err;
+
+	clear_bit(ctx->index + 16, &subs->active_mask);
+	if (subs->running && subs->ops.retire_sync(subs, substream->runtime, urb))
+		return;
+	if (! subs->running) /* can be stopped during retire callback */
+		return;
+	if ((err = subs->ops.prepare_sync(subs, substream->runtime, urb))  < 0 ||
+	    (err = usb_submit_urb(urb, GFP_ATOMIC)) < 0) {
+		snd_printd(KERN_ERR "cannot submit sync urb (err = %d)\n", err);
+		snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
+		return;
+	}
+	set_bit(ctx->index + 16, &subs->active_mask);
+}
+
+
+/*
+ * unlink active urbs.
+ * return the number of active urbs.
+ */
+static int deactivate_urbs(snd_usb_substream_t *subs)
+{
+	int i, alive;
+
+	subs->running = 0;
+
+#ifndef SND_USB_ASYNC_UNLINK
+	if (in_interrupt())
+		return 0;
+#endif
+	alive = 0;
+	for (i = 0; i < subs->nurbs; i++) {
+		if (test_bit(i, &subs->active_mask)) {
+			alive++;
+			if (! test_and_set_bit(i, &subs->unlink_mask))
+				usb_unlink_urb(subs->dataurb[i].urb);
+		}
+	}
+	if (subs->syncpipe) {
+		for (i = 0; i < SYNC_URBS; i++) {
+			if (test_bit(i+16, &subs->active_mask)) {
+				alive++;
+ 				if (! test_and_set_bit(i+16, &subs->unlink_mask))
+					usb_unlink_urb(subs->syncurb[i].urb);
+			}
+		}
+	}
+#ifdef SND_USB_ASYNC_UNLINK
+	return alive;
+#else
+	return 0;
+#endif
+}
+
+
+/*
+ * set up and start data/sync urbs
+ */
+static int start_urbs(snd_usb_substream_t *subs, snd_pcm_runtime_t *runtime)
+{
+	int i, err;
+
+	for (i = 0; i < subs->nurbs; i++) {
+		snd_assert(subs->dataurb[i].urb, return -EINVAL);
+		if (subs->ops.prepare(subs, runtime, subs->dataurb[i].urb) < 0) {
+			snd_printk(KERN_ERR "cannot prepare datapipe for urb %d\n", i);
+			goto __error;
+		}
+	}
+	if (subs->syncpipe) {
+		for (i = 0; i < SYNC_URBS; i++) {
+			snd_assert(subs->syncurb[i].urb, return -EINVAL);
+			if (subs->ops.prepare_sync(subs, runtime, subs->syncurb[i].urb) < 0) {
+				snd_printk(KERN_ERR "cannot prepare syncpipe for urb %d\n", i);
+				goto __error;
+			}
+		}
+	}
+
+	subs->running = 1;
+	for (i = 0; i < subs->nurbs; i++) {
+		if ((err = usb_submit_urb(subs->dataurb[i].urb, GFP_KERNEL)) < 0) {
+			snd_printk(KERN_ERR "cannot submit datapipe for urb %d, err = %d\n", i, err);
+			goto __error;
+		}
+		set_bit(i, &subs->active_mask);
+	}
+	if (subs->syncpipe) {
+		for (i = 0; i < SYNC_URBS; i++) {
+			if ((err = usb_submit_urb(subs->syncurb[i].urb, GFP_KERNEL)) < 0) {
+				snd_printk(KERN_ERR "cannot submit syncpipe for urb %d, err = %d\n", i, err);
+				goto __error;
+			}
+			set_bit(i + 16, &subs->active_mask);
+		}
+	}
+	return 0;
+
+ __error:
+	// snd_pcm_stop(subs->pcm_substream, SNDRV_PCM_STATE_XRUN);
+	deactivate_urbs(subs);
+	return -EPIPE;
+}
+
+
+/* 
+ *  wait until all urbs are processed.
+ */
+static int wait_clear_urbs(snd_usb_substream_t *subs)
+{
+	int timeout = HZ;
+	int i, alive;
+
+	do {
+		alive = 0;
+		for (i = 0; i < subs->nurbs; i++) {
+			if (test_bit(i, &subs->active_mask))
+				alive++;
+		}
+		if (subs->syncpipe) {
+			for (i = 0; i < SYNC_URBS; i++) {
+				if (test_bit(i + 16, &subs->active_mask))
+					alive++;
+			}
+		}
+		if (! alive)
+			break;
+		set_current_state(TASK_UNINTERRUPTIBLE);
+		schedule_timeout(1);
+		set_current_state(TASK_RUNNING);
+	} while (--timeout > 0);
+	if (alive)
+		snd_printk(KERN_ERR "timeout: still %d active urbs..\n", alive);
+	return 0;
+}
+
+
+/*
+ * return the current pcm pointer.  just return the hwptr_done value.
+ */
+static snd_pcm_uframes_t snd_usb_pcm_pointer(snd_pcm_substream_t *substream)
+{
+	snd_usb_substream_t *subs = (snd_usb_substream_t *)substream->runtime->private_data;
+	return subs->hwptr_done;
+}
+
+
+/*
+ * start/stop substream
+ */
+static int snd_usb_pcm_trigger(snd_pcm_substream_t *substream, int cmd)
+{
+	snd_usb_substream_t *subs = (snd_usb_substream_t *)substream->runtime->private_data;
+	int err;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		err = start_urbs(subs, substream->runtime);
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+		err = deactivate_urbs(subs);
+		break;
+	default:
+		err = -EINVAL;
+		break;
+	}
+	return err < 0 ? err : 0;
+}
+
+
+/*
+ * release a urb data
+ */
+static void release_urb_ctx(snd_urb_ctx_t *u)
+{
+	if (u->urb) {
+		usb_free_urb(u->urb);
+		u->urb = 0;
+	}
+	if (u->buf) {
+		kfree(u->buf);
+		u->buf = 0;
+	}
+}
+
+/*
+ * release a substream
+ */
+static void release_substream_urbs(snd_usb_substream_t *subs)
+{
+	int i;
+
+	/* stop urbs (to be sure) */
+	if (deactivate_urbs(subs) > 0)
+		wait_clear_urbs(subs);
+
+	for (i = 0; i < MAX_URBS; i++)
+		release_urb_ctx(&subs->dataurb[i]);
+	for (i = 0; i < SYNC_URBS; i++)
+		release_urb_ctx(&subs->syncurb[i]);
+	if (subs->tmpbuf) {
+		kfree(subs->tmpbuf);
+		subs->tmpbuf = 0;
+	}
+	subs->nurbs = 0;
+}
+
+/*
+ * initialize a substream for plaback/capture
+ */
+static int init_substream_urbs(snd_usb_substream_t *subs, snd_pcm_runtime_t *runtime)
+{
+	int maxsize, n, i;
+	int is_playback = subs->direction == SNDRV_PCM_STREAM_PLAYBACK;
+	int npacks[MAX_URBS], total_packs;
+
+	/* calculate the frequency in 10.14 format */
+	subs->freqn = subs->freqm = get_usb_rate(runtime->rate);
+	subs->freqmax = subs->freqn + (subs->freqn >> 2); /* max. allowed frequency */
+	subs->phase = 0;
+
+	/* reset the pointer */
+	subs->hwptr = 0;
+	subs->hwptr_done = 0;
+	subs->transfer_sched = 0;
+	subs->transfer_done = 0;
+	subs->active_mask = 0;
+	subs->unlink_mask = 0;
+
+	/* calculate the max. size of packet */
+	maxsize = ((subs->freqmax + 0x3fff) * (runtime->frame_bits >> 3)) >> 14;
+	if (subs->maxpacksize && maxsize > subs->maxpacksize) {
+		//snd_printd(KERN_DEBUG "maxsize %d is greater than defined size %d\n",
+		//	   maxsize, subs->maxpacksize);
+		maxsize = subs->maxpacksize;
+	}
+
+	if (subs->fill_max)
+		subs->curpacksize = subs->maxpacksize;
+	else
+		subs->curpacksize = maxsize;
+	subs->curframesize = bytes_to_frames(runtime, subs->curpacksize);
+
+	/* allocate a temporary buffer for playback */
+	if (is_playback) {
+		subs->tmpbuf = kmalloc(maxsize * NRPACKS, GFP_KERNEL);
+		if (! subs->tmpbuf) {
+			snd_printk(KERN_ERR "cannot malloc tmpbuf\n");
+			return -ENOMEM;
+		}
+	}
+
+	/* decide how many packets to be used */
+	total_packs = (frames_to_bytes(runtime, runtime->period_size) + maxsize - 1) / maxsize;
+	if (total_packs < 2 * MIN_PACKS_URB)
+		total_packs = 2 * MIN_PACKS_URB;
+	subs->nurbs = (total_packs + NRPACKS - 1) / NRPACKS;
+	if (subs->nurbs > MAX_URBS) {
+		/* too much... */
+		subs->nurbs = MAX_URBS;
+		total_packs = MAX_URBS * NRPACKS;
+	}
+	n = total_packs;
+	for (i = 0; i < subs->nurbs; i++) {
+		npacks[i] = n > NRPACKS ? NRPACKS : n;
+		n -= NRPACKS;
+	}
+	if (subs->nurbs <= 1) {
+		/* too little - we need at least two packets
+		 * to ensure contiguous playback/capture
+		 */
+		subs->nurbs = 2;
+		npacks[0] = (total_packs + 1) / 2;
+		npacks[1] = total_packs - npacks[0];
+	} else if (npacks[subs->nurbs-1] < MIN_PACKS_URB) {
+		/* the last packet is too small.. */
+		if (subs->nurbs > 2) {
+			/* merge to the first one */
+			npacks[0] += npacks[subs->nurbs - 1];
+			subs->nurbs--;
+		} else {
+			/* divide to two */
+			subs->nurbs = 2;
+			npacks[0] = (total_packs + 1) / 2;
+			npacks[1] = total_packs - npacks[0];
+		}
+	}
+
+	/* allocate and initialize data urbs */
+	for (i = 0; i < subs->nurbs; i++) {
+		snd_urb_ctx_t *u = &subs->dataurb[i];
+		u->index = i;
+		u->subs = subs;
+		u->transfer = 0;
+		u->packets = npacks[i];
+		if (! is_playback) {
+			/* allocate a capture buffer per urb */
+			u->buf = kmalloc(maxsize * u->packets, GFP_KERNEL);
+			if (! u->buf) {
+				release_substream_urbs(subs);
+				return -ENOMEM;
+			}
+		}
+		u->urb = usb_alloc_urb(u->packets, GFP_KERNEL);
+		if (! u->urb) {
+			release_substream_urbs(subs);
+			return -ENOMEM;
+		}
+		u->urb->dev = subs->dev;
+		u->urb->pipe = subs->datapipe;
+		u->urb->transfer_flags = USB_ISO_ASAP | UNLINK_FLAGS;
+		u->urb->number_of_packets = u->packets;
+		u->urb->context = u;
+		u->urb->complete = snd_complete_urb;
+	}
+
+	if (subs->syncpipe) {
+		/* allocate and initialize sync urbs */
+		for (i = 0; i < SYNC_URBS; i++) {
+			snd_urb_ctx_t *u = &subs->syncurb[i];
+			u->index = i;
+			u->subs = subs;
+			u->packets = NRPACKS;
+			u->urb = usb_alloc_urb(u->packets, GFP_KERNEL);
+			if (! u->urb) {
+				release_substream_urbs(subs);
+				return -ENOMEM;
+			}
+			u->urb->transfer_buffer = subs->syncbuf + i * NRPACKS * 3;
+			u->urb->transfer_buffer_length = NRPACKS * 3;
+			u->urb->dev = subs->dev;
+			u->urb->pipe = subs->syncpipe;
+			u->urb->transfer_flags = USB_ISO_ASAP | UNLINK_FLAGS;
+			u->urb->number_of_packets = u->packets;
+			u->urb->context = u;
+			u->urb->complete = snd_complete_sync_urb;
+		}
+	}
+	return 0;
+}
+
+
+/*
+ * find a matching audio format
+ */
+static struct audioformat *find_format(snd_usb_substream_t *subs, snd_pcm_runtime_t *runtime)
+{
+	struct list_head *p;
+
+	list_for_each(p, &subs->fmt_list) {
+		struct audioformat *fp;
+		fp = list_entry(p, struct audioformat, list);
+		if (fp->format != runtime->format ||
+		    fp->channels != runtime->channels)
+			continue;
+		if (runtime->rate < fp->rate_min || runtime->rate > fp->rate_max)
+			continue;
+		if (fp->rates & SNDRV_PCM_RATE_CONTINUOUS)
+			return fp;
+		else {
+			int i;
+			for (i = 0; i < fp->nr_rates; i++)
+				if (fp->rate_table[i] == runtime->rate)
+					return fp;
+		}
+	}
+	return NULL;
+}
+
+
+/*
+ * find a matching format and set up the interface
+ */
+static int set_format(snd_usb_substream_t *subs, snd_pcm_runtime_t *runtime)
+{
+	struct usb_device *dev = subs->dev;
+	struct usb_config_descriptor *config = dev->actconfig;
+	struct usb_interface_descriptor *alts;
+	struct usb_interface *iface;	
+	struct audioformat *fmt;
+	unsigned int ep, attr;
+	unsigned char data[3];
+	int is_playback = subs->direction == SNDRV_PCM_STREAM_PLAYBACK;
+	int err;
+
+	fmt = find_format(subs, runtime);
+	if (! fmt) {
+		snd_printd(KERN_DEBUG "cannot set format: format = %s, rate = %d, channels = %d\n",
+			   snd_pcm_format_name(runtime->format), runtime->rate, runtime->channels);
+		return -EINVAL;
+	}
+
+	iface = &config->interface[fmt->iface];
+	alts = &iface->altsetting[fmt->altset_idx];
+	snd_assert(alts->bAlternateSetting == fmt->altsetting, return -EINVAL);
+
+	/* close the old interface */
+	if (subs->interface >= 0 && subs->interface != fmt->iface) {
+		usb_set_interface(subs->dev, subs->interface, 0);
+		subs->interface = -1;
+	}
+
+	/* create a data pipe */
+	ep = alts->endpoint[0].bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
+	if (is_playback)
+		subs->datapipe = usb_sndisocpipe(dev, ep);
+	else
+		subs->datapipe = usb_rcvisocpipe(dev, ep);
+	subs->format = fmt->altset_idx;
+	subs->syncpipe = subs->syncinterval = 0;
+	subs->maxpacksize = alts->endpoint[0].wMaxPacketSize;
+	subs->maxframesize = bytes_to_frames(runtime, subs->maxpacksize);
+	subs->fill_max = 0;
+
+	/* we need a sync pipe in async OUT or adaptive IN mode */
+	attr = alts->endpoint[0].bmAttributes & EP_ATTR_MASK;
+	if ((is_playback && attr == EP_ATTR_ASYNC) ||
+	    (! is_playback && attr == EP_ATTR_ADAPTIVE)) {
+		/* check endpoint */
+		if (alts->bNumEndpoints < 2 ||
+		    alts->endpoint[1].bmAttributes != 0x01 ||
+		    alts->endpoint[1].bSynchAddress != 0) {
+			snd_printk(KERN_ERR "%d:%d:%d : invalid synch pipe\n",
+				   dev->devnum, fmt->iface, fmt->altsetting);
+			return -EINVAL;
+		}
+		ep = alts->endpoint[1].bEndpointAddress;
+		if ((is_playback && ep != (alts->endpoint[0].bSynchAddress | USB_DIR_IN)) ||
+		    (! is_playback && ep != (alts->endpoint[0].bSynchAddress & ~USB_DIR_IN))) {
+			snd_printk(KERN_ERR "%d:%d:%d : invalid synch pipe\n",
+				   dev->devnum, fmt->iface, fmt->altsetting);
+			return -EINVAL;
+		}
+		ep &= USB_ENDPOINT_NUMBER_MASK;
+		if (is_playback)
+			subs->syncpipe = usb_rcvisocpipe(dev, ep);
+		else
+			subs->syncpipe = usb_sndisocpipe(dev, ep);
+		subs->syncinterval = alts->endpoint[1].bRefresh;
+	}
+
+	/* set interface */
+	if (usb_set_interface(dev, fmt->iface, fmt->altset_idx) < 0) {
+		snd_printk(KERN_ERR "%d:%d:%d: usb_set_interface failed\n",
+			   dev->devnum, fmt->iface, fmt->altsetting);
+		return -EIO;
+	}
+	snd_printdd(KERN_INFO "setting usb interface %d:%d\n", fmt->iface, fmt->altset_idx);
+	subs->interface = fmt->iface;
+
+	ep = alts->endpoint[0].bEndpointAddress;
+	/* if endpoint has pitch control, enable it */
+	if (fmt->attributes & EP_CS_ATTR_PITCH_CONTROL) {
+		data[0] = 1;
+		if ((err = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), SET_CUR,
+					   USB_TYPE_CLASS|USB_RECIP_ENDPOINT|USB_DIR_OUT, 
+					   PITCH_CONTROL << 8, ep, data, 1, HZ)) < 0) {
+			snd_printk(KERN_ERR "%d:%d:%d: cannot set enable PITCH\n",
+				   dev->devnum, subs->interface, ep);
+			return err;
+		}
+	}
+	/* if endpoint has sampling rate control, set it */
+	if (fmt->attributes & EP_CS_ATTR_SAMPLE_RATE) {
+		int crate;
+		data[0] = runtime->rate;
+		data[1] = runtime->rate >> 8;
+		data[2] = runtime->rate >> 16;
+		if ((err = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), SET_CUR,
+					   USB_TYPE_CLASS|USB_RECIP_ENDPOINT|USB_DIR_OUT, 
+					   SAMPLING_FREQ_CONTROL << 8, ep, data, 3, HZ)) < 0) {
+			snd_printk(KERN_ERR "%d:%d:%d: cannot set freq %d to ep 0x%x\n",
+				   dev->devnum, subs->interface, fmt->altsetting, runtime->rate, ep);
+			return err;
+		}
+		if ((err = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), GET_CUR,
+					   USB_TYPE_CLASS|USB_RECIP_ENDPOINT|USB_DIR_IN,
+					   SAMPLING_FREQ_CONTROL << 8, ep, data, 3, HZ)) < 0) {
+			snd_printk(KERN_ERR "%d:%d:%d: cannot get freq at ep 0x%x\n",
+				   dev->devnum, subs->interface, fmt->altsetting, ep);
+			return err;
+		}
+		crate = data[0] | (data[1] << 8) | (data[2] << 16);
+		if (crate != runtime->rate) {
+			snd_printd(KERN_WARNING "current rate %d is different from the runtime rate %d\n", crate, runtime->rate);
+			// runtime->rate = crate;
+		}
+	}
+	/* always fill max packet size */
+	if (fmt->attributes & EP_CS_ATTR_FILL_MAX)
+		subs->fill_max = 1;
+
+#if 0
+	printk("setting done: format = %d, rate = %d, channels = %d\n",
+	       runtime->format, runtime->rate, runtime->channels);
+	printk("  datapipe = 0x%0x, syncpipe = 0x%0x\n",
+	       subs->datapipe, subs->syncpipe);
+#endif
+
+	return 0;
+}
+
+/*
+ * allocate a buffer.
+ *
+ * so far we use a physically linear buffer although packetize transfer
+ * doesn't need a continuous area.
+ * if sg buffer is supported on the later version of alsa, we'll follow
+ * that.
+ */
+static int snd_usb_hw_params(snd_pcm_substream_t *substream,
+			     snd_pcm_hw_params_t *hw_params)
+{
+	return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
+}
+
+/*
+ * free the buffer
+ */
+static int snd_usb_hw_free(snd_pcm_substream_t *substream)
+{
+	return snd_pcm_lib_free_pages(substream);
+}
+
+/*
+ * prepare callback
+ *
+ * set format and initialize urbs
+ */
+static int snd_usb_pcm_prepare(snd_pcm_substream_t *substream)
+{
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	snd_usb_substream_t *subs = (snd_usb_substream_t *)runtime->private_data;
+	int err;
+
+	release_substream_urbs(subs);
+	if ((err = set_format(subs, runtime)) < 0)
+		return err;
+
+	return init_substream_urbs(subs, runtime);
+}
+
+static snd_pcm_hardware_t snd_usb_playback =
+{
+	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
+				 SNDRV_PCM_INFO_MMAP_VALID),
+	.buffer_bytes_max =	(128*1024),
+	.period_bytes_min =	64,
+	.period_bytes_max =	(128*1024),
+	.periods_min =		2,
+	.periods_max =		1024,
+};
+
+static snd_pcm_hardware_t snd_usb_capture =
+{
+	.info =			(SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
+				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
+				 SNDRV_PCM_INFO_MMAP_VALID),
+	.buffer_bytes_max =	(128*1024),
+	.period_bytes_min =	64,
+	.period_bytes_max =	(128*1024),
+	.periods_min =		2,
+	.periods_max =		1024,
+};
+
+/*
+ * set up the runtime hardware information.
+ */
+
+static void setup_hw_info(snd_pcm_runtime_t *runtime, snd_usb_substream_t *subs)
+{
+	struct list_head *p;
+
+	runtime->hw.formats = subs->formats;
+
+	runtime->hw.rate_min = 0x7fffffff;
+	runtime->hw.rate_max = 0;
+	runtime->hw.channels_min = 256;
+	runtime->hw.channels_max = 0;
+	runtime->hw.rates = 0;
+	/* check min/max rates and channels */
+	list_for_each(p, &subs->fmt_list) {
+		struct audioformat *fp;
+		fp = list_entry(p, struct audioformat, list);
+		runtime->hw.rates |= fp->rates;
+		if (runtime->hw.rate_min > fp->rate_min)
+			runtime->hw.rate_min = fp->rate_min;
+		if (runtime->hw.rate_max < fp->rate_max)
+			runtime->hw.rate_max = fp->rate_max;
+		if (runtime->hw.channels_min > fp->channels)
+			runtime->hw.channels_min = fp->channels;
+		if (runtime->hw.channels_max < fp->channels)
+			runtime->hw.channels_max = fp->channels;
+	}
+
+	/* set the period time minimum 1ms */
+	snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_TIME,
+				     1000 * MIN_PACKS_URB,
+				     /*(NRPACKS * MAX_URBS) * 1000*/ UINT_MAX);
+
+	/* FIXME: we need more constraints to restrict the format type,
+	 * channels and rates according to the audioformat list!
+	 */
+}
+
+static int snd_usb_pcm_open(snd_pcm_substream_t *substream, int direction,
+			    snd_pcm_hardware_t *hw)
+{
+	snd_usb_stream_t *as = snd_pcm_substream_chip(substream);
+	snd_pcm_runtime_t *runtime = substream->runtime;
+	snd_usb_substream_t *subs = &as->substream[direction];
+
+	subs->interface = -1;
+	runtime->hw = *hw;
+	runtime->private_data = subs;
+	subs->pcm_substream = substream;
+	setup_hw_info(runtime, subs);
+
+	return 0;
+}
+
+static int snd_usb_pcm_close(snd_pcm_substream_t *substream, int direction)
+{
+	snd_usb_stream_t *as = snd_pcm_substream_chip(substream);
+	snd_usb_substream_t *subs = &as->substream[direction];
+	release_substream_urbs(subs);
+	if (subs->interface >= 0)
+		usb_set_interface(subs->dev, subs->interface, 0);
+	subs->pcm_substream = NULL;
+	return 0;
+}
+
+static int snd_usb_playback_open(snd_pcm_substream_t *substream)
+{
+	return snd_usb_pcm_open(substream, SNDRV_PCM_STREAM_PLAYBACK, &snd_usb_playback);
+}
+
+static int snd_usb_playback_close(snd_pcm_substream_t *substream)
+{
+	return snd_usb_pcm_close(substream, SNDRV_PCM_STREAM_PLAYBACK);
+}
+
+static int snd_usb_capture_open(snd_pcm_substream_t *substream)
+{
+	return snd_usb_pcm_open(substream, SNDRV_PCM_STREAM_CAPTURE, &snd_usb_capture);
+}
+
+static int snd_usb_capture_close(snd_pcm_substream_t *substream)
+{
+	return snd_usb_pcm_close(substream, SNDRV_PCM_STREAM_CAPTURE);
+}
+
+static snd_pcm_ops_t snd_usb_playback_ops = {
+	.open =		snd_usb_playback_open,
+	.close =	snd_usb_playback_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	snd_usb_hw_params,
+	.hw_free =	snd_usb_hw_free,
+	.prepare =	snd_usb_pcm_prepare,
+	.trigger =	snd_usb_pcm_trigger,
+	.pointer =	snd_usb_pcm_pointer,
+};
+
+static snd_pcm_ops_t snd_usb_capture_ops = {
+	.open =		snd_usb_capture_open,
+	.close =	snd_usb_capture_close,
+	.ioctl =	snd_pcm_lib_ioctl,
+	.hw_params =	snd_usb_hw_params,
+	.hw_free =	snd_usb_hw_free,
+	.prepare =	snd_usb_pcm_prepare,
+	.trigger =	snd_usb_pcm_trigger,
+	.pointer =	snd_usb_pcm_pointer,
+};
+
+
+
+/*
+ * helper functions
+ */
+
+/*
+ * combine bytes and get an integer value
+ */
+unsigned int snd_usb_combine_bytes(unsigned char *bytes, int size)
+{
+	switch (size) {
+	case 1:  return *bytes;
+	case 2:  return combine_word(bytes);
+	case 3:  return combine_triple(bytes);
+	case 4:  return combine_quad(bytes);
+	default: return 0;
+	}
+}
+
+/*
+ * parse descriptor buffer and return the pointer starting the given
+ * descriptor type and interface.
+ * if altsetting is not -1, seek the buffer until the matching alternate
+ * setting is found.
+ */
+void *snd_usb_find_desc(void *descstart, int desclen, void *after, 
+			u8 dtype, int iface, int altsetting)
+{
+	u8 *p, *end, *next;
+	int ifc = -1, as = -1;
+
+	p = descstart;
+	end = p + desclen;
+	for (; p < end;) {
+		if (p[0] < 2)
+			return NULL;
+		next = p + p[0];
+		if (next > end)
+			return NULL;
+		if (p[1] == USB_DT_INTERFACE) {
+			/* minimum length of interface descriptor */
+			if (p[0] < 9)
+				return NULL;
+			ifc = p[2];
+			as = p[3];
+		}
+		if (p[1] == dtype && (!after || (void *)p > after) &&
+		    (iface == -1 || iface == ifc) && (altsetting == -1 || altsetting == as)) {
+			return p;
+		}
+		p = next;
+	}
+	return NULL;
+}
+
+/*
+ * find a class-specified interface descriptor with the given subtype.
+ */
+void *snd_usb_find_csint_desc(void *buffer, int buflen, void *after, u8 dsubtype, int iface, int altsetting)
+{
+	unsigned char *p = after;
+
+	while ((p = snd_usb_find_desc(buffer, buflen, p,
+				      USB_DT_CS_INTERFACE, iface, altsetting)) != NULL) {
+		if (p[0] >= 3 && p[2] == dsubtype)
+			return p;
+	}
+	return NULL;
+}
+
+
+/*
+ * entry point for linux usb interface
+ */
+
+#ifdef OLD_USB
+static void * usb_audio_probe(struct usb_device *dev, unsigned int ifnum,
+			      const struct usb_device_id *id);
+static void usb_audio_disconnect(struct usb_device *dev, void *ptr);
+#else
+static int usb_audio_probe(struct usb_interface *intf,
+			   const struct usb_device_id *id);
+static void usb_audio_disconnect(struct usb_interface *intf);
+#endif
+
+static struct usb_device_id usb_audio_ids [] = {
+#include "usbquirks.h"
+    { .match_flags = (USB_DEVICE_ID_MATCH_INT_CLASS | USB_DEVICE_ID_MATCH_INT_SUBCLASS),
+      .bInterfaceClass = USB_CLASS_AUDIO,
+      .bInterfaceSubClass = USB_SUBCLASS_AUDIO_CONTROL },
+    { }						/* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE (usb, usb_audio_ids);
+
+static struct usb_driver usb_audio_driver = {
+	.name =		"snd-usb-audio",
+	.probe =	usb_audio_probe,
+	.disconnect =	usb_audio_disconnect,
+#ifdef OLD_USB
+	.driver_list =	LIST_HEAD_INIT(usb_audio_driver.driver_list), 
+#endif
+	.id_table =	usb_audio_ids,
+};
+
+
+/*
+ * proc interface for list the supported pcm formats
+ */
+static void proc_dump_substream_formats(snd_usb_substream_t *subs, snd_info_buffer_t *buffer)
+{
+	struct list_head *p;
+	static char *sync_types[4] = {
+		"NONE", "ASYNC", "ADAPTIVE", "SYNC"
+	};
+
+	list_for_each(p, &subs->fmt_list) {
+		struct audioformat *fp;
+		fp = list_entry(p, struct audioformat, list);
+		snd_iprintf(buffer, "  Interface %d\n", fp->iface);
+		snd_iprintf(buffer, "    Altset %d\n", fp->altset_idx);
+		snd_iprintf(buffer, "    Format: %s\n", snd_pcm_format_name(fp->format));
+		snd_iprintf(buffer, "    Channels: %d\n", fp->channels);
+		snd_iprintf(buffer, "    Endpoint: %d %s (%s)\n",
+			    fp->endpoint & USB_ENDPOINT_NUMBER_MASK,
+			    fp->endpoint & USB_DIR_IN ? "IN" : "OUT",
+			    sync_types[(fp->ep_attr & EP_ATTR_MASK) >> 2]);
+		if (fp->rates & SNDRV_PCM_RATE_CONTINUOUS) {
+			snd_iprintf(buffer, "    Rates: %d - %d (continous)\n",
+				    fp->rate_min, fp->rate_max);
+		} else {
+			int i;
+			snd_iprintf(buffer, "    Rates: ");
+			for (i = 0; i < fp->nr_rates; i++) {
+				if (i > 0)
+					snd_iprintf(buffer, ", ");
+				snd_iprintf(buffer, "%d", fp->rate_table[i]);
+			}
+			snd_iprintf(buffer, "\n");
+		}
+	}
+}
+
+static void proc_dump_substream_status(snd_usb_substream_t *subs, snd_info_buffer_t *buffer)
+{
+	if (subs->running) {
+		int i;
+		snd_iprintf(buffer, "  Status: Running\n");
+		snd_iprintf(buffer, "    Interface = %d\n", subs->interface);
+		snd_iprintf(buffer, "    Altset = %d\n", subs->format);
+		snd_iprintf(buffer, "    URBs = %d [ ", subs->nurbs);
+		for (i = 0; i < subs->nurbs; i++)
+			snd_iprintf(buffer, "%d ", subs->dataurb[i].packets);
+		snd_iprintf(buffer, "]\n");
+		snd_iprintf(buffer, "    Packet Size = %d\n", subs->curpacksize);
+		snd_iprintf(buffer, "    Momentary freq = %d,%03d Hz\n",
+			    subs->freqm >> 14,
+			    ((subs->freqm & ((1 << 14) - 1)) * 1000) / ((1 << 14) - 1));
+	} else {
+		snd_iprintf(buffer, "  Status: Stop\n");
+	}
+}
+
+static void proc_pcm_format_read(snd_info_entry_t *entry, snd_info_buffer_t *buffer)
+{
+	snd_usb_stream_t *stream = snd_magic_cast(snd_usb_stream_t, entry->private_data, return);
+	
+	snd_iprintf(buffer, "%s : %s\n", stream->chip->card->longname, stream->pcm->name);
+
+	if (stream->substream[SNDRV_PCM_STREAM_PLAYBACK].num_formats) {
+		snd_iprintf(buffer, "\nPlayback:\n");
+		proc_dump_substream_status(&stream->substream[SNDRV_PCM_STREAM_PLAYBACK], buffer);
+		proc_dump_substream_formats(&stream->substream[SNDRV_PCM_STREAM_PLAYBACK], buffer);
+	}
+	if (stream->substream[SNDRV_PCM_STREAM_CAPTURE].num_formats) {
+		snd_iprintf(buffer, "\nCapture:\n");
+		proc_dump_substream_status(&stream->substream[SNDRV_PCM_STREAM_CAPTURE], buffer);
+		proc_dump_substream_formats(&stream->substream[SNDRV_PCM_STREAM_CAPTURE], buffer);
+	}
+}
+
+static void proc_pcm_format_add(snd_usb_stream_t *stream)
+{
+	snd_info_entry_t *entry;
+	char name[32];
+	snd_card_t *card = stream->chip->card;
+
+	sprintf(name, "stream%d", stream->pcm_index);
+	if ((entry = snd_info_create_card_entry(card, name, card->proc_root)) != NULL) {
+		entry->content = SNDRV_INFO_CONTENT_TEXT;
+		entry->private_data = stream;
+		entry->mode = S_IFREG | S_IRUGO | S_IWUSR;
+		entry->c.text.read_size = 4096;
+		entry->c.text.read = proc_pcm_format_read;
+		if (snd_info_register(entry) < 0) {
+			snd_info_free_entry(entry);
+			entry = NULL;
+		}
+	}
+	stream->proc_entry = entry;
+}
+
+
+/*
+ * intialize the substream instance.
+ */
+
+static void init_substream(snd_usb_stream_t *as, int stream, struct audioformat *fp)
+{
+	snd_usb_substream_t *subs = &as->substream[stream];
+
+	INIT_LIST_HEAD(&subs->fmt_list);
+	spin_lock_init(&subs->lock);
+
+	subs->stream = as;
+	subs->direction = stream;
+	subs->dev = as->chip->dev;
+	subs->ops = audio_urb_ops[stream];
+	snd_pcm_lib_preallocate_pages(as->pcm->streams[stream].substream,
+				      64 * 1024, 128 * 1024, GFP_ATOMIC);
+	snd_pcm_set_ops(as->pcm, stream,
+			stream == SNDRV_PCM_STREAM_PLAYBACK ?
+			&snd_usb_playback_ops : &snd_usb_capture_ops);
+
+	list_add_tail(&fp->list, &subs->fmt_list);
+	subs->formats |= 1ULL << fp->format;
+	subs->endpoint = fp->endpoint;
+	subs->num_formats++;
+}
+
+
+/*
+ * free a substream
+ */
+static void free_substream(snd_usb_substream_t *subs)
+{
+	struct list_head *p, *n;
+
+	if (! subs->num_formats)
+		return; /* not initialized */
+	list_for_each_safe(p, n, &subs->fmt_list) {
+		struct audioformat *fp = list_entry(p, struct audioformat, list);
+		if (fp->rate_table)
+			kfree(fp->rate_table);
+		kfree(fp);
+	}
+}
+
+
+/*
+ * free a usb stream instance
+ */
+static void snd_usb_audio_stream_free(snd_usb_stream_t *stream)
+{
+	if (stream->proc_entry) {
+		snd_info_unregister(stream->proc_entry);
+		stream->proc_entry = NULL;
+	}
+	free_substream(&stream->substream[0]);
+	free_substream(&stream->substream[1]);
+	list_del(&stream->list);
+	snd_magic_kfree(stream);
+}
+
+static void snd_usb_audio_pcm_free(snd_pcm_t *pcm)
+{
+	snd_usb_stream_t *stream = pcm->private_data;
+	if (stream) {
+		stream->pcm = NULL;
+		snd_pcm_lib_preallocate_free_for_all(pcm);
+		snd_usb_audio_stream_free(stream);
+	}
+}
+
+
+/*
+ * add this endpoint to the chip instance.
+ * if a stream with the same endpoint already exists, append to it.
+ * if not, create a new pcm stream.
+ */
+static int add_audio_endpoint(snd_usb_audio_t *chip, int stream, struct audioformat *fp)
+{
+	struct list_head *p;
+	snd_usb_stream_t *as;
+	snd_usb_substream_t *subs;
+	snd_pcm_t *pcm;
+	int err;
+
+	list_for_each(p, &chip->pcm_list) {
+		as = list_entry(p, snd_usb_stream_t, list);
+		subs = &as->substream[stream];
+		if (! subs->endpoint)
+			break;
+		if (subs->endpoint == fp->endpoint) {
+			list_add_tail(&fp->list, &subs->fmt_list);
+			subs->num_formats++;
+			subs->formats |= 1ULL << fp->format;
+			return 0;
+		}
+	}
+	/* look for an empty stream */
+	list_for_each(p, &chip->pcm_list) {
+		as = list_entry(p, snd_usb_stream_t, list);
+		subs = &as->substream[stream];
+		if (subs->endpoint)
+			continue;
+		err = snd_pcm_new_stream(as->pcm, stream, 1);
+		if (err < 0)
+			return err;
+		init_substream(as, stream, fp);
+		return 0;
+	}
+
+	/* create a new pcm */
+	as = snd_magic_kmalloc(snd_usb_stream_t, 0, GFP_KERNEL);
+	if (! as)
+		return -ENOMEM;
+	memset(as, 0, sizeof(*as));
+	as->pcm_index = chip->pcm_devs;
+	as->chip = chip;
+	err = snd_pcm_new(chip->card, "USB Audio", chip->pcm_devs,
+			  stream == SNDRV_PCM_STREAM_PLAYBACK ? 1 : 0,
+			  stream == SNDRV_PCM_STREAM_PLAYBACK ? 0 : 1,
+			  &pcm);
+	if (err < 0) {
+		snd_magic_kfree(as);
+		return err;
+	}
+	as->pcm = pcm;
+	pcm->private_data = as;
+	pcm->private_free = snd_usb_audio_pcm_free;
+	pcm->info_flags = 0;
+	if (chip->pcm_devs > 0)
+		sprintf(pcm->name, "USB Audio #%d", chip->pcm_devs);
+	else
+		strcpy(pcm->name, "USB Audio");
+
+	init_substream(as, stream, fp);
+
+	list_add(&as->list, &chip->pcm_list);
+	chip->pcm_devs++;
+
+	proc_pcm_format_add(as);
+
+	return 0;
+}
+
+
+/*
+ * parse the audio format type descriptor
+ * and returns the corresponding pcm format
+ */
+static int parse_audio_format_type(struct usb_device *dev, int iface_no, int altno,
+				   int format, unsigned char *fmt)
+{
+	int format_type = fmt[3];
+	int pcm_format;
+
+	/* FIXME: needed support for TYPE II and III */
+	if (format_type != USB_FORMAT_TYPE_I) {
+		snd_printd(KERN_INFO "%d:%u:%d : format type %d is not supported yet\n",
+			   dev->devnum, iface_no, altno, format_type);
+		return -1;
+	}
+	
+	/* FIXME: correct endianess and sign? */
+	pcm_format = -1;
+	switch (format) {
+	case USB_AUDIO_FORMAT_PCM:
+		/* check the format byte size */
+		switch (fmt[6]) {
+		case 8:
+			pcm_format = SNDRV_PCM_FORMAT_U8;
+			break;
+		case 16:
+			pcm_format = SNDRV_PCM_FORMAT_S16_LE;
+			break;
+		case 18:
+		case 20:
+			if (fmt[5] == 3)
+				pcm_format = SNDRV_PCM_FORMAT_S24_3LE;
+			else
+				snd_printk(KERN_ERR "%d:%u:%d : non-supported sample bit %d in %d bytes\n",
+					   dev->devnum, iface_no, altno, fmt[6], fmt[5]);
+			break;
+		case 24:
+			if (fmt[5] == 4)
+				/* FIXME: correct?  or S32_LE? */
+				pcm_format = SNDRV_PCM_FORMAT_S24_LE;
+			else if (fmt[5] == 3)
+				pcm_format = SNDRV_PCM_FORMAT_S24_3LE;
+			else
+				snd_printk(KERN_ERR "%d:%u:%d : non-supported sample bit %d in %d bytes\n",
+					   dev->devnum, iface_no, altno, format, fmt[5]);
+			break;
+		case 32:
+			pcm_format = SNDRV_PCM_FORMAT_S32_LE;
+			break;
+		default:
+			snd_printk(KERN_INFO "%d:%u:%d : unsupported sample bitwidth %d in %d bytes\n",
+				   dev->devnum, iface_no, altno, fmt[6], fmt[5]);
+			break;
+		}
+		break;
+	case USB_AUDIO_FORMAT_PCM8:
+		/* Dallas DS4201 workaround */
+		if (dev->descriptor.idVendor == 0x04fa && dev->descriptor.idProduct == 0x4201)
+			pcm_format = SNDRV_PCM_FORMAT_S8;
+		else
+			pcm_format = SNDRV_PCM_FORMAT_U8;
+		break;
+	case USB_AUDIO_FORMAT_IEEE_FLOAT:
+		pcm_format = SNDRV_PCM_FORMAT_FLOAT_LE;
+		break;
+	case USB_AUDIO_FORMAT_ALAW:
+		pcm_format = SNDRV_PCM_FORMAT_A_LAW;
+		break;
+	case USB_AUDIO_FORMAT_MU_LAW:
+		pcm_format = SNDRV_PCM_FORMAT_MU_LAW;
+		break;
+	default:
+		snd_printk(KERN_INFO "%d:%u:%d : unsupported format type %d\n",
+			   dev->devnum, iface_no, altno, format);
+		break;
+	}
+	return pcm_format;
+}
+
+
+static int parse_audio_endpoints(snd_usb_audio_t *chip, unsigned char *buffer, int buflen, int iface_no)
+{
+	struct usb_device *dev;
+	struct usb_config_descriptor *config;
+	struct usb_interface *iface;
+	struct usb_interface_descriptor *alts;
+	int i, altno, err, stream;
+	int channels, nr_rates, pcm_format, format;
+	struct audioformat *fp;
+	unsigned char *fmt, *csep;
+
+	dev = chip->dev;
+	config = dev->actconfig;
+
+	/* parse the interface's altsettings */
+	iface = &config->interface[iface_no];
+	for (i = 0; i < iface->num_altsetting; i++) {
+		alts = &iface->altsetting[i];
+		/* skip invalid one */
+		if (alts->bInterfaceClass != USB_CLASS_AUDIO ||
+		    alts->bInterfaceSubClass != USB_SUBCLASS_AUDIO_STREAMING ||
+		    alts->bNumEndpoints < 1)
+			continue;
+		/* must be isochronous */
+		if ((alts->endpoint[0].bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) !=
+		    USB_ENDPOINT_XFER_ISOC)
+			continue;
+		/* check direction */
+		stream = (alts->endpoint[0].bEndpointAddress & USB_DIR_IN) ?
+			SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK;
+		altno = alts->bAlternateSetting;
+
+		/* get audio formats */
+		fmt = snd_usb_find_csint_desc(buffer, buflen, NULL, AS_GENERAL, iface_no, altno);
+		if (!fmt) {
+			snd_printk(KERN_ERR "%d:%u:%d : AS_GENERAL descriptor not found\n",
+				   dev->devnum, iface_no, altno);
+			continue;
+		}
+
+		if (fmt[0] < 7) {
+			snd_printk(KERN_ERR "%d:%u:%d : invalid AS_GENERAL desc\n", 
+				   dev->devnum, iface_no, altno);
+			continue;
+		}
+
+		format = (fmt[6] << 8) | fmt[5]; /* remember the format value */
+			
+		/* get format type */
+		fmt = snd_usb_find_csint_desc(buffer, buflen, NULL, FORMAT_TYPE, iface_no, altno);
+		if (!fmt) {
+			snd_printk(KERN_ERR "%d:%u:%d : no FORMAT_TYPE desc\n", 
+				   dev->devnum, iface_no, altno);
+			continue;
+		}
+		if (fmt[0] < 8) {
+			snd_printk(KERN_ERR "%d:%u:%d : invalid FORMAT_TYPE desc\n", 
+				   dev->devnum, iface_no, altno);
+			continue;
+		}
+
+		pcm_format = parse_audio_format_type(dev, iface_no, altno, format, fmt);
+		if (pcm_format < 0)
+			continue;
+		
+		channels = fmt[4];
+		if (channels < 1) {
+			snd_printk(KERN_ERR "%d:%u:%d : invalid channels %d\n",
+				   dev->devnum, iface_no, altno, channels);
+			continue;
+		}
+
+		nr_rates = fmt[7];
+		if (fmt[0] < 8 + 3 * (nr_rates ? nr_rates : 2)) {
+			snd_printk(KERN_ERR "%d:%u:%d : invalid FORMAT_TYPE desc\n", 
+				   dev->devnum, iface_no, altno);
+			continue;
+		}
+
+		csep = snd_usb_find_desc(buffer, buflen, NULL, USB_DT_CS_ENDPOINT, iface_no, altno);
+		if (!csep || csep[0] < 7 || csep[2] != EP_GENERAL) {
+			snd_printk(KERN_ERR "%d:%u:%d : no or invalid class specific endpoint descriptor\n", 
+				   dev->devnum, iface_no, altno);
+			continue;
+		}
+
+		fp = kmalloc(sizeof(*fp), GFP_KERNEL);
+		if (! fp) {
+			snd_printk(KERN_ERR "cannot malloc\n");
+			return -ENOMEM;
+		}
+
+		memset(fp, 0, sizeof(*fp));
+		fp->iface = iface_no;
+		fp->altsetting = altno;
+		fp->altset_idx = i;
+		fp->format = pcm_format;
+		fp->endpoint = alts->endpoint[0].bEndpointAddress;
+		fp->ep_attr = alts->endpoint[0].bmAttributes;
+		fp->channels = channels;
+		fp->attributes = csep[3];
+
+		if (nr_rates) {
+			/*
+			 * build the rate table and bitmap flags
+			 */
+			int r, idx, c;
+			/* this table corresponds to the SNDRV_PCM_RATE_XXX bit */
+			static int conv_rates[] = {
+				5512, 8000, 11025, 16000, 22050, 32000, 44100, 48000,
+				64000, 88200, 96000, 176400, 192000
+			};
+			fp->rate_table = kmalloc(sizeof(int) * nr_rates, GFP_KERNEL);
+			if (fp->rate_table == NULL) {
+				snd_printk(KERN_ERR "cannot malloc\n");
+				kfree(fp);
+				break;
+			}
+
+			fp->nr_rates = nr_rates;
+			fp->rate_min = fp->rate_max = combine_triple(&fmt[8]);
+			for (r = 0, idx = 8; r < nr_rates; r++, idx += 3) {
+				int rate = fp->rate_table[r] = combine_triple(&fmt[idx]);
+				if (rate < fp->rate_min)
+					fp->rate_min = rate;
+				else if (rate > fp->rate_max)
+					fp->rate_max = rate;
+				for (c = 0; c < 13; c++) {
+					if (rate == conv_rates[c]) {
+						fp->rates |= (1 << c);
+						break;
+					}
+				}
+#if 0 // FIXME - we need to define constraint
+				if (c >= 13)
+					fp->rates |= SNDRV_PCM_KNOT; /* unconventional rate */
+#endif
+			}
+
+		} else {
+			/* continuous rates */
+			fp->rates = SNDRV_PCM_RATE_CONTINUOUS;
+			fp->rate_min = combine_triple(&fmt[8]);
+			fp->rate_max = combine_triple(&fmt[11]);
+		}
+
+		
+		snd_printdd(KERN_INFO "%d:%u:%d: add audio endpoint 0x%x\n", dev->devnum, iface_no, i, fp->endpoint);
+		err = add_audio_endpoint(chip, stream, fp);
+		if (err < 0) {
+			if (fp->rate_table)
+				kfree(fp->rate_table);
+			kfree(fp);
+			return err;
+		}
+	}
+	return 0;
+}
+
+
+/*
+ * parse audio control descriptor and create pcm/midi streams
+ */
+
+static int snd_usb_create_midi_interface(snd_usb_audio_t *chip,
+					 struct usb_interface *iface,
+					 const snd_usb_audio_quirk_t *quirk);
+
+static int snd_usb_create_streams(snd_usb_audio_t *chip, int ctrlif,
+				  unsigned char *buffer, int buflen)
+{
+	struct usb_device *dev = chip->dev;
+	struct usb_config_descriptor *config;
+	struct usb_interface *iface;
+	unsigned char *p1;
+	int i, j;
+
+	/* find audiocontrol interface */
+	if (!(p1 = snd_usb_find_csint_desc(buffer, buflen, NULL, HEADER, ctrlif, -1))) {
+		snd_printk(KERN_ERR "cannot find HEADER\n");
+		return -EINVAL;
+	}
+	if (! p1[7] || p1[0] < 8 + p1[7]) {
+		snd_printk(KERN_ERR "invalid HEADER\n");
+		return -EINVAL;
+	}
+
+	/*
+	 * parse all USB audio streaming interfaces
+	 */
+	config = dev->actconfig;
+	for (i = 0; i < p1[7]; i++) {
+		j = p1[8 + i];
+		if (j >= config->bNumInterfaces) {
+			snd_printk(KERN_ERR "%d:%u:%d : does not exist\n",
+				   dev->devnum, ctrlif, j);
+			continue;
+		}
+		iface = &config->interface[j];
+		if (usb_interface_claimed(iface)) {
+			snd_printdd(KERN_INFO "%d:%d:%d: skipping, already claimed\n", dev->devnum, ctrlif, j);
+			continue;
+		}
+		if (iface->altsetting[0].bInterfaceClass == USB_CLASS_AUDIO &&
+		    iface->altsetting[0].bInterfaceSubClass == USB_SUBCLASS_MIDI_STREAMING) {
+			if (snd_usb_create_midi_interface(chip, iface, NULL) < 0) {
+				snd_printk(KERN_ERR "%d:%u:%d: cannot create sequencer device\n", dev->devnum, ctrlif, j);
+				continue;
+			}
+			usb_driver_claim_interface(&usb_audio_driver, iface, (void *)-1);
+			continue;
+		}
+		if (iface->altsetting[0].bInterfaceClass != USB_CLASS_AUDIO ||
+		    iface->altsetting[0].bInterfaceSubClass != USB_SUBCLASS_AUDIO_STREAMING) {
+			snd_printdd(KERN_ERR "%d:%u:%d: skipping non-supported interface %d\n", dev->devnum, ctrlif, j, iface->altsetting[0].bInterfaceClass);
+			/* skip non-supported classes */
+			continue;
+		}
+		parse_audio_endpoints(chip, buffer, buflen, j);
+		usb_set_interface(dev, j, 0); /* reset the current interface */
+		usb_driver_claim_interface(&usb_audio_driver, iface, (void *)-1);
+	}
+
+	return 0;
+}
+
+static int snd_usb_create_midi_interface(snd_usb_audio_t *chip,
+					 struct usb_interface *iface,
+					 const snd_usb_audio_quirk_t *quirk)
+{
+#if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE)
+	snd_seq_device_t *seq_device;
+	snd_usb_midi_t *umidi;
+	int err;
+
+	err = snd_seq_device_new(chip->card, chip->next_seq_device,
+				 SNDRV_SEQ_DEV_ID_USBMIDI,
+				 sizeof(snd_usb_midi_t), &seq_device);
+	if (err < 0)
+		return err;
+	chip->next_seq_device++;
+	strcpy(seq_device->name, chip->card->shortname);
+	umidi = (snd_usb_midi_t *)SNDRV_SEQ_DEVICE_ARGPTR(seq_device);
+	umidi->chip = chip;
+	umidi->iface = iface;
+	umidi->ifnum = iface->altsetting->bInterfaceNumber;
+	umidi->quirk = quirk;
+	umidi->seq_client = -1;
+#endif
+	return 0;
+}
+
+static inline int snd_usb_create_quirk(snd_usb_audio_t *chip,
+				       struct usb_interface *iface,
+	       			       const snd_usb_audio_quirk_t *quirk)
+{
+	/* in the future, there may be quirks for PCM devices */
+	return snd_usb_create_midi_interface(chip, iface, quirk);
+}
+
+
+/*
+ * free the chip instance
+ *
+ * here we have to do not much, since pcm and controls are already freed
+ *
+ */
+
+static int snd_usb_audio_free(snd_usb_audio_t *chip)
+{
+	down(&register_mutex);
+	usb_chip[chip->index] = NULL;
+	up(&register_mutex);
+	snd_magic_kfree(chip);
+	return 0;
+}
+
+static int snd_usb_audio_dev_free(snd_device_t *device)
+{
+	snd_usb_audio_t *chip = snd_magic_cast(snd_usb_audio_t, device->device_data, return -ENXIO);
+	return snd_usb_audio_free(chip);
+}
+
+
+/*
+ * create a chip instance and set its names.
+ */
+static int snd_usb_audio_create(snd_card_t *card, struct usb_device *dev,
+				const snd_usb_audio_quirk_t *quirk,
+				snd_usb_audio_t **rchip)
+{
+	snd_usb_audio_t *chip;
+	int err, len;
+	static snd_device_ops_t ops = {
+		.dev_free =	snd_usb_audio_dev_free,
+	};
+	
+	*rchip = NULL;
+	chip = snd_magic_kcalloc(snd_usb_audio_t, 0, GFP_KERNEL);
+	if (! chip)
+		return -ENOMEM;
+
+	chip->dev = dev;
+	chip->card = card;
+	INIT_LIST_HEAD(&chip->pcm_list);
+
+	if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
+		snd_usb_audio_free(chip);
+		return err;
+	}
+
+	strcpy(card->driver, "USB-Audio");
+
+	/* retrieve the device string as shortname */
+	if (dev->descriptor.iProduct)
+		len = usb_string(dev, dev->descriptor.iProduct,
+				 card->shortname, sizeof(card->shortname));
+	else
+		len = 0;
+	if (len <= 0) {
+ 		if (quirk && quirk->product_name) {
+			strncpy(card->shortname, quirk->product_name, sizeof(card->shortname) - 1);
+			card->shortname[sizeof(card->shortname) - 1] = '\0';
+		} else {
+			sprintf(card->shortname, "USB Device %#04x:%#04x",
+				dev->descriptor.idVendor, dev->descriptor.idProduct);
+		}
+	}
+
+	/* retrieve the vendor and device strings as longname */
+	if (dev->descriptor.iManufacturer)
+		len = usb_string(dev, dev->descriptor.iManufacturer,
+				 card->longname, sizeof(card->longname) - 1);
+	else
+		len = 0;
+	if (len <= 0) {
+		if (quirk && quirk->vendor_name) {
+			strncpy(card->longname, quirk->vendor_name, sizeof(card->longname) - 2);
+			card->longname[sizeof(card->longname) - 2] = '\0';
+			len = strlen(card->longname);
+		} else {
+			len = 0;
+		}
+	}
+	if (len > 0) {
+		card->longname[len] = ' ';
+		len++;
+	}
+	card->longname[len] = '\0';
+	if ((!dev->descriptor.iProduct
+	     || usb_string(dev, dev->descriptor.iProduct,
+			   card->longname + len, sizeof(card->longname) - len) <= 0)
+	    && quirk && quirk->product_name) {
+		strncpy(card->longname + len, quirk->product_name, sizeof(card->longname) - len - 1);
+		card->longname[sizeof(card->longname) - 1] = '\0';
+	}
+
+	*rchip = chip;
+	return 0;
+}
+
+
+/*
+ * allocate and get description buffer
+ * must be freed later.
+ */
+static int alloc_desc_buffer(struct usb_device *dev, int index, unsigned char **bufptr)
+{
+	int err, buflen;
+	unsigned char buf[8];
+	unsigned char *buffer;
+
+	*bufptr = 0;
+	err = usb_get_descriptor(dev, USB_DT_CONFIG, index, buf, 8);
+	if (err < 0) {
+		snd_printk(KERN_ERR "%d:%d: cannot get first 8 bytes\n", index, dev->devnum);
+		return err;
+	}
+	if (buf[1] != USB_DT_CONFIG || buf[0] < 9) {
+		snd_printk(KERN_ERR "%d:%d: invalid config desc\n", index, dev->devnum);
+		return -EINVAL;
+	}
+	buflen = combine_word(&buf[2]);
+	if (!(buffer = kmalloc(buflen, GFP_KERNEL))) {
+		snd_printk(KERN_ERR "cannot malloc descriptor (size = %d)\n", buflen);
+		return -ENOMEM;
+	}
+	err = usb_get_descriptor(dev, USB_DT_CONFIG, index, buffer, buflen);
+	if (err < 0) {
+		snd_printk(KERN_ERR "%d:%d: cannot get DT_CONFIG: error %d\n", index, dev->devnum, err);
+		kfree(buffer);
+		return err;
+	}
+	*bufptr = buffer;
+	return buflen;
+}
+
+
+/*
+ * probe the active usb device
+ *
+ * note that this can be called multiple times per a device, when it
+ * includes multiple audio control interfaces.
+ *
+ * thus we check the usb device pointer and creates the card instance
+ * only at the first time.  the successive calls of this function will
+ * append the pcm interface to the corresponding card.
+ */
+static void *snd_usb_audio_probe(struct usb_device *dev,
+				 struct usb_interface *intf,
+				 const struct usb_device_id *id)
+{
+	struct usb_config_descriptor *config = dev->actconfig;	
+	const snd_usb_audio_quirk_t *quirk = (const snd_usb_audio_quirk_t *)id->driver_info;
+	int i;
+	snd_card_t *card;
+	snd_usb_audio_t *chip;
+	int ifnum = intf->altsetting->bInterfaceNumber;
+
+	if (quirk && quirk->ifnum != QUIRK_ANY_INTERFACE && ifnum != quirk->ifnum)
+		goto __err_val;
+
+	if (usb_set_configuration(dev, config->bConfigurationValue) < 0) {
+		snd_printk(KERN_ERR "cannot set configuration (value 0x%x)\n", config->bConfigurationValue);
+		goto __err_val;
+	}
+
+	/*
+	 * found a config.  now register to ALSA
+	 */
+
+	/* check whether it's already registered */
+	chip = NULL;
+	down(&register_mutex);
+	for (i = 0; i < SNDRV_CARDS; i++) {
+		if (usb_chip[i] && usb_chip[i]->dev == dev) {
+			chip = usb_chip[i];
+			break;
+		}
+	}
+	if (! chip) {
+		/* it's a fresh one.
+		 * now look for an empty slot and create a new card instance
+		 */
+		for (i = 0; i < SNDRV_CARDS; i++)
+			if (snd_enable[i] && ! usb_chip[i] &&
+			    (snd_vid[i] == -1 || snd_vid[i] == dev->descriptor.idVendor) &&
+			    (snd_pid[i] == -1 || snd_pid[i] == dev->descriptor.idProduct)) {
+				card = snd_card_new(snd_index[i], snd_id[i], THIS_MODULE, 0);
+				if (card == NULL) {
+					snd_printk(KERN_ERR "cannot create a card instance %d\n", i);
+					goto __error;
+				}
+				if (snd_usb_audio_create(card, dev, quirk, &chip) < 0) {
+					snd_card_free(card);
+					goto __error;
+				}
+				chip->index = i;
+				usb_chip[i] = chip;
+				break;
+			}
+		if (! chip) {
+			snd_printk(KERN_ERR "no available usb audio device\n");
+			goto __error;
+		}
+	}
+
+	if (!quirk) {
+		/* USB audio interface */
+		unsigned char *buffer;
+		unsigned int index;
+		int buflen;
+
+		index = dev->actconfig - config;
+		buflen = alloc_desc_buffer(dev, index, &buffer);
+		if (buflen <= 0)
+			goto __error;
+		if (snd_usb_create_streams(chip, ifnum, buffer, buflen) < 0 ||
+		    snd_usb_create_mixer(chip, ifnum, buffer, buflen) < 0) {
+			kfree(buffer);
+			goto __error;
+		}
+		kfree(buffer);
+	} else {
+		/* USB midi interface */
+		if (snd_usb_create_quirk(chip, intf, quirk) < 0)
+			goto __error;
+	}
+
+	/* we are allowed to call snd_card_register() many times */
+	if (snd_card_register(chip->card) < 0) {
+		if (! chip->num_interfaces)
+			snd_card_free(chip->card);
+		goto __error;
+	}
+
+	chip->num_interfaces++;
+	up(&register_mutex);
+	return chip;
+
+ __error:
+	up(&register_mutex);
+ __err_val:
+	return NULL;
+}
+
+/*
+ * we need to take care of counter, since disconnection can be called also
+ * many times as well as usb_audio_probe(). 
+ */
+static void snd_usb_audio_disconnect(struct usb_device *dev, void *ptr)
+{
+	snd_usb_audio_t *chip;
+
+	if (ptr == (void *)-1)
+		return;
+
+	chip = snd_magic_cast(snd_usb_audio_t, ptr, return);
+	chip->num_interfaces--;
+	if (chip->num_interfaces <= 0)
+		snd_card_free(chip->card);
+}
+
+
+#ifdef OLD_USB
+
+/*
+ * 2.4 USB kernel API
+ */
+static void *usb_audio_probe(struct usb_device *dev, unsigned int ifnum,
+			     const struct usb_device_id *id)
+{
+	return snd_usb_audio_probe(dev, usb_ifnum_to_if(dev, ifnum), id);
+}
+
+static void usb_audio_disconnect(struct usb_device *dev, void *ptr)
+{
+	snd_usb_audio_disconnect(dev, ptr);
+}
+
+#else
+
+/*
+ * new 2.5 USB kernel API
+ */
+static int usb_audio_probe(struct usb_interface *intf,
+			   const struct usb_device_id *id)
+{
+	void *chip;
+	chip = snd_usb_audio_probe(interface_to_usbdev(intf), intf, id);
+	if (chip) {
+		dev_set_drvdata(&intf->dev, chip);
+		return 0;
+	} else
+		return -EIO;
+}
+
+static void usb_audio_disconnect(struct usb_interface *intf)
+{
+	snd_usb_audio_disconnect(interface_to_usbdev(intf),
+				 dev_get_drvdata(&intf->dev));
+}
+#endif
+
+
+
+static int __init snd_usb_audio_init(void)
+{
+	usb_register(&usb_audio_driver);
+	return 0;
+}
+
+
+static void __exit snd_usb_audio_cleanup(void)
+{
+	usb_deregister(&usb_audio_driver);
+}
+
+module_init(snd_usb_audio_init);
+module_exit(snd_usb_audio_cleanup);
+
+#ifndef MODULE
+/*
+ * format is snd-usb-audio=snd_enable,snd_index,snd_id
+ */
+static int __init snd_usb_audio_module_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-usb-audio=", snd_usb_audio_module_setup);
+
+#endif /* !MODULE */
diff -Nru a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/sound/usb/usbaudio.h	Mon Oct  7 11:25:40 2002
@@ -0,0 +1,218 @@
+#ifndef __USBAUDIO_H
+#define __USBAUDIO_H
+/*
+ *   (Tentative) USB Audio Driver for ALSA
+ *
+ *   Copyright (c) 2002 by Takashi Iwai <tiwai@suse.de>
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+
+
+/*
+ */
+
+#define USB_SUBCLASS_AUDIO_CONTROL	0x01
+#define USB_SUBCLASS_AUDIO_STREAMING	0x02
+#define USB_SUBCLASS_MIDI_STREAMING	0x03
+
+#define USB_DT_CS_DEVICE                0x21
+#define USB_DT_CS_CONFIG                0x22
+#define USB_DT_CS_STRING                0x23
+#define USB_DT_CS_INTERFACE             0x24
+#define USB_DT_CS_ENDPOINT              0x25
+
+#define CS_AUDIO_UNDEFINED		0x20
+#define CS_AUDIO_DEVICE			0x21
+#define CS_AUDIO_CONFIGURATION		0x22
+#define CS_AUDIO_STRING			0x23
+#define CS_AUDIO_INTERFACE		0x24
+#define CS_AUDIO_ENDPOINT		0x25
+
+#define HEADER				0x01
+#define INPUT_TERMINAL			0x02
+#define OUTPUT_TERMINAL			0x03
+#define MIXER_UNIT			0x04
+#define SELECTOR_UNIT			0x05
+#define FEATURE_UNIT			0x06
+#define PROCESSING_UNIT			0x07
+#define EXTENSION_UNIT			0x08
+
+#define AS_GENERAL			0x01
+#define FORMAT_TYPE			0x02
+#define FORMAT_SPECIFIC			0x03
+
+#define EP_GENERAL			0x01
+
+#define MS_GENERAL			0x01
+#define MIDI_IN_JACK			0x02
+#define MIDI_OUT_JACK			0x03
+
+/* endpoint attributes */
+#define EP_ATTR_MASK			0x0c
+#define EP_ATTR_ASYNC			0x04
+#define EP_ATTR_ADAPTIVE		0x08
+#define EP_ATTR_SYNC			0x0c
+
+/* cs endpoint attributes */
+#define EP_CS_ATTR_SAMPLE_RATE		0x01
+#define EP_CS_ATTR_PITCH_CONTROL	0x02
+#define EP_CS_ATTR_FILL_MAX		0x80
+
+/* Audio Class specific Request Codes */
+
+#define SET_CUR    0x01
+#define GET_CUR    0x81
+#define SET_MIN    0x02
+#define GET_MIN    0x82
+#define SET_MAX    0x03
+#define GET_MAX    0x83
+#define SET_RES    0x04
+#define GET_RES    0x84
+#define SET_MEM    0x05
+#define GET_MEM    0x85
+#define GET_STAT   0xff
+
+/* Terminal Control Selectors */
+
+#define COPY_PROTECT_CONTROL       0x01
+
+/* Endpoint Control Selectors */
+
+#define SAMPLING_FREQ_CONTROL      0x01
+#define PITCH_CONTROL              0x02
+
+/* Format Types */
+#define USB_FORMAT_TYPE_I	0x01
+#define USB_FORMAT_TYPE_II	0x02
+#define USB_FORMAT_TYPE_III	0x03
+
+/* type I */
+#define USB_AUDIO_FORMAT_PCM	0x01
+#define USB_AUDIO_FORMAT_PCM8	0x02
+#define USB_AUDIO_FORMAT_IEEE_FLOAT	0x03
+#define USB_AUDIO_FORMAT_ALAW	0x04
+#define USB_AUDIO_FORMAT_MU_LAW	0x05
+
+/* type II */
+#define USB_AUDIO_FORMAT_MPEG	0x1001
+#define USB_AUDIO_FORMAT_AC3	0x1002
+
+/* type III */
+#define USB_AUDIO_FORMAT_IEC1937_AC3	0x2001
+#define USB_AUDIO_FORMAT_IEC1937_MPEG1_LAYER1	0x2002
+#define USB_AUDIO_FORMAT_IEC1937_MPEG2_NOEXT	0x2003
+#define USB_AUDIO_FORMAT_IEC1937_MPEG2_EXT	0x2004
+#define USB_AUDIO_FORMAT_IEC1937_MPEG2_LAYER1_LS	0x2005
+#define USB_AUDIO_FORMAT_IEC1937_MPEG2_LAYER23_LS	0x2006
+
+
+/* maximum number of endpoints per interface */
+#define MIDI_MAX_ENDPOINTS 2
+
+#define SNDRV_SEQ_DEV_ID_USBMIDI "usb-midi"
+
+/*
+ */
+
+typedef struct snd_usb_audio snd_usb_audio_t;
+
+struct snd_usb_audio {
+	
+	int index;
+	struct usb_device *dev;
+	snd_card_t *card;
+	int num_interfaces;
+
+	struct list_head pcm_list;	/* list of pcm streams */
+	int pcm_devs;
+
+#if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE)
+	int next_seq_device;
+#endif
+};  
+
+/*
+ * Information about devices with broken descriptors
+ */
+#define QUIRK_ANY_INTERFACE -1
+
+#define QUIRK_MIDI_FIXED_ENDPOINT	0
+#define QUIRK_MIDI_YAMAHA		1
+#define QUIRK_MIDI_MIDIMAN		2
+
+typedef struct snd_usb_audio_quirk snd_usb_audio_quirk_t;
+typedef struct snd_usb_midi_endpoint_info snd_usb_midi_endpoint_info_t;
+
+struct snd_usb_audio_quirk {
+	const char *vendor_name;
+	const char *product_name;
+	int16_t ifnum;
+	int16_t type;
+	const void *data;
+};
+
+/* data for QUIRK_MIDI_FIXED_ENDPOINT */
+struct snd_usb_midi_endpoint_info {
+	int16_t epnum;		/* ep number, -1 autodetect */
+	uint16_t out_cables;	/* bitmask */
+	uint16_t in_cables;	/* bitmask */
+};
+
+/* for QUIRK_MIDI_YAMAHA, data is NULL */
+
+/* for QUIRK_MIDI_MIDIMAN, data is the number of ports */
+
+/*
+ * USB MIDI sequencer device data
+ */
+typedef struct snd_usb_midi snd_usb_midi_t;
+typedef struct snd_usb_midi_endpoint snd_usb_midi_endpoint_t;
+typedef struct snd_usb_midi_out_endpoint snd_usb_midi_out_endpoint_t;
+typedef struct snd_usb_midi_in_endpoint snd_usb_midi_in_endpoint_t;
+
+struct snd_usb_midi {
+	/* filled by usbaudio.c */
+	snd_usb_audio_t *chip;
+	struct usb_interface *iface;
+	int ifnum;
+	const snd_usb_audio_quirk_t *quirk;
+
+	/* used internally in usbmidi.c */
+	int seq_client;
+	struct snd_usb_midi_endpoint {
+		snd_usb_midi_out_endpoint_t *out;
+		snd_usb_midi_in_endpoint_t *in;
+		snd_rawmidi_t *rmidi[0x10];
+	} endpoints[MIDI_MAX_ENDPOINTS];
+};
+
+
+/*
+ */
+
+#define combine_word(s)    ((*s) | ((unsigned int)(s)[1] << 8))
+#define combine_triple(s)  (combine_word(s) | ((unsigned int)(s)[2] << 16))
+#define combine_quad(s)    (combine_triple(s) | ((unsigned int)(s)[3] << 24))
+
+unsigned int snd_usb_combine_bytes(unsigned char *bytes, int size);
+
+void *snd_usb_find_desc(void *descstart, int desclen, void *after, u8 dtype, int iface, int altsetting);
+void *snd_usb_find_csint_desc(void *descstart, int desclen, void *after, u8 dsubtype, int iface, int altsetting);
+
+int snd_usb_create_mixer(snd_usb_audio_t *chip, int ctrlif, unsigned char *buffer, int buflen);
+
+#endif /* __USBAUDIO_H */
diff -Nru a/sound/usb/usbmidi.c b/sound/usb/usbmidi.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/sound/usb/usbmidi.c	Mon Oct  7 11:26:06 2002
@@ -0,0 +1,1197 @@
+/*
+ * usbmidi.c - ALSA USB MIDI driver
+ *
+ * Copyright (c) 2002 Clemens Ladisch
+ * All rights reserved.
+ *
+ * Based on the OSS usb-midi driver by NAGANO Daisuke,
+ *          NetBSD's umidi driver by Takuya SHIOZAKI,
+ *          the "USB Device Class Definition for MIDI Devices" by Roland
+ *
+ * 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. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed and/or modified 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 SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sound/driver.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/string.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+#include <sound/core.h>
+#include <sound/minors.h>
+#include <sound/asequencer.h>
+#include <sound/seq_device.h>
+#include <sound/seq_kernel.h>
+#include <sound/seq_virmidi.h>
+#include <sound/seq_midi_event.h>
+#include <sound/initval.h>
+#include "usbaudio.h"
+
+MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
+MODULE_DESCRIPTION("USB MIDI");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_CLASSES("{sound}");
+
+/* size of the per-endpoint output buffer, must be a multiple of 4 */
+#define OUTPUT_BUFFER_SIZE 0x400
+
+/* max. size of incoming sysex messages */
+#define INPUT_BUFFER_SIZE 0x200
+
+typedef struct usb_driver usb_driver_t;
+typedef struct usb_device usb_device_t;
+typedef struct usb_device_id usb_device_id_t;
+typedef struct usb_interface usb_interface_t;
+typedef struct usb_interface_descriptor usb_interface_descriptor_t;
+typedef struct usb_ms_header_descriptor usb_ms_header_descriptor_t;
+typedef struct usb_endpoint_descriptor usb_endpoint_descriptor_t;
+typedef struct usb_ms_endpoint_descriptor usb_ms_endpoint_descriptor_t;
+
+struct usb_ms_header_descriptor {
+	__u8  bLength;
+	__u8  bDescriptorType;
+	__u8  bDescriptorSubtype;
+	__u8  bcdMSC[2];
+	__u16 wTotalLength;
+} __attribute__ ((packed));
+
+struct usb_ms_endpoint_descriptor {
+	__u8  bLength;
+	__u8  bDescriptorType;
+	__u8  bDescriptorSubtype;
+	__u8  bNumEmbMIDIJack;
+	__u8  baAssocJackID[0];
+} __attribute__ ((packed));
+
+typedef struct usbmidi_out_port usbmidi_out_port_t;
+typedef struct usbmidi_in_port usbmidi_in_port_t;
+
+struct snd_usb_midi_out_endpoint {
+	snd_usb_midi_t* umidi;
+	struct urb* urb;
+	int max_transfer;		/* size of urb buffer */
+	struct tasklet_struct tasklet;
+
+	uint8_t buffer[OUTPUT_BUFFER_SIZE]; /* ring buffer */
+	int data_begin;
+	int data_size;
+	spinlock_t buffer_lock;
+
+	struct usbmidi_out_port {
+		snd_usb_midi_out_endpoint_t* ep;
+		uint8_t cable;		/* cable number << 4 */
+		uint8_t sysex_len;
+		uint8_t sysex[2];
+	} ports[0x10];
+};
+
+struct snd_usb_midi_in_endpoint {
+	snd_usb_midi_t* umidi;
+	snd_usb_midi_endpoint_t* ep;
+	struct urb* urb;
+	struct usbmidi_in_port {
+		int seq_port;
+		snd_midi_event_t* midi_event;
+	} ports[0x10];
+};
+
+static void snd_usbmidi_do_output(snd_usb_midi_out_endpoint_t* ep);
+
+static const uint8_t snd_usbmidi_cin_length[] = {
+	0, 0, 2, 3, 3, 1, 2, 3, 3, 3, 3, 3, 2, 2, 3, 1
+};
+
+/*
+ * Submits the URB, with error handling.
+ */
+static int snd_usbmidi_submit_urb(struct urb* urb, int flags)
+{
+	int err = usb_submit_urb(urb, flags);
+	if (err < 0 && err != -ENODEV)
+		printk(KERN_ERR "snd-usb-midi: usb_submit_urb: %d\n", err);
+	return err;
+}
+
+/*
+ * Error handling for URB completion functions.
+ */
+static int snd_usbmidi_urb_error(int status)
+{
+	if (status == -ENOENT)
+		return status; /* killed */
+	if (status == -ENODEV ||
+	    status == -EILSEQ ||
+	    status == -ETIMEDOUT)
+		return -ENODEV; /* device removed */
+	printk(KERN_ERR "snd-usb-midi: urb status %d\n", status);
+	return 0; /* continue */
+}
+
+/*
+ * Converts a USB MIDI packet into an ALSA sequencer event.
+ */
+static void snd_usbmidi_input_packet(snd_usb_midi_in_endpoint_t* ep,
+				     uint8_t packet[4])
+{
+	int cable = packet[0] >> 4;
+	usbmidi_in_port_t* port = &ep->ports[cable];
+	snd_seq_event_t ev;
+
+	if (!port->midi_event)
+		return;
+	memset(&ev, 0, sizeof(ev));
+	if (snd_midi_event_encode(port->midi_event, &packet[1],
+				  snd_usbmidi_cin_length[packet[0] & 0x0f], &ev) > 0
+	    && ev.type != SNDRV_SEQ_EVENT_NONE) {
+		ev.source.port = port->seq_port;
+		ev.dest.client = SNDRV_SEQ_ADDRESS_SUBSCRIBERS;
+		snd_seq_kernel_client_dispatch(ep->umidi->seq_client,
+					       &ev, 1, 0);
+		if (ep->ep->rmidi[cable])
+			snd_virmidi_receive(ep->ep->rmidi[cable], &ev);
+	}
+}
+
+/*
+ * Processes the data read from the device.
+ */
+static void snd_usbmidi_in_urb_complete(struct urb* urb)
+{
+	snd_usb_midi_in_endpoint_t* ep = snd_magic_cast(snd_usb_midi_in_endpoint_t, urb->context, return);
+
+	if (urb->status == 0) {
+		uint8_t* buffer = (uint8_t*)ep->urb->transfer_buffer;
+		int i;
+
+		for (i = 0; i + 4 <= urb->actual_length; i += 4)
+			if (buffer[i] != 0)
+				snd_usbmidi_input_packet(ep, &buffer[i]);
+	} else {
+		if (snd_usbmidi_urb_error(urb->status) < 0)
+			return;
+	}
+
+	if (!usb_pipeint(urb->pipe)) {
+		urb->dev = ep->umidi->chip->dev;
+		snd_usbmidi_submit_urb(urb, GFP_ATOMIC);
+	}
+}
+
+/*
+ * Converts the data read from a Midiman device to standard USB MIDI packets.
+ */
+static void snd_usbmidi_in_midiman_complete(struct urb* urb)
+{
+	if (urb->status == 0) {
+		uint8_t* buffer = (uint8_t*)urb->transfer_buffer;
+		int i;
+
+		for (i = 0; i + 4 <= urb->actual_length; i += 4) {
+			if (buffer[i + 3] != 0) {
+				/*
+				 * snd_usbmidi_input_packet() doesn't check the
+				 * contents of the message, so we simply use
+				 * some random CIN with the desired length.
+				 */
+				static const uint8_t cin[4] = {
+					0x0, 0xf, 0x2, 0x3
+				};
+				uint8_t ctl = buffer[i + 3];
+				buffer[i + 3] = buffer[i + 2];
+				buffer[i + 2] = buffer[i + 1];
+				buffer[i + 1] = buffer[i + 0];
+				buffer[i + 0] = (ctl & 0xf0) | cin[ctl & 3];
+			} else {
+				buffer[i + 0] = 0;
+			}
+		}
+	}
+	snd_usbmidi_in_urb_complete(urb);
+}
+
+static void snd_usbmidi_out_urb_complete(struct urb* urb)
+{
+	snd_usb_midi_out_endpoint_t* ep = snd_magic_cast(snd_usb_midi_out_endpoint_t, urb->context, return);
+	unsigned long flags;
+
+	if (urb->status < 0) {
+		if (snd_usbmidi_urb_error(urb->status) < 0)
+			return;
+	}
+	spin_lock_irqsave(&ep->buffer_lock, flags);
+	snd_usbmidi_do_output(ep);
+	spin_unlock_irqrestore(&ep->buffer_lock, flags);
+}
+
+/*
+ * Converts standard USB MIDI packets to what Midman devices expect.
+ */
+static void snd_usbmidi_convert_to_midiman(struct urb* urb)
+{
+	uint8_t* buffer = (uint8_t*)urb->transfer_buffer;
+	int i;
+
+	for (i = 0; i + 4 <= urb->transfer_buffer_length; i += 4) {
+		uint8_t cin = buffer[i];
+		buffer[i + 0] = buffer[i + 1];
+		buffer[i + 1] = buffer[i + 2];
+		buffer[i + 2] = buffer[i + 3];
+		buffer[i + 3] = (cin & 0xf0) | snd_usbmidi_cin_length[cin & 0x0f];
+	}
+}
+
+/*
+ * This is called when some data should be transferred to the device
+ * (after the reception of one or more sequencer events, or after completion
+ * of the previous transfer). ep->buffer_lock must be held.
+ */
+static void snd_usbmidi_do_output(snd_usb_midi_out_endpoint_t* ep)
+{
+	int len;
+	uint8_t* buffer;
+
+	if (ep->urb->status == -EINPROGRESS ||
+	    ep->data_size == 0)
+		return;
+	buffer = (uint8_t*)ep->urb->transfer_buffer;
+
+	/* first chunk, up to the end of the buffer */
+	len = OUTPUT_BUFFER_SIZE - ep->data_begin;
+	if (len > ep->data_size)
+		len = ep->data_size;
+	if (len > ep->max_transfer)
+		len = ep->max_transfer;
+	if (len > 0) {
+		memcpy(buffer, ep->buffer + ep->data_begin, len);
+		ep->data_begin = (ep->data_begin + len) % OUTPUT_BUFFER_SIZE;
+		ep->data_size -= len;
+		buffer += len;
+		ep->urb->transfer_buffer_length = len;
+	}
+
+	/* second chunk (after wraparound) */
+	if (ep->data_begin == 0 && ep->data_size > 0 &&
+	    len < ep->max_transfer) {
+		len = ep->max_transfer - len;
+		if (len > ep->data_size)
+			len = ep->data_size;
+		memcpy(buffer, ep->buffer, len);
+		ep->data_begin = len;
+		ep->data_size -= len;
+		ep->urb->transfer_buffer_length += len;
+	}
+
+	if (len > 0) {
+		if (ep->umidi->quirk && ep->umidi->quirk->type == QUIRK_MIDI_MIDIMAN)
+			snd_usbmidi_convert_to_midiman(ep->urb);
+
+		ep->urb->dev = ep->umidi->chip->dev;
+		snd_usbmidi_submit_urb(ep->urb, GFP_ATOMIC);
+	}
+}
+
+static void snd_usbmidi_out_tasklet(unsigned long data)
+{
+	snd_usb_midi_out_endpoint_t* ep = snd_magic_cast(snd_usb_midi_out_endpoint_t, (void*)data, return);
+	unsigned long flags;
+	
+	spin_lock_irqsave(&ep->buffer_lock, flags);
+	snd_usbmidi_do_output(ep);
+	spin_unlock_irqrestore(&ep->buffer_lock, flags);
+}
+
+/*
+ * Adds one USB MIDI packet to the output buffer.
+ */
+static void output_packet(usbmidi_out_port_t* port,
+			  uint8_t p0, uint8_t p1, uint8_t p2, uint8_t p3)
+{
+	snd_usb_midi_out_endpoint_t* ep = port->ep;
+	unsigned long flags;
+
+	spin_lock_irqsave(&ep->buffer_lock, flags);
+	if (ep->data_size < OUTPUT_BUFFER_SIZE) {
+		uint8_t* buf = ep->buffer + (ep->data_begin + ep->data_size) % OUTPUT_BUFFER_SIZE;
+		buf[0] = p0;
+		buf[1] = p1;
+		buf[2] = p2;
+		buf[3] = p3;
+		ep->data_size += 4;
+		if (ep->data_size == ep->max_transfer)
+			snd_usbmidi_do_output(ep);
+	}
+	spin_unlock_irqrestore(&ep->buffer_lock, flags);
+}
+
+/*
+ * Callback for snd_seq_dump_var_event.
+ */
+static int snd_usbmidi_sysex_dump(void* ptr, void* buf, int count)
+{
+	usbmidi_out_port_t* port = (usbmidi_out_port_t*)ptr;
+	const uint8_t* dump = (const uint8_t*)buf;
+
+	for (; count; --count) {
+		uint8_t byte = *dump++;
+
+		if (byte == 0xf0 && port->sysex_len > 0) {
+			/*
+			 * The previous SysEx wasn't terminated correctly.
+			 * Send the last bytes anyway, and hope that the
+			 * receiving device won't be too upset about the
+			 * missing F7.
+			 */
+			output_packet(port,
+				      port->cable | (0x04 + port->sysex_len),
+				      port->sysex[0],
+				      port->sysex_len >= 2 ? port->sysex[1] : 0,
+				      0);
+			port->sysex_len = 0;
+		}
+		if (byte != 0xf7) {
+			if (port->sysex_len >= 2) {
+				output_packet(port,
+					      port->cable | 0x04,
+					      port->sysex[0],
+					      port->sysex[1],
+					      byte);
+				port->sysex_len = 0;
+			} else {
+				port->sysex[port->sysex_len++] = byte;
+			}
+		} else {
+			uint8_t cin, data[3];
+			int i;
+
+			for (i = 0; i < port->sysex_len; ++i)
+				data[i] = port->sysex[i];
+			data[i++] = 0xf7;
+			cin = port->cable | (0x04 + i);
+			for (; i < 3; ++i)
+				data[i] = 0;
+			/*
+			 * cin,data[] is x5,{F7 00 00}
+			 *            or x6,{xx F7 00}
+			 *            or x7,{xx xx F7}
+			 */
+			output_packet(port, cin, data[0], data[1], data[2]);
+			port->sysex_len = 0;
+		}
+	}
+	return 0;
+}
+
+/*
+ * Converts an ALSA sequencer event into USB MIDI packets.
+ */
+static int snd_usbmidi_event_input(snd_seq_event_t* ev, int direct,
+				   void* private_data, int atomic, int hop)
+{
+	usbmidi_out_port_t* port = (usbmidi_out_port_t*)private_data;
+	int err;
+	uint8_t p0, p1;
+
+	p0 = port->cable;
+	p1 = ev->data.note.channel & 0xf;
+
+	switch (ev->type) {
+	case SNDRV_SEQ_EVENT_NOTEON:
+		output_packet(port, p0 | 0x09, p1 | 0x90,
+			      ev->data.note.note & 0x7f,
+			      ev->data.note.velocity & 0x7f);
+		break;
+	case SNDRV_SEQ_EVENT_NOTEOFF:
+		output_packet(port, p0 | 0x08, p1 | 0x80,
+			      ev->data.note.note & 0x7f,
+			      ev->data.note.velocity & 0x7f);
+		break;
+	case SNDRV_SEQ_EVENT_KEYPRESS:
+		output_packet(port, p0 | 0x0a, p1 | 0xa0,
+			      ev->data.note.note & 0x7f,
+			      ev->data.note.velocity & 0x7f);
+		break;
+	case SNDRV_SEQ_EVENT_CONTROLLER:
+		output_packet(port, p0 | 0x0b, p1 | 0xb0,
+			      ev->data.control.param & 0x7f,
+			      ev->data.control.value & 0x7f);
+		break;
+	case SNDRV_SEQ_EVENT_PGMCHANGE:
+		output_packet(port, p0 | 0x0c, p1 | 0xc0,
+			      ev->data.control.value & 0x7f, 0);
+		break;
+	case SNDRV_SEQ_EVENT_CHANPRESS:
+		output_packet(port, p0 | 0x0d, p1 | 0xd0,
+			      ev->data.control.value & 0x7f, 0);
+		break;
+	case SNDRV_SEQ_EVENT_PITCHBEND:
+		output_packet(port, p0 | 0x0e, p1 | 0xe0,
+			      (ev->data.control.value + 0x2000) & 0x7f,
+			      ((ev->data.control.value + 0x2000) >> 7) & 0x7f);
+		break;
+	case SNDRV_SEQ_EVENT_CONTROL14:
+		if (ev->data.control.param < 0x20) {
+			output_packet(port, p0 | 0x0b, p1 | 0xb0,
+				      ev->data.control.param,
+				      (ev->data.control.value >> 7) & 0x7f);
+			output_packet(port, p0 | 0x0b, p1 | 0xb0,
+				      ev->data.control.param + 0x20,
+				      ev->data.control.value & 0x7f);
+		} else {
+			output_packet(port, p0 | 0x0b, p1 | 0xb0,
+				      ev->data.control.param & 0x7f,
+				      ev->data.control.value & 0x7f);
+		}
+		break;
+	case SNDRV_SEQ_EVENT_SONGPOS:
+		output_packet(port, p0 | 0x03, 0xf2,
+			      ev->data.control.value & 0x7f,
+			      (ev->data.control.value >> 7) & 0x7f);
+		break;
+	case SNDRV_SEQ_EVENT_SONGSEL:
+		output_packet(port, p0 | 0x02, 0xf3,
+			      ev->data.control.value & 0x7f, 0);
+		break;
+	case SNDRV_SEQ_EVENT_QFRAME:
+		output_packet(port, p0 | 0x02, 0xf1,
+			      ev->data.control.value & 0x7f, 0);
+		break;
+	case SNDRV_SEQ_EVENT_START:
+		output_packet(port, p0 | 0x0f, 0xfa, 0, 0);
+		break;
+	case SNDRV_SEQ_EVENT_CONTINUE:
+		output_packet(port, p0 | 0x0f, 0xfb, 0, 0);
+		break;
+	case SNDRV_SEQ_EVENT_STOP:
+		output_packet(port, p0 | 0x0f, 0xfc, 0, 0);
+		break;
+	case SNDRV_SEQ_EVENT_CLOCK:
+		output_packet(port, p0 | 0x0f, 0xf8, 0, 0);
+		break;
+	case SNDRV_SEQ_EVENT_TUNE_REQUEST:
+		output_packet(port, p0 | 0x05, 0xf6, 0, 0);
+		break;
+	case SNDRV_SEQ_EVENT_RESET:
+		output_packet(port, p0 | 0x0f, 0xff, 0, 0);
+		break;
+	case SNDRV_SEQ_EVENT_SENSING:
+		output_packet(port, p0 | 0x0f, 0xfe, 0, 0);
+		break;
+	case SNDRV_SEQ_EVENT_SYSEX:
+		err = snd_seq_dump_var_event(ev, snd_usbmidi_sysex_dump, port);
+		if (err < 0)
+			return err;
+		break;
+	default:
+		return 0;
+	}
+	tasklet_hi_schedule(&port->ep->tasklet);
+	return 0;
+}
+
+/*
+ * Frees an input endpoint.
+ * May be called when ep hasn't been initialized completely.
+ */
+static void snd_usbmidi_in_endpoint_delete(snd_usb_midi_in_endpoint_t* ep)
+{
+	int i;
+
+	if (ep->urb) {
+		if (ep->urb->transfer_buffer) {
+			usb_unlink_urb(ep->urb);
+			kfree(ep->urb->transfer_buffer);
+		}
+		usb_free_urb(ep->urb);
+	}
+	for (i = 0; i < 0x10; ++i)
+		if (ep->ports[i].midi_event)
+			snd_midi_event_free(ep->ports[i].midi_event);
+	snd_magic_kfree(ep);
+}
+
+/*
+ * For Roland devices, use the alternate setting which uses interrupt
+ * transfers for input.
+ */
+static usb_endpoint_descriptor_t* snd_usbmidi_get_int_epd(snd_usb_midi_t* umidi,
+							  uint8_t epnum)
+{
+	usb_interface_t* intf;
+	usb_interface_descriptor_t* intfd;
+
+	if (umidi->chip->dev->descriptor.idVendor != 0x0582)
+		return NULL;
+	intf = umidi->iface;
+	if (!intf || intf->num_altsetting != 2)
+		return NULL;
+
+	intfd = &intf->altsetting[0];
+	if (intfd->bNumEndpoints != 2 ||
+	    (intfd->endpoint[0].bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_BULK ||
+	    (intfd->endpoint[1].bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_BULK)
+		return NULL;
+
+	intfd = &intf->altsetting[1];
+	if (intfd->bNumEndpoints != 2 ||
+	    (intfd->endpoint[0].bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_BULK ||
+	    (intfd->endpoint[1].bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_INT)
+		return NULL;
+
+	usb_set_interface(umidi->chip->dev, intfd->bInterfaceNumber,
+			  intfd->bAlternateSetting);
+	return &intfd->endpoint[1];
+}
+
+static usb_endpoint_descriptor_t* snd_usbmidi_get_midiman_int_epd(snd_usb_midi_t* umidi)
+{
+	usb_interface_t* intf = umidi->iface;
+	if (!intf)
+		return NULL;
+	return &intf->altsetting[0].endpoint[0];
+}
+
+/*
+ * Creates an input endpoint, and initalizes input ports.
+ * ALSA ports are created later.
+ */
+static int snd_usbmidi_in_endpoint_create(snd_usb_midi_t* umidi,
+					  snd_usb_midi_endpoint_info_t* ep_info,
+					  snd_usb_midi_endpoint_t* rep)
+{
+	snd_usb_midi_in_endpoint_t* ep;
+	usb_endpoint_descriptor_t* int_epd;
+	void* buffer;
+	unsigned int pipe;
+	int length, i, err;
+
+	rep->in = NULL;
+	ep = snd_magic_kcalloc(snd_usb_midi_in_endpoint_t, 0, GFP_KERNEL);
+	if (!ep)
+		return -ENOMEM;
+	ep->umidi = umidi;
+	ep->ep = rep;
+	for (i = 0; i < 0x10; ++i)
+		ep->ports[i].seq_port = -1;
+
+	if (umidi->quirk && umidi->quirk->type == QUIRK_MIDI_MIDIMAN)
+		int_epd = snd_usbmidi_get_midiman_int_epd(umidi);
+	else
+		int_epd = snd_usbmidi_get_int_epd(umidi, ep_info->epnum);
+
+	ep->urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!ep->urb) {
+		snd_usbmidi_in_endpoint_delete(ep);
+		return -ENOMEM;
+	}
+	if (int_epd)
+		pipe = usb_rcvintpipe(umidi->chip->dev, ep_info->epnum);
+	else
+		pipe = usb_rcvbulkpipe(umidi->chip->dev, ep_info->epnum);
+	length = usb_maxpacket(umidi->chip->dev, pipe, 0);
+	buffer = kmalloc(length, GFP_KERNEL);
+	if (!buffer) {
+		snd_usbmidi_in_endpoint_delete(ep);
+		return -ENOMEM;
+	}
+	if (int_epd)
+		FILL_INT_URB(ep->urb, umidi->chip->dev, pipe, buffer, length,
+			     snd_usbmidi_in_urb_complete, ep, int_epd->bInterval);
+	else
+		FILL_BULK_URB(ep->urb, umidi->chip->dev, pipe, buffer, length,
+			      snd_usbmidi_in_urb_complete, ep);
+
+	for (i = 0; i < 0x10; ++i)
+		if (ep_info->in_cables & (1 << i)) {
+			err = snd_midi_event_new(INPUT_BUFFER_SIZE,
+				 		 &ep->ports[i].midi_event);
+			if (err < 0) {
+				snd_usbmidi_in_endpoint_delete(ep);
+				return -ENOMEM;
+			}
+		}
+
+	rep->in = ep;
+	return 0;
+}
+
+static int snd_usbmidi_count_bits(uint16_t x)
+{
+	int i, bits = 0;
+
+	for (i = 0; i < 16; ++i)
+		bits += (x & (1 << i)) != 0;
+	return bits;
+}
+
+/*
+ * Frees an output endpoint.
+ * May be called when ep hasn't been initialized completely.
+ */
+static void snd_usbmidi_out_endpoint_delete(snd_usb_midi_out_endpoint_t* ep)
+{
+	if (ep->tasklet.func)
+		tasklet_kill(&ep->tasklet);
+	if (ep->urb) {
+		if (ep->urb->transfer_buffer) {
+			usb_unlink_urb(ep->urb);
+			kfree(ep->urb->transfer_buffer);
+		}
+		usb_free_urb(ep->urb);
+	}
+	snd_magic_kfree(ep);
+}
+
+/*
+ * Creates an output endpoint, and initializes output ports.
+ * ALSA ports are created later.
+ */
+static int snd_usbmidi_out_endpoint_create(snd_usb_midi_t* umidi,
+					   snd_usb_midi_endpoint_info_t* ep_info,
+			 		   snd_usb_midi_endpoint_t* rep)
+{
+	snd_usb_midi_out_endpoint_t* ep;
+	int i;
+	unsigned int pipe;
+	void* buffer;
+
+	rep->out = NULL;
+	ep = snd_magic_kcalloc(snd_usb_midi_out_endpoint_t, 0, GFP_KERNEL);
+	if (!ep)
+		return -ENOMEM;
+	ep->umidi = umidi;
+
+	ep->urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (!ep->urb) {
+		snd_usbmidi_out_endpoint_delete(ep);
+		return -ENOMEM;
+	}
+	pipe = usb_sndbulkpipe(umidi->chip->dev, ep_info->epnum);
+	ep->max_transfer = usb_maxpacket(umidi->chip->dev, pipe, 1) & ~3;
+	buffer = kmalloc(ep->max_transfer, GFP_KERNEL);
+	if (!buffer) {
+		snd_usbmidi_out_endpoint_delete(ep);
+		return -ENOMEM;
+	}
+	FILL_BULK_URB(ep->urb, umidi->chip->dev, pipe, buffer,
+		      ep->max_transfer, snd_usbmidi_out_urb_complete, ep);
+
+	spin_lock_init(&ep->buffer_lock);
+	tasklet_init(&ep->tasklet, snd_usbmidi_out_tasklet, (unsigned long)ep);
+
+	for (i = 0; i < 0x10; ++i)
+		if (ep_info->out_cables & (1 << i)) {
+			ep->ports[i].ep = ep;
+			ep->ports[i].cable = i << 4;
+		}
+
+	rep->out = ep;
+	return 0;
+}
+
+/*
+ * Frees the sequencer client, endpoints and ports.
+ */
+static int snd_usbmidi_seq_device_delete(snd_seq_device_t* seq_device)
+{
+	snd_usb_midi_t* umidi;
+	int i, j;
+
+	umidi = (snd_usb_midi_t*)SNDRV_SEQ_DEVICE_ARGPTR(seq_device);
+
+	if (umidi->seq_client >= 0) {
+		snd_seq_delete_kernel_client(umidi->seq_client);
+		umidi->seq_client = -1;
+	}
+	for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) {
+		snd_usb_midi_endpoint_t* ep = &umidi->endpoints[i];
+		if (ep->out) {
+			snd_usbmidi_out_endpoint_delete(ep->out);
+			ep->out = NULL;
+		}
+		if (ep->in) {
+			snd_usbmidi_in_endpoint_delete(ep->in);
+			ep->in = NULL;
+		}
+		for (j = 0; j < 0x10; ++j)
+			if (ep->rmidi[j]) {
+				snd_device_free(umidi->chip->card, ep->rmidi[j]);
+				ep->rmidi[j] = NULL;
+			}
+	}
+	return 0;
+}
+
+/*
+ * Creates a sequencer port for an input/output cable pair.
+ */
+static int snd_usbmidi_create_port(snd_usb_midi_t* umidi,
+				   snd_usb_midi_out_endpoint_t* out_ep,
+				   snd_usb_midi_in_endpoint_t* in_ep,
+				   int cable, int port_idx)
+{
+	int cap, type, port;
+	snd_seq_port_callback_t port_callback;
+	char port_name[48];
+
+	cap = 0;
+	memset(&port_callback, 0, sizeof(port_callback));
+	port_callback.owner = THIS_MODULE;
+	if (out_ep) {
+		port_callback.event_input = snd_usbmidi_event_input;
+		port_callback.private_data = &out_ep->ports[cable];
+		cap |= SNDRV_SEQ_PORT_CAP_WRITE | SNDRV_SEQ_PORT_CAP_SUBS_WRITE;
+	}
+	if (in_ep) {
+		cap |= SNDRV_SEQ_PORT_CAP_READ | SNDRV_SEQ_PORT_CAP_SUBS_READ;
+	}
+	if (out_ep && in_ep) {
+		cap |= SNDRV_SEQ_PORT_CAP_DUPLEX;
+	}
+	/* TODO: read type bits from element descriptor */
+	type = SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC;
+	/* TODO: read port name from jack descriptor */
+	snprintf(port_name, sizeof(port_name), "%s Port %d",
+		 umidi->chip->card->shortname, port_idx);
+	port = snd_seq_event_port_attach(umidi->seq_client, &port_callback,
+					 cap, type, port_name);
+	if (port < 0) {
+		snd_printk(KERN_ERR "cannot create port (error code %d)\n", port);
+		return port;
+	}
+	if (in_ep)
+		in_ep->ports[cable].seq_port = port;
+	return port;
+}
+
+/*
+ * Creates a virmidi port emulating rawmidi for the sequencer port.
+ */
+static int snd_usbmidi_create_virmidi(snd_usb_midi_t* umidi, int port,
+				      int port_idx, snd_rawmidi_t** rrmidi)
+{
+	snd_rawmidi_t *rmidi;
+	snd_virmidi_dev_t *rdev;
+	int err;
+
+	*rrmidi = NULL;
+	err = snd_virmidi_new(umidi->chip->card, port_idx, &rmidi);
+	if (err < 0)
+		return err;
+	sprintf(rmidi->name, "%s MIDI %d", umidi->chip->card->shortname, port_idx);
+	rdev = snd_magic_cast(snd_virmidi_dev_t, rmidi->private_data, return -ENXIO);
+	rdev->seq_mode = SNDRV_VIRMIDI_SEQ_ATTACH;
+	rdev->client = umidi->seq_client;
+	rdev->port = port;
+	err = snd_device_register(umidi->chip->card, rmidi);
+	if (err < 0) {
+		snd_device_free(umidi->chip->card, rmidi);
+		return err;
+	}
+	*rrmidi = rmidi;
+	return 0;
+}
+
+/*
+ * After input and output endpoints have been initialized, create
+ * the ALSA port for each input/output port pair in the endpoint.
+ * *port_idx is the port number, which must be unique over all endpoints.
+ */
+static int snd_usbmidi_create_endpoint_ports(snd_usb_midi_t* umidi,
+					     snd_usb_midi_endpoint_t* endpoint,
+					     int* port_idx,
+					     snd_usb_midi_endpoint_info_t* ep_info)
+{
+	int cable;
+
+	for (cable = 0; cable < 0x10; ++cable) {
+		int port, err;
+		int out = ep_info->out_cables & (1 << cable);
+		int in = ep_info->in_cables & (1 << cable);
+		if (!(in || out))
+			continue;
+
+		port = snd_usbmidi_create_port(umidi,
+					       out ? endpoint->out : NULL,
+					       in ? endpoint->in : NULL,
+					       cable, *port_idx);
+		if (port < 0)
+			return port;
+
+		if (*port_idx < SNDRV_MINOR_RAWMIDIS) {
+			err = snd_usbmidi_create_virmidi(umidi, port, *port_idx,
+			      				 &endpoint->rmidi[cable]);
+			if (err < 0)
+				return err;
+		}
+		++*port_idx;
+	}
+	return 0;
+}
+
+/*
+ * Creates the endpoints and their ports.
+ */
+static int snd_usbmidi_create_endpoints(snd_usb_midi_t* umidi,
+					snd_usb_midi_endpoint_info_t* endpoints)
+{
+	int i, err, port_idx = 0;
+
+	for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) {
+		if (!endpoints[i].epnum)
+			continue;
+		if (endpoints[i].out_cables) {
+			err = snd_usbmidi_out_endpoint_create(umidi, &endpoints[i],
+							      &umidi->endpoints[i]);
+			if (err < 0)
+				return err;
+		}
+		if (endpoints[i].in_cables) {
+			err = snd_usbmidi_in_endpoint_create(umidi, &endpoints[i],
+							     &umidi->endpoints[i]);
+			if (err < 0)
+				return err;
+		}
+		err = snd_usbmidi_create_endpoint_ports(umidi, &umidi->endpoints[i],
+							&port_idx, &endpoints[i]);
+		if (err < 0)
+			return err;
+		printk(KERN_INFO "snd-usb-midi: endpoint %d: created %d output and %d input ports\n",
+		       endpoints[i].epnum,
+		       snd_usbmidi_count_bits(endpoints[i].out_cables),
+		       snd_usbmidi_count_bits(endpoints[i].in_cables));
+	}
+	return 0;
+}
+
+/*
+ * Returns MIDIStreaming device capabilities.
+ */
+static int snd_usbmidi_get_ms_info(snd_usb_midi_t* umidi,
+			   	   snd_usb_midi_endpoint_info_t* endpoints)
+{
+	usb_interface_t* intf;
+	usb_interface_descriptor_t* intfd;
+	usb_ms_header_descriptor_t* ms_header;
+	usb_endpoint_descriptor_t* ep;
+	usb_ms_endpoint_descriptor_t* ms_ep;
+	int i, epidx;
+
+	intf = umidi->iface;
+	if (!intf)
+		return -ENXIO;
+	intfd = &intf->altsetting[0];
+	ms_header = (usb_ms_header_descriptor_t*)intfd->extra;
+	if (intfd->extralen >= 7 &&
+	    ms_header->bLength >= 7 &&
+	    ms_header->bDescriptorType == USB_DT_CS_INTERFACE &&
+	    ms_header->bDescriptorSubtype == HEADER)
+		printk(KERN_INFO "snd-usb-midi: MIDIStreaming version %02x.%02x\n",
+		       ms_header->bcdMSC[1], ms_header->bcdMSC[0]);
+	else
+		printk(KERN_WARNING "snd-usb-midi: MIDIStreaming interface descriptor not found\n");
+
+	epidx = 0;
+	for (i = 0; i < intfd->bNumEndpoints; ++i) {
+		ep = &intfd->endpoint[i];
+		if ((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_BULK)
+			continue;
+		ms_ep = (usb_ms_endpoint_descriptor_t*)ep->extra;
+		if (ep->extralen < 4 ||
+		    ms_ep->bLength < 4 ||
+		    ms_ep->bDescriptorType != USB_DT_CS_ENDPOINT ||
+		    ms_ep->bDescriptorSubtype != MS_GENERAL)
+			continue;
+		if (endpoints[epidx].epnum != 0 &&
+		    endpoints[epidx].epnum != (ep->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK)) {
+			++epidx;
+			if (epidx >= MIDI_MAX_ENDPOINTS) {
+				printk(KERN_WARNING "snd-usb-midi: too many endpoints\n");
+				break;
+			}
+		}
+		endpoints[epidx].epnum = ep->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
+		if (ep->bEndpointAddress & USB_DIR_IN) {
+			endpoints[epidx].in_cables = (1 << ms_ep->bNumEmbMIDIJack) - 1;
+		} else {
+			endpoints[epidx].out_cables = (1 << ms_ep->bNumEmbMIDIJack) - 1;
+		}
+		printk(KERN_INFO "snd-usb-midi: detected %d %s jack(s) on endpoint %d\n",
+		       ms_ep->bNumEmbMIDIJack,
+		       ep->bEndpointAddress & USB_DIR_IN ? "input" : "output",
+		       endpoints[epidx].epnum);
+	}
+	return 0;
+}
+
+/*
+ * If the first endpoint isn't specified, use the first endpoint in the
+ * first alternate setting of the interface.
+ */
+static int snd_usbmidi_detect_endpoint(snd_usb_midi_t* umidi, 
+			       	       snd_usb_midi_endpoint_info_t* endpoint)
+{
+	usb_interface_t* intf;
+	usb_interface_descriptor_t* intfd;
+	usb_endpoint_descriptor_t* epd;
+
+	if (endpoint->epnum == -1) {
+		intf = umidi->iface;
+		if (!intf || intf->num_altsetting < 1)
+			return -ENOENT;
+		intfd = intf->altsetting;
+		if (intfd->bNumEndpoints < 1)
+			return -ENOENT;
+		epd = intfd->endpoint;
+		endpoint->epnum = epd->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
+	}
+	return 0;
+}
+
+/*
+ * Detects the endpoints and ports of Yamaha devices.
+ */
+static int snd_usbmidi_detect_yamaha(snd_usb_midi_t* umidi, 
+				     snd_usb_midi_endpoint_info_t* endpoint)
+{
+	usb_interface_t* intf;
+	usb_interface_descriptor_t* intfd;
+	uint8_t* cs_desc;
+
+	intf = umidi->iface;
+	if (!intf)
+		return -ENOENT;
+	intfd = intf->altsetting;
+	if (intfd->bNumEndpoints < 1)
+		return -ENOENT;
+
+	for (cs_desc = intfd->extra;
+	     cs_desc < intfd->extra + intfd->extralen && cs_desc[0] >= 2;
+	     cs_desc += cs_desc[0]) {
+		if (cs_desc[1] == CS_AUDIO_INTERFACE) {
+			if (cs_desc[2] == MIDI_IN_JACK)
+				endpoint->in_cables = (endpoint->in_cables << 1) | 1;
+			else if (cs_desc[2] == MIDI_OUT_JACK)
+				endpoint->out_cables = (endpoint->out_cables << 1) | 1;
+		}
+	}
+	if (!endpoint->in_cables && !endpoint->out_cables)
+		return -ENOENT;
+
+	endpoint->epnum = -1;
+	return snd_usbmidi_detect_endpoint(umidi, endpoint);
+}
+
+/*
+ * Creates the endpoints and their ports for Midiman devices.
+ */
+static int snd_usbmidi_create_endpoints_midiman(snd_usb_midi_t* umidi, int ports)
+{
+	snd_usb_midi_endpoint_info_t ep_info;
+	usb_interface_t* intf;
+	usb_interface_descriptor_t* intfd;
+	usb_endpoint_descriptor_t* epd;
+	int cable, err;
+
+	intf = umidi->iface;
+	if (!intf)
+		return -ENOENT;
+	intfd = intf->altsetting;
+	if (intfd->bNumEndpoints < (ports > 1 ? 5 : 3)) {
+		snd_printdd(KERN_ERR "not enough endpoints\n");
+		return -ENOENT;
+	}
+
+	epd = &intfd->endpoint[0];
+	if ((epd->bEndpointAddress & USB_ENDPOINT_DIR_MASK) != USB_DIR_IN ||
+	    (epd->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_INT) {
+		snd_printdd(KERN_ERR "endpoint[0] isn't interrupt\n");
+		return -ENXIO;
+	}
+	epd = &intfd->endpoint[2];
+	if ((epd->bEndpointAddress & USB_ENDPOINT_DIR_MASK) != USB_DIR_OUT ||
+	    (epd->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_BULK) {
+		snd_printdd(KERN_ERR "endpoint[2] isn't bulk output\n");
+		return -ENXIO;
+	}
+	if (ports > 1) {
+		epd = &intfd->endpoint[4];
+		if ((epd->bEndpointAddress & USB_ENDPOINT_DIR_MASK) != USB_DIR_OUT ||
+		    (epd->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_BULK) {
+			snd_printdd(KERN_ERR "endpoint[4] isn't bulk output\n");
+			return -ENXIO;
+		}
+	}
+
+	ep_info.epnum = intfd->endpoint[2].bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
+	ep_info.out_cables = 0x5555 & ((1 << ports) - 1);
+	err = snd_usbmidi_out_endpoint_create(umidi, &ep_info, &umidi->endpoints[0]);
+	if (err < 0)
+		return err;
+
+	ep_info.epnum = intfd->endpoint[0].bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
+	ep_info.in_cables = (1 << ports) - 1;
+	err = snd_usbmidi_in_endpoint_create(umidi, &ep_info, &umidi->endpoints[0]);
+	if (err < 0)
+		return err;
+	umidi->endpoints[0].in->urb->complete = snd_usbmidi_in_midiman_complete;
+
+	if (ports > 1) {
+		ep_info.epnum = intfd->endpoint[4].bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
+		ep_info.out_cables = 0xaaaa & ((1 << ports) - 1);
+		err = snd_usbmidi_out_endpoint_create(umidi, &ep_info, &umidi->endpoints[1]);
+		if (err < 0)
+			return err;
+	}
+
+	for (cable = 0; cable < ports; ++cable) {
+		int port = snd_usbmidi_create_port(umidi,
+		    				   umidi->endpoints[cable & 1].out,
+		    				   umidi->endpoints[0].in,
+		    				   cable, cable);
+		if (port < 0)
+			return port;
+
+		if (cable < SNDRV_MINOR_RAWMIDIS) {
+			int err = snd_usbmidi_create_virmidi(umidi, port, cable,
+							     &umidi->endpoints[0].rmidi[cable]);
+			if (err < 0)
+				return err;
+		}
+	}
+	return 0;
+}
+
+/*
+ * Initialize the sequencer device.
+ */
+static int snd_usbmidi_seq_device_new(snd_seq_device_t* seq_device)
+{
+	snd_usb_midi_t* umidi;
+	usb_device_t* dev;
+	snd_seq_client_callback_t client_callback;
+	snd_seq_client_info_t client_info;
+	snd_usb_midi_endpoint_info_t endpoints[MIDI_MAX_ENDPOINTS];
+	int i, err;
+
+	umidi = (snd_usb_midi_t*)SNDRV_SEQ_DEVICE_ARGPTR(seq_device);
+
+	memset(&client_callback, 0, sizeof(client_callback));
+	client_callback.allow_output = 1;
+	client_callback.allow_input = 1;
+	umidi->seq_client = snd_seq_create_kernel_client(umidi->chip->card, 0,
+							 &client_callback);
+	if (umidi->seq_client < 0)
+		return umidi->seq_client;
+
+	/* set the client name */
+	memset(&client_info, 0, sizeof(client_info));
+	client_info.client = umidi->seq_client;
+	client_info.type = KERNEL_CLIENT;
+	dev = umidi->chip->dev;
+	if (dev->descriptor.iProduct)
+		err = usb_string(dev, dev->descriptor.iProduct,
+				 client_info.name, sizeof(client_info.name));
+	else
+		err = 0;
+	if (err <= 0) {
+		if (umidi->quirk && umidi->quirk->product_name) {
+			strncpy(client_info.name, umidi->quirk->product_name,
+				sizeof(client_info.name) - 1);
+			client_info.name[sizeof(client_info.name) - 1] = '\0';
+		} else {
+			sprintf(client_info.name, "USB Device %#04x:%#04x",
+				dev->descriptor.idVendor, dev->descriptor.idProduct);
+		}
+	}
+	snd_seq_kernel_client_ctl(umidi->seq_client,
+				  SNDRV_SEQ_IOCTL_SET_CLIENT_INFO,
+				  &client_info);
+
+	/* detect the endpoint(s) to use */
+	memset(endpoints, 0, sizeof(endpoints));
+	if (!umidi->quirk) {
+		err = snd_usbmidi_get_ms_info(umidi, endpoints);
+	} else {
+		switch (umidi->quirk->type) {
+		case QUIRK_MIDI_FIXED_ENDPOINT:
+			memcpy(&endpoints[0], umidi->quirk->data,
+			       sizeof(snd_usb_midi_endpoint_info_t));
+			err = snd_usbmidi_detect_endpoint(umidi, &endpoints[0]);
+			break;
+		case QUIRK_MIDI_YAMAHA:
+			err = snd_usbmidi_detect_yamaha(umidi, &endpoints[0]);
+			break;
+		case QUIRK_MIDI_MIDIMAN:
+			err = 0;
+			break;
+		default:
+			snd_printd(KERN_ERR "invalid quirk type %d\n", umidi->quirk->type);
+			err = -ENXIO;
+			break;
+		}
+	}
+
+	/* create ports */
+	if (err >= 0) {
+		if (umidi->quirk && umidi->quirk->type == QUIRK_MIDI_MIDIMAN)
+			err = snd_usbmidi_create_endpoints_midiman(umidi, (int)umidi->quirk->data);
+		else
+			err = snd_usbmidi_create_endpoints(umidi, endpoints);
+	}
+	if (err < 0) {
+		snd_usbmidi_seq_device_delete(seq_device);
+		return err;
+	}
+
+	for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i)
+		if (umidi->endpoints[i].in)
+			snd_usbmidi_submit_urb(umidi->endpoints[i].in->urb,
+					       GFP_KERNEL);
+	return 0;
+}
+
+static int __init snd_usbmidi_module_init(void)
+{
+	static snd_seq_dev_ops_t ops = {
+		snd_usbmidi_seq_device_new,
+		snd_usbmidi_seq_device_delete
+	};
+
+	return snd_seq_device_register_driver(SNDRV_SEQ_DEV_ID_USBMIDI, &ops,
+	 				      sizeof(snd_usb_midi_t));
+}
+
+static void __exit snd_usbmidi_module_exit(void)
+{
+	snd_seq_device_unregister_driver(SNDRV_SEQ_DEV_ID_USBMIDI);
+}
+
+module_init(snd_usbmidi_module_init)
+module_exit(snd_usbmidi_module_exit)
diff -Nru a/sound/usb/usbmixer.c b/sound/usb/usbmixer.c
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/sound/usb/usbmixer.c	Mon Oct  7 11:25:40 2002
@@ -0,0 +1,1314 @@
+/*
+ *   (Tentative) USB Audio Driver for ALSA
+ *
+ *   Mixer control part
+ *
+ *   Copyright (c) 2002 by Takashi Iwai <tiwai@suse.de>
+ *
+ *   Many codes borrowed from audio.c by 
+ *	    Alan Cox (alan@lxorguk.ukuu.org.uk)
+ *	    Thomas Sailer (sailer@ife.ee.ethz.ch)
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <linux/bitops.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/usb.h>
+#include <sound/core.h>
+#include <sound/control.h>
+
+#include "usbaudio.h"
+
+
+/*
+ */
+
+typedef struct usb_mixer_build mixer_build_t;
+typedef struct usb_audio_term usb_audio_term_t;
+typedef struct usb_mixer_elem_info usb_mixer_elem_info_t;
+
+
+struct usb_audio_term {
+	int id;
+	int type;
+	int channels;
+	unsigned int chconfig;
+	int name;
+};
+
+struct usb_mixer_build {
+	snd_usb_audio_t *chip;
+	unsigned char *buffer;
+	unsigned int buflen;
+	unsigned int ctrlif;
+	DECLARE_BITMAP(unitbitmap, 32*32);
+	usb_audio_term_t oterm;
+};
+
+struct usb_mixer_elem_info {
+	snd_usb_audio_t *chip;
+	unsigned int ctrlif;
+	unsigned int id;
+	unsigned int control;
+	unsigned int cmask; /* channel mask bitmap: 0 = master */ 
+	int channels;
+	int val_type;
+	int min, max;
+	unsigned int initialized: 1;
+};
+
+
+enum {
+	USB_FEATURE_MUTE = 0,
+	USB_FEATURE_VOLUME,
+	USB_FEATURE_BASS,
+	USB_FEATURE_MID,
+	USB_FEATURE_TREBLE,
+	USB_FEATURE_GEQ,
+	USB_FEATURE_AGC,
+	USB_FEATURE_DELAY,
+	USB_FEATURE_BASSBOOST,
+	FSB_FEATURE_LOUDNESS
+};
+
+enum {
+	USB_MIXER_BOOLEAN,
+	USB_MIXER_INV_BOOLEAN,
+	USB_MIXER_S8,
+	USB_MIXER_U8,
+	USB_MIXER_S16,
+	USB_MIXER_U16,
+};
+
+#define MAX_CHANNELS	10	/* max logical channels */
+
+
+/*
+ * find an audio control unit with the given unit id
+ */
+static void *find_audio_control_unit(mixer_build_t *state, unsigned char unit)
+{
+	unsigned char *p;
+
+	p = NULL;
+	while ((p = snd_usb_find_desc(state->buffer, state->buflen, p,
+				      USB_DT_CS_INTERFACE, state->ctrlif, -1)) != NULL) {
+		if (p[0] >= 4 && p[2] >= INPUT_TERMINAL && p[2] <= EXTENSION_UNIT && p[3] == unit)
+			return p;
+	}
+	return NULL;
+}
+
+
+/*
+ * copy a string with the given id
+ */
+static int snd_usb_copy_string_desc(mixer_build_t *state, int index, char *buf, int maxlen)
+{
+	int len = usb_string(state->chip->dev, index, buf, maxlen - 1);
+	buf[len] = 0;
+	return len;
+}
+
+/*
+ * convert from the byte/word on usb descriptor to the zero-based integer
+ */
+static int convert_signed_value(usb_mixer_elem_info_t *cval, int val)
+{
+	switch (cval->val_type) {
+	case USB_MIXER_BOOLEAN:
+		return !!val;
+	case USB_MIXER_INV_BOOLEAN:
+		return !val;
+	case USB_MIXER_U8:
+		val &= 0xff;
+		break;
+	case USB_MIXER_S8:
+		val &= 0xff;
+		if (val >= 0x80)
+			val -= 0x100;
+		break;
+	case USB_MIXER_U16:
+		val &= 0xffff;
+		break;
+	case USB_MIXER_S16:
+		val &= 0xffff;
+		if (val >= 0x8000)
+			val -= 0x10000;
+		break;
+	}
+	return val;
+}
+
+/*
+ * convert from the zero-based int to the byte/word for usb descriptor
+ */
+static int convert_bytes_value(usb_mixer_elem_info_t *cval, int val)
+{
+	switch (cval->val_type) {
+	case USB_MIXER_BOOLEAN:
+		return !!val;
+	case USB_MIXER_INV_BOOLEAN:
+		return !val;
+	case USB_MIXER_S8:
+	case USB_MIXER_U8:
+		return val & 0xff;
+	case USB_MIXER_S16:
+	case USB_MIXER_U16:
+		return val & 0xffff;
+	}
+	return 0; /* not reached */
+}
+
+static int get_relative_value(usb_mixer_elem_info_t *cval, int val)
+{
+	if (val < cval->min)
+		return 0;
+	else if (val > cval->max)
+		return cval->max - cval->min;
+	else
+		return val - cval->min;
+}
+
+static int get_abs_value(usb_mixer_elem_info_t *cval, int val)
+{
+	if (val < 0)
+		return cval->min;
+	val += cval->min;
+	if (val > cval->max)
+		return cval->max;
+	return val;
+}
+
+
+/*
+ * retrieve a mixer value
+ */
+
+static int get_ctl_value(usb_mixer_elem_info_t *cval, int request, int validx, int *value_ret)
+{
+	unsigned char buf[2];
+	int val_len = cval->val_type >= USB_MIXER_S16 ? 2 : 1;
+ 
+	if (usb_control_msg(cval->chip->dev, usb_rcvctrlpipe(cval->chip->dev, 0),
+			    request,
+			    USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
+			    validx, cval->ctrlif | (cval->id << 8),
+			    buf, val_len, HZ) < 0)
+		return -EINVAL;
+	*value_ret = convert_signed_value(cval, snd_usb_combine_bytes(buf, val_len));
+	return 0;
+}
+
+static int get_cur_ctl_value(usb_mixer_elem_info_t *cval, int validx, int *value)
+{
+	return get_ctl_value(cval, GET_CUR, validx, value);
+}
+
+/* channel = 0: master, 1 = first channel */
+inline static int get_cur_mix_value(usb_mixer_elem_info_t *cval, int channel, int *value)
+{
+	return get_ctl_value(cval, GET_CUR, ((cval->control + 1) << 8) | channel, value);
+}
+
+/*
+ * set a mixer value
+ */
+
+static int set_ctl_value(usb_mixer_elem_info_t *cval, int request, int validx, int value_set)
+{
+	unsigned char buf[2];
+	int val_len = cval->val_type >= USB_MIXER_S16 ? 2 : 1;
+ 
+	value_set = convert_bytes_value(cval, value_set);
+	buf[0] = value_set & 0xff;
+	buf[1] = (value_set >> 8) & 0xff;
+	return usb_control_msg(cval->chip->dev, usb_sndctrlpipe(cval->chip->dev, 0),
+			       request,
+			       USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT,
+			       validx, cval->ctrlif | (cval->id << 8),
+			       buf, val_len, HZ);
+}
+
+static int set_cur_ctl_value(usb_mixer_elem_info_t *cval, int validx, int value)
+{
+	return set_ctl_value(cval, SET_CUR, validx, value);
+}
+
+inline static int set_cur_mix_value(usb_mixer_elem_info_t *cval, int channel, int value)
+{
+	return set_ctl_value(cval, SET_CUR, ((cval->control + 1) << 8) | channel, value);
+}
+
+
+/*
+ * parser routines begin here... 
+ */
+
+static int parse_audio_unit(mixer_build_t *state, int unitid);
+
+
+/*
+ * check if the input/output channel routing is enabled on the given bitmap.
+ * used for mixer unit parser
+ */
+static int check_matrix_bitmap(unsigned char *bmap, int ich, int och, int num_outs)
+{
+	int idx = ich * num_outs + och;
+	return bmap[-(idx >> 3)] & (0x80 >> (idx & 7));
+}
+
+
+/*
+ * add an alsa control element
+ * search and increment the index until an empty slot is found.
+ *
+ * if failed, give up and free the control instance.
+ */
+
+static int add_control_to_empty(snd_card_t *card, snd_kcontrol_t *kctl)
+{
+	int err;
+	while (snd_ctl_find_id(card, &kctl->id))
+		kctl->id.index++;
+	if ((err = snd_ctl_add(card, kctl)) < 0) {
+		snd_printk(KERN_ERR "cannot add control\n");
+		snd_ctl_free_one(kctl);
+	}
+	return err;
+}
+
+
+/*
+ * get a terminal name string
+ */
+
+static struct iterm_name_combo {
+	int type;
+	char *name;
+} iterm_names[] = {
+	{ 0x0300, "Output" },
+	{ 0x0301, "Speaker" },
+	{ 0x0302, "Headphone" },
+	{ 0x0303, "HMD Audio" },
+	{ 0x0304, "Desktop Speaker" },
+	{ 0x0305, "Room Speaker" },
+	{ 0x0306, "Com Speaker" },
+	{ 0x0307, "LFE" },
+	{ 0x0600, "External In" },
+	{ 0x0601, "Analog In" },
+	{ 0x0602, "Digital In" },
+	{ 0x0603, "Line" },
+	{ 0x0604, "Legacy In" },
+	{ 0x0605, "IEC958 In" },
+	{ 0x0606, "1394 DA Stream" },
+	{ 0x0607, "1394 DV Stream" },
+	{ 0x0700, "Embedded" },
+	{ 0x0701, "Noise Source" },
+	{ 0x0702, "Equalization Noise" },
+	{ 0x0703, "CD" },
+	{ 0x0704, "DAT" },
+	{ 0x0705, "DCC" },
+	{ 0x0706, "MiniDisk" },
+	{ 0x0707, "Analog Tape" },
+	{ 0x0708, "Phonograph" },
+	{ 0x0709, "VCR Audio" },
+	{ 0x070a, "Video Disk Audio" },
+	{ 0x070b, "DVD Audio" },
+	{ 0x070c, "TV Tuner Audio" },
+	{ 0x070d, "Satellite Rec Audio" },
+	{ 0x070e, "Cable Tuner Audio" },
+	{ 0x070f, "DSS Audio" },
+	{ 0x0710, "Radio Receiver" },
+	{ 0x0711, "Radio Transmitter" },
+	{ 0x0712, "Multi-Track Recorder" },
+	{ 0x0713, "Synthesizer" },
+	{ 0 },
+};	
+
+static int get_term_name(mixer_build_t *state, usb_audio_term_t *iterm,
+			 unsigned char *name, int maxlen, int term_only)
+{
+	struct iterm_name_combo *names;
+
+	if (iterm->name)
+		return snd_usb_copy_string_desc(state, iterm->name, name, maxlen);
+
+	/* virtual type - not a real terminal */
+	if (iterm->type >> 16) {
+		if (term_only)
+			return 0;
+		switch (iterm->type >> 16) {
+		case SELECTOR_UNIT:
+			strcpy(name, "Selector"); return 8;
+		case PROCESSING_UNIT:
+			strcpy(name, "Process Unit"); return 12;
+		case EXTENSION_UNIT:
+			strcpy(name, "Ext Unit"); return 8;
+		case MIXER_UNIT:
+			strcpy(name, "Mixer"); return 5;
+		default:
+			return sprintf(name, "Unit %d", iterm->id);
+		}
+	}
+
+	switch (iterm->type & 0xff00) {
+	case 0x0100:
+		strcpy(name, "PCM"); return 3;
+	case 0x0200:
+		strcpy(name, "Mic"); return 3;
+	case 0x0400:
+		strcpy(name, "Headset"); return 7;
+	case 0x0500:
+		strcpy(name, "Phone"); return 5;
+	}
+
+	for (names = iterm_names; names->type; names++)
+		if (names->type == iterm->type) {
+			strcpy(name, names->name);
+			return strlen(names->name);
+		}
+	return 0;
+}
+
+
+/*
+ * parse the source unit recursively until it reaches to a terminal
+ * or a branched unit.
+ */
+static int check_input_term(mixer_build_t *state, int id, usb_audio_term_t *term)
+{
+	unsigned char *p1;
+
+	memset(term, 0, sizeof(*term));
+	while ((p1 = find_audio_control_unit(state, id)) != NULL) {
+		term->id = id;
+		switch (p1[2]) {
+		case INPUT_TERMINAL:
+			term->type = combine_word(p1 + 4);
+			term->channels = p1[7];
+			term->chconfig = combine_word(p1 + 8);
+			term->name = p1[11];
+			return 0;
+		case FEATURE_UNIT:
+			id = p1[4];
+			break; /* continue to parse */
+		case MIXER_UNIT:
+			term->type = p1[2] << 16; /* virtual type */
+			term->channels = p1[5 + p1[4]];
+			term->chconfig = combine_word(p1 + 6 + p1[4]);
+			term->name = p1[p1[0] - 1];
+			return 0;
+		case SELECTOR_UNIT:
+			/* call recursively to retrieve the channel info */
+			if (check_input_term(state, p1[5], term) < 0)
+				return -ENODEV;
+			term->type = p1[2] << 16; /* virtual type */
+			term->id = id;
+			term->name = p1[9 + p1[0] - 1];
+			return 0;
+		case PROCESSING_UNIT:
+		case EXTENSION_UNIT:
+			if (p1[6] == 1) {
+				id = p1[7];
+				break; /* continue to parse */
+			}
+			term->type = p1[2] << 16; /* virtual type */
+			term->channels = p1[7 + p1[6]];
+			term->chconfig = combine_word(p1 + 8 + p1[6]);
+			term->name = p1[12 + p1[6] + p1[11 + p1[6]]];
+			return 0;
+		default:
+			return -ENODEV;
+		}
+	}
+	return -ENODEV;
+}
+
+
+/*
+ * Feature Unit
+ */
+
+/* feature unit control information */
+struct usb_feature_control_info {
+	const char *name;
+	unsigned int type;	/* control type (mute, volume, etc.) */
+};
+
+static struct usb_feature_control_info audio_feature_info[] = {
+	{ "Mute",		USB_MIXER_INV_BOOLEAN },
+	{ "Volume",		USB_MIXER_S16 },
+	{ "Tone Control - Bass",	USB_MIXER_S8 },
+	{ "Tone Control - Mid",		USB_MIXER_S8 },
+	{ "Tone Control - Treble",	USB_MIXER_S8 },
+	{ "Graphic Equalizer",		USB_MIXER_S8 }, /* FIXME: not implemeted yet */
+	{ "Auto Gain Control",	USB_MIXER_BOOLEAN },
+	{ "Delay Control",	USB_MIXER_U16 },
+	{ "Bass Boost",		USB_MIXER_BOOLEAN },
+	{ "Loudness",		USB_MIXER_BOOLEAN },
+};
+
+
+/* private_free callback */
+static void usb_mixer_elem_free(snd_kcontrol_t *kctl)
+{
+	if (kctl->private_data) {
+		snd_magic_kfree((void *)kctl->private_data);
+		kctl->private_data = 0;
+	}
+}
+
+
+/*
+ * interface to ALSA control for feature/mixer units
+ */
+
+/* get a feature/mixer unit info */
+static int mixer_ctl_feature_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{	
+	usb_mixer_elem_info_t *cval = snd_magic_cast(usb_mixer_elem_info_t, kcontrol->private_data, return -EINVAL);
+
+	if (cval->val_type == USB_MIXER_BOOLEAN ||
+	    cval->val_type == USB_MIXER_INV_BOOLEAN)
+		uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+	else
+		uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = cval->channels;
+	if (cval->val_type == USB_MIXER_BOOLEAN ||
+	    cval->val_type == USB_MIXER_INV_BOOLEAN) {
+		uinfo->value.integer.min = 0;
+		uinfo->value.integer.max = 1;
+	} else {
+		if (! cval->initialized) {
+			int minchn = 0;
+			if (cval->cmask) {
+				int i;
+				for (i = 0; i < MAX_CHANNELS; i++)
+					if (cval->cmask & (1 << i)) {
+						minchn = i + 1;
+						break;
+					}
+			}
+			if (get_ctl_value(cval, GET_MAX, ((cval->control+1) << 8) | minchn, &cval->max) < 0 ||
+			    get_ctl_value(cval, GET_MIN, ((cval->control+1) << 8) | minchn, &cval->min) < 0) {
+				snd_printk(KERN_ERR "%d:%d: cannot get min/max values for control %d\n", cval->id, cval->ctrlif, cval->control);
+				return -EINVAL;
+			}
+			cval->initialized = 1;
+		}
+		uinfo->value.integer.min = 0;
+		uinfo->value.integer.max = cval->max - cval->min;
+	}
+	return 0;
+}
+
+/* get the current value from feature/mixer unit */
+static int mixer_ctl_feature_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	usb_mixer_elem_info_t *cval = snd_magic_cast(usb_mixer_elem_info_t, kcontrol->private_data, return -EINVAL);
+	int c, cnt, val, err;
+
+	if (cval->cmask) {
+		cnt = 0;
+		for (c = 0; c < MAX_CHANNELS; c++) {
+			if (cval->cmask & (1 << c)) {
+				err = get_cur_mix_value(cval, c + 1, &val);
+				if (err < 0) {
+					printk("cannot get current value for control %d ch %d: err = %d\n", cval->control, c + 1, err);
+					return err;
+				}
+				val = get_relative_value(cval, val);
+				ucontrol->value.integer.value[cnt] = val;
+				cnt++;
+			}
+		}
+	} else {
+		/* master channel */
+		err = get_cur_mix_value(cval, 0, &val);
+		if (err < 0) {
+			printk("cannot get current value for control %d master ch: err = %d\n", cval->control, err);
+			return err;
+		}
+		val = get_relative_value(cval, val);
+		ucontrol->value.integer.value[0] = val;
+	}
+	return 0;
+}
+
+/* put the current value to feature/mixer unit */
+static int mixer_ctl_feature_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	usb_mixer_elem_info_t *cval = snd_magic_cast(usb_mixer_elem_info_t, kcontrol->private_data, return -EINVAL);
+	int c, cnt, val, oval, err;
+	int changed = 0;
+
+	if (cval->cmask) {
+		cnt = 0;
+		for (c = 0; c < MAX_CHANNELS; c++) {
+			if (cval->cmask & (1 << c)) {
+				err = get_cur_mix_value(cval, c + 1, &oval);
+				if (err < 0)
+					return err;
+				val = ucontrol->value.integer.value[cnt];
+				val = get_abs_value(cval, val);
+				if (oval != val) {
+					set_cur_mix_value(cval, c + 1, val);
+					changed = 1;
+				}
+				cnt++;
+			}
+		}
+	} else {
+		/* master channel */
+		err = get_cur_mix_value(cval, 0, &oval);
+		if (err < 0)
+			return err;
+		val = ucontrol->value.integer.value[0];
+		val = get_abs_value(cval, val);
+		if (val != oval) {
+			set_cur_mix_value(cval, 0, val);
+			changed = 1;
+		}
+	}
+	return changed;
+}
+
+static snd_kcontrol_new_t usb_feature_unit_ctl = {
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "", /* will be filled later manually */
+	.info = mixer_ctl_feature_info,
+	.get = mixer_ctl_feature_get,
+	.put = mixer_ctl_feature_put,
+};
+
+
+/*
+ * build a feature control
+ */
+
+static void build_feature_ctl(mixer_build_t *state, unsigned char *desc,
+			      unsigned int ctl_mask, int control,
+			      usb_audio_term_t *iterm, int unitid)
+{
+	int len = 0;
+	int nameid = desc[desc[0] - 1];
+	snd_kcontrol_t *kctl;
+	usb_mixer_elem_info_t *cval;
+	int minchn = 0;
+
+	if (control == USB_FEATURE_GEQ) {
+		/* FIXME: not supported yet */
+		return;
+	}
+
+	cval = snd_magic_kcalloc(usb_mixer_elem_info_t, 0, GFP_KERNEL);
+	if (! cval) {
+		snd_printk(KERN_ERR "cannot malloc kcontrol");
+		return;
+	}
+	cval->chip = state->chip;
+	cval->ctrlif = state->ctrlif;
+	cval->id = unitid;
+	cval->control = control;
+	cval->cmask = ctl_mask;
+	cval->val_type = audio_feature_info[control].type;
+	if (ctl_mask == 0)
+		cval->channels = 1;	/* master channel */
+	else {
+		int i, c = 0;
+		for (i = 0; i < 16; i++)
+			if (ctl_mask & (1 << i)) {
+				if (! minchn)
+					minchn = i + 1;
+				c++;
+			}
+		cval->channels = c;
+	}
+
+	/* get min/max values */
+	if (cval->val_type == USB_MIXER_BOOLEAN ||
+	    cval->val_type == USB_MIXER_INV_BOOLEAN) {
+		cval->max = 1;
+		cval->initialized = 1;
+	} else {
+		if (get_ctl_value(cval, GET_MAX, ((cval->control+1) << 8) | minchn, &cval->max) < 0 ||
+		    get_ctl_value(cval, GET_MIN, ((cval->control+1) << 8) | minchn, &cval->min) < 0)
+			snd_printk(KERN_ERR "%d:%d: cannot get min/max values for control %d\n", cval->id, cval->ctrlif, control);
+		else
+			cval->initialized = 1;
+	}
+
+	kctl = snd_ctl_new1(&usb_feature_unit_ctl, cval);
+	if (! kctl) {
+		snd_printk(KERN_ERR "cannot malloc kcontrol");
+		snd_magic_kfree(cval);
+		return;
+	}
+	kctl->private_free = usb_mixer_elem_free;
+
+	if (nameid)
+		len = snd_usb_copy_string_desc(state, nameid, kctl->id.name, sizeof(kctl->id.name));
+
+	switch (control) {
+	case USB_FEATURE_MUTE:
+	case USB_FEATURE_VOLUME:
+		/* determine the control name.  the rule is:
+		 * - if a name id is given in descriptor, use it.
+		 * - if the connected input can be determined, then use the name
+		 *   of terminal type.
+		 * - if the connected output can be determined, use it.
+		 * - otherwise, anonymous name.
+		 */
+		if (! nameid) {
+			len = get_term_name(state, iterm, kctl->id.name, sizeof(kctl->id.name), 1);
+			if (! len)
+				len = get_term_name(state, &state->oterm, kctl->id.name, sizeof(kctl->id.name), 1);
+			if (! len)
+				len = sprintf(kctl->id.name, "Feature %d", unitid);
+		}
+		/* determine the stream direction:
+		 * if the connected output is USB stream, then it's likely a
+		 * capture stream.  otherwise it should be playback (hopefully :)
+		 */
+		if (! (state->oterm.type >> 16)) {
+			if ((state->oterm.type & 0xff00) == 0x0100) {
+				strcpy(kctl->id.name + len, " Capture");
+				len += 8;
+			} else {
+				strcpy(kctl->id.name + len, " Playback");
+				len += 9;
+			}
+		}
+		strcpy(kctl->id.name + len, control == USB_FEATURE_MUTE ? " Switch" : " Volume");
+		break;
+
+	default:
+		if (! nameid)
+			strcpy(kctl->id.name, audio_feature_info[control].name);
+		break;
+	}
+
+	snd_printdd(KERN_INFO "[%d] FU [%s] ch = %d, val = %d/%d\n",
+		    cval->id, kctl->id.name, cval->channels, cval->min, cval->max);
+	add_control_to_empty(state->chip->card, kctl);
+}
+
+
+
+/*
+ * parse a feature unit
+ *
+ * most of controlls are defined here.
+ */
+static int parse_audio_feature_unit(mixer_build_t *state, int unitid, unsigned char *ftr)
+{
+	int channels, i, j;
+	usb_audio_term_t iterm;
+	unsigned int master_bits, first_ch_bits;
+	int err, csize;
+
+	if (ftr[0] < 7 || ! (csize = ftr[5]) || ftr[0] < 7 + csize) {
+		snd_printk(KERN_ERR "usbaudio: unit %u: invalid FEATURE_UNIT descriptor\n", unitid);
+		return -EINVAL;
+	}
+
+	/* parse the source unit */
+	if ((err = parse_audio_unit(state, ftr[4])) < 0)
+		return err;
+
+	/* determine the input source type and name */
+	if (check_input_term(state, ftr[4], &iterm) < 0)
+		return -EINVAL;
+
+	channels = (ftr[0] - 7) / csize - 1;
+
+	master_bits = snd_usb_combine_bytes(ftr + 6, csize);
+	if (channels > 0)
+		first_ch_bits = snd_usb_combine_bytes(ftr + 6 + csize, csize);
+	else
+		first_ch_bits = 0;
+	/* check all control types */
+	for (i = 0; i < 10; i++) {
+		unsigned int ch_bits = 0;
+		for (j = 0; j < channels; j++) {
+			unsigned int mask = snd_usb_combine_bytes(ftr + 6 + csize * (j+1), csize);
+			if (mask & (1 << i))
+				ch_bits |= (1 << j);
+		}
+		if (ch_bits & 1) /* the first channel must be set (for ease of programming) */
+			build_feature_ctl(state, ftr, ch_bits, i, &iterm, unitid);
+		if (master_bits & (1 << i))
+			build_feature_ctl(state, ftr, 0, i, &iterm, unitid);
+	}
+	
+	return 0;
+}
+
+
+/*
+ * Mixer Unit
+ */
+
+/*
+ * build a mixer unit control
+ *
+ * the callbacks are identical with feature unit.
+ * input channel number (zero based) is given in control field instead.
+ */
+
+static void build_mixer_unit_ctl(mixer_build_t *state, unsigned char *desc,
+				 int in_ch, int unitid)
+{
+	usb_mixer_elem_info_t *cval;
+	int num_ins = desc[4];
+	int num_outs = desc[5 + num_ins];
+	int i, len;
+	snd_kcontrol_t *kctl;
+	usb_audio_term_t iterm;
+	int minchn = 0;
+
+	cval = snd_magic_kcalloc(usb_mixer_elem_info_t, 0, GFP_KERNEL);
+	if (! cval)
+		return;
+
+	if (check_input_term(state, desc[5 + in_ch], &iterm) < 0)
+		return;
+
+	cval->chip = state->chip;
+	cval->ctrlif = state->ctrlif;
+	cval->id = unitid;
+	cval->control = in_ch;
+	cval->val_type = USB_MIXER_S16;
+	for (i = 0; i < num_outs; i++) {
+		if (check_matrix_bitmap(desc + 9 + num_ins, in_ch, i, num_outs)) {
+			cval->cmask |= (1 << i);
+			cval->channels++;
+			if (! minchn)
+				minchn = i + 1;
+		}
+	}
+
+	/* get min/max values */
+	if (get_ctl_value(cval, GET_MAX, ((in_ch+1) << 8) | minchn, &cval->max) < 0 ||
+	    get_ctl_value(cval, GET_MIN, ((in_ch+1) << 8) | minchn, &cval->min) < 0)
+		snd_printk(KERN_ERR "cannot get min/max values for mixer\n");
+	else
+		cval->initialized = 1;
+
+	kctl = snd_ctl_new1(&usb_feature_unit_ctl, cval);
+	if (! kctl) {
+		snd_printk(KERN_ERR "cannot malloc kcontrol");
+		snd_magic_kfree(cval);
+		return;
+	}
+	kctl->private_free = usb_mixer_elem_free;
+
+	len = get_term_name(state, &iterm, kctl->id.name, sizeof(kctl->id.name), 0);
+	if (! len)
+		len = sprintf(kctl->id.name, "Mixer Source %d", in_ch);
+	strcpy(kctl->id.name + len, " Volume");
+
+	snd_printdd(KERN_INFO "[%d] MU [%s] ch = %d, val = %d/%d\n",
+		    cval->id, kctl->id.name, cval->channels, cval->min, cval->max);
+	add_control_to_empty(state->chip->card, kctl);
+}
+
+
+/*
+ * parse a mixer unit
+ */
+static int parse_audio_mixer_unit(mixer_build_t *state, int unitid, unsigned char *desc)
+{
+	int num_ins, num_outs;
+	int i, err;
+	if (desc[0] < 12 || ! (num_ins = desc[4]) || ! (num_outs = desc[5 + num_ins]))
+		return -EINVAL;
+
+	for (i = 0; i < num_ins; i++) {
+		err = parse_audio_unit(state, desc[5 + i]);
+		if (err < 0)
+			return err;
+		if (check_matrix_bitmap(desc + 9 + num_ins, i, 0, num_outs))
+			build_mixer_unit_ctl(state, desc, i, unitid);
+	}
+	return 0;
+}
+
+
+/*
+ * Processing Unit / Extension Unit
+ */
+
+/* get callback for processing/extension unit */
+static int mixer_ctl_procunit_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	usb_mixer_elem_info_t *cval = snd_magic_cast(usb_mixer_elem_info_t, kcontrol->private_data, return -EINVAL);
+	int err, val;
+
+	err = get_cur_ctl_value(cval, cval->control, &val);
+	if (err < 0)
+		return err;
+	val = get_relative_value(cval, val);
+	ucontrol->value.integer.value[0] = val;
+	return 0;
+}
+
+/* put callback for processing/extension unit */
+static int mixer_ctl_procunit_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	usb_mixer_elem_info_t *cval = snd_magic_cast(usb_mixer_elem_info_t, kcontrol->private_data, return -EINVAL);
+	int val, oval, err;
+
+	err = get_cur_ctl_value(cval, cval->control, &oval);
+	if (err < 0)
+		return err;
+	val = ucontrol->value.integer.value[0];
+	val = get_abs_value(cval, val);
+	if (val != oval) {
+		set_cur_ctl_value(cval, cval->control, val);
+		return 1;
+	}
+	return 0;
+}
+
+/* alsa control interface for processing/extension unit */
+static snd_kcontrol_new_t mixer_procunit_ctl = {
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "", /* will be filled later */
+	.info = mixer_ctl_feature_info,
+	.get = mixer_ctl_procunit_get,
+	.put = mixer_ctl_procunit_put,
+};
+
+
+/*
+ * predefined data for processing units
+ */
+struct procunit_value_info {
+	int control;
+	char *suffix;
+	int val_type;
+};
+
+struct procunit_info {
+	int type;
+	char *name;
+	struct procunit_value_info *values;
+};
+
+static struct procunit_value_info updown_proc_info[] = {
+	{ 0x01, "Switch", USB_MIXER_BOOLEAN },
+	{ 0x02, "Mode Select", USB_MIXER_U8 },
+	{ 0 }
+};
+static struct procunit_value_info prologic_proc_info[] = {
+	{ 0x01, "Switch", USB_MIXER_BOOLEAN },
+	{ 0x02, "Mode Select", USB_MIXER_U8 },
+	{ 0 }
+};
+static struct procunit_value_info threed_enh_proc_info[] = {
+	{ 0x01, "Switch", USB_MIXER_BOOLEAN },
+	{ 0x02, "Spaciousness", USB_MIXER_U8 },
+	{ 0 }
+};
+static struct procunit_value_info reverb_proc_info[] = {
+	{ 0x01, "Switch", USB_MIXER_BOOLEAN },
+	{ 0x02, "Level", USB_MIXER_U8 },
+	{ 0x03, "Time", USB_MIXER_U16 },
+	{ 0x04, "Delay", USB_MIXER_U8 },
+	{ 0 }
+};
+static struct procunit_value_info chorus_proc_info[] = {
+	{ 0x01, "Switch", USB_MIXER_BOOLEAN },
+	{ 0x02, "Level", USB_MIXER_U8 },
+	{ 0x03, "Rate", USB_MIXER_U16 },
+	{ 0x04, "Depth", USB_MIXER_U16 },
+	{ 0 }
+};
+static struct procunit_value_info dcr_proc_info[] = {
+	{ 0x01, "Switch", USB_MIXER_BOOLEAN },
+	{ 0x02, "Ratio", USB_MIXER_U16 },
+	{ 0x03, "Max Amp", USB_MIXER_S16 },
+	{ 0x04, "Threshold", USB_MIXER_S16 },
+	{ 0x05, "Attack Time", USB_MIXER_U16 },
+	{ 0x06, "Release Time", USB_MIXER_U16 },
+	{ 0 }
+};
+
+static struct procunit_info procunits[] = {
+	{ 0x01, "Up Down", updown_proc_info },
+	{ 0x02, "Dolby Prologic", prologic_proc_info },
+	{ 0x03, "3D Stereo Extender", threed_enh_proc_info },
+	{ 0x04, "Reverb", reverb_proc_info },
+	{ 0x05, "Chorus", chorus_proc_info },
+	{ 0x06, "DCR", dcr_proc_info },
+	{ 0 },
+};
+
+/*
+ * build a processing/extension unit
+ */
+static int build_audio_procunit(mixer_build_t *state, int unitid, unsigned char *dsc, struct procunit_info *list, char *name)
+{
+	int num_ins = dsc[6];
+	usb_mixer_elem_info_t *cval;
+	snd_kcontrol_t *kctl;
+	int i, err, nameid, type;
+	struct procunit_info *info;
+	struct procunit_value_info *valinfo;
+	static struct procunit_value_info default_value_info[] = {
+		{ 0x01, "Switch", USB_MIXER_BOOLEAN },
+		{ 0 }
+	};
+	static struct procunit_info default_info = {
+		0, NULL, default_value_info
+	};
+
+	if (dsc[0] < 13 || dsc[0] < 13 + num_ins || dsc[0] < num_ins + dsc[11 + num_ins]) {
+		snd_printk(KERN_ERR "invalid %s descriptor %d\n", name, unitid);
+		return -EINVAL;
+	}
+
+	for (i = 0; i < num_ins; i++) {
+		if ((err = parse_audio_unit(state, dsc[7 + i])) < 0)
+			return err;
+	}
+
+	type = combine_word(&dsc[4]);
+	if (! type)
+		return 0; /* undefined? */
+
+	for (info = list; info && info->type; info++)
+		if (info->type == type)
+			break;
+	if (! info || ! info->type)
+		info = &default_info;
+
+	for (valinfo = info->values; valinfo->control; valinfo++) {
+		/* FIXME: bitmap might be longer than 8bit */
+		if (! (dsc[12 + num_ins] & (1 << (valinfo->control - 1))))
+			continue;
+		cval = snd_magic_kcalloc(usb_mixer_elem_info_t, 0, GFP_KERNEL);
+		if (! cval) {
+			snd_printk(KERN_ERR "cannot malloc kcontrol");
+			return -ENOMEM;
+		}
+		cval->chip = state->chip;
+		cval->ctrlif = state->ctrlif;
+		cval->id = unitid;
+		cval->control = valinfo->control;
+		cval->val_type = valinfo->val_type;
+		cval->channels = 1;
+
+		/* get min/max values */
+		if (get_ctl_value(cval, GET_MAX, cval->control, &cval->max) < 0 ||
+		    get_ctl_value(cval, GET_MIN, cval->control, &cval->min) < 0)
+			snd_printk(KERN_ERR "cannot get min/max values for proc/ext unit\n");
+		else
+			cval->initialized = 1;
+
+		kctl = snd_ctl_new1(&mixer_procunit_ctl, cval);
+		if (! kctl) {
+			snd_printk(KERN_ERR "cannot malloc kcontrol");
+			snd_magic_kfree(cval);
+			return -ENOMEM;
+		}
+		kctl->private_free = usb_mixer_elem_free;
+
+		if (info->name)
+			sprintf(kctl->id.name, "%s %s", info->name, valinfo->suffix);
+		else {
+			nameid = dsc[12 + num_ins + dsc[11 + num_ins]];
+			if (nameid) {
+				int len = snd_usb_copy_string_desc(state, nameid, kctl->id.name, sizeof(kctl->id.name));
+				strcpy(kctl->id.name + len, valinfo->suffix);
+			} else
+				sprintf(kctl->id.name, "%s %s", name, valinfo->suffix);
+		}
+
+		snd_printdd(KERN_INFO "[%d] PU [%s] ch = %d, val = %d/%d\n",
+			    cval->id, kctl->id.name, cval->channels, cval->min, cval->max);
+		if ((err = add_control_to_empty(state->chip->card, kctl)) < 0)
+			return err;
+	}
+	return 0;
+}
+
+
+static int parse_audio_processing_unit(mixer_build_t *state, int unitid, unsigned char *desc)
+{
+	return build_audio_procunit(state, unitid, desc, procunits, "Processing Unit");
+}
+
+static int parse_audio_extension_unit(mixer_build_t *state, int unitid, unsigned char *desc)
+{
+	return build_audio_procunit(state, unitid, desc, NULL, "Extension Unit");
+}
+
+
+/*
+ * Selector Unit
+ */
+
+/* info callback for selector unit
+ * use an enumerator type for routing
+ */
+static int mixer_ctl_selector_info(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t *uinfo)
+{	
+	usb_mixer_elem_info_t *cval = snd_magic_cast(usb_mixer_elem_info_t, kcontrol->private_data, return -EINVAL);
+	char **itemlist = (char **)kcontrol->private_value;
+
+	snd_assert(itemlist, return -EINVAL);
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	uinfo->count = 1;
+	uinfo->value.enumerated.items = cval->max;
+	if (uinfo->value.enumerated.item >= cval->max)
+		uinfo->value.enumerated.item = cval->max - 1;
+	strcpy(uinfo->value.enumerated.name, itemlist[uinfo->value.enumerated.item]);
+	return 0;
+}
+
+/* get callback for selector unit */
+static int mixer_ctl_selector_get(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	usb_mixer_elem_info_t *cval = snd_magic_cast(usb_mixer_elem_info_t, kcontrol->private_data, return -EINVAL);
+	int val, err;
+
+	err = get_cur_ctl_value(cval, 0, &val);
+	if (err < 0)
+		return err;
+	val = get_relative_value(cval, val);
+	ucontrol->value.enumerated.item[0] = val;
+	return 0;
+}
+
+/* put callback for selector unit */
+static int mixer_ctl_selector_put(snd_kcontrol_t *kcontrol, snd_ctl_elem_value_t *ucontrol)
+{
+	usb_mixer_elem_info_t *cval = snd_magic_cast(usb_mixer_elem_info_t, kcontrol->private_data, return -EINVAL);
+	int val, oval, err;
+
+	err = get_cur_ctl_value(cval, 0, &oval);
+	if (err < 0)
+		return err;
+	val = ucontrol->value.enumerated.item[0];
+	val = get_abs_value(cval, val);
+	if (val != oval) {
+		set_cur_ctl_value(cval, 0, val);
+		return 1;
+	}
+	return 0;
+}
+
+/* alsa control interface for selector unit */
+static snd_kcontrol_new_t mixer_selectunit_ctl = {
+	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "", /* will be filled later */
+	.info = mixer_ctl_selector_info,
+	.get = mixer_ctl_selector_get,
+	.put = mixer_ctl_selector_put,
+};
+
+
+/* private free callback.
+ * free both private_data and private_value
+ */
+static void usb_mixer_selector_elem_free(snd_kcontrol_t *kctl)
+{
+	int i, num_ins = 0;
+
+	if (kctl->private_data) {
+		usb_mixer_elem_info_t *cval = snd_magic_cast(usb_mixer_elem_info_t, kctl->private_data,);
+		num_ins = cval->max;
+		snd_magic_kfree(cval);
+		kctl->private_data = 0;
+	}
+	if (kctl->private_value) {
+		char **itemlist = (char **)kctl->private_value;
+		for (i = 0; i < num_ins; i++)
+			kfree(itemlist[i]);
+		kfree(itemlist);
+		kctl->private_value = 0;
+	}
+}
+
+/*
+ * parse a selector unit
+ */
+static int parse_audio_selector_unit(mixer_build_t *state, int unitid, unsigned char *desc)
+{
+	int num_ins = desc[4];
+	int i, err, nameid;
+	usb_mixer_elem_info_t *cval;
+	snd_kcontrol_t *kctl;
+	char **namelist;
+
+	if (! num_ins || desc[0] < 6 + num_ins) {
+		snd_printk(KERN_ERR "invalid SELECTOR UNIT descriptor %d\n", unitid);
+		return -EINVAL;
+	}
+
+	for (i = 0; i < num_ins; i++) {
+		if ((err = parse_audio_unit(state, desc[5 + i])) < 0)
+			return err;
+	}
+
+	cval = snd_magic_kcalloc(usb_mixer_elem_info_t, 0, GFP_KERNEL);
+	if (! cval) {
+		snd_printk(KERN_ERR "cannot malloc kcontrol");
+		return -ENOMEM;
+	}
+	cval->chip = state->chip;
+	cval->ctrlif = state->ctrlif;
+	cval->id = unitid;
+	cval->val_type = USB_MIXER_U8;
+	cval->channels = 1;
+	cval->min = 1;
+	cval->max = num_ins;
+	cval->initialized = 1;
+
+	namelist = kmalloc(sizeof(char *) * num_ins, GFP_KERNEL);
+	if (! namelist) {
+		snd_printk(KERN_ERR "cannot malloc\n");
+		snd_magic_kfree(cval);
+		return -ENOMEM;
+	}
+#define MAX_ITEM_NAME_LEN	64
+	for (i = 0; i < num_ins; i++) {
+		usb_audio_term_t iterm;
+		int len = 0;
+		namelist[i] = kmalloc(MAX_ITEM_NAME_LEN, GFP_KERNEL);
+		if (! namelist[i]) {
+			snd_printk(KERN_ERR "cannot malloc\n");
+			while (--i > 0)
+				kfree(namelist[i]);
+			kfree(namelist);
+			snd_magic_kfree(cval);
+			return -ENOMEM;
+		}
+		if (check_input_term(state, desc[5 + i], &iterm) >= 0)
+			len = get_term_name(state, &iterm, namelist[i], MAX_ITEM_NAME_LEN, 0);
+		if (! len)
+			sprintf(namelist[i], "Input %d", i);
+	}
+
+	kctl = snd_ctl_new1(&mixer_selectunit_ctl, cval);
+	if (! kctl) {
+		snd_printk(KERN_ERR "cannot malloc kcontrol");
+		snd_magic_kfree(cval);
+		return -ENOMEM;
+	}
+	kctl->private_value = (unsigned long)namelist;
+	kctl->private_free = usb_mixer_selector_elem_free;
+
+	nameid = desc[desc[0] - 1];
+	if (nameid)
+		snd_usb_copy_string_desc(state, nameid, kctl->id.name, sizeof(kctl->id.name));
+	else {
+		int len = get_term_name(state, &state->oterm,
+					kctl->id.name, sizeof(kctl->id.name), 0);
+		if (! len)
+			len = sprintf(kctl->id.name, "USB");
+		if ((state->oterm.type & 0xff00) == 0x0100)
+			strcpy(kctl->id.name + len, " Capture Source");
+		else
+			strcpy(kctl->id.name + len, " Playback Source");
+	}
+
+	snd_printdd(KERN_INFO "[%d] SU [%s] items = %d\n",
+		    cval->id, kctl->id.name, num_ins);
+	if ((err = add_control_to_empty(state->chip->card, kctl)) < 0)
+		return err;
+
+	return 0;
+}
+
+
+/*
+ * parse an audio unit recursively
+ */
+
+static int parse_audio_unit(mixer_build_t *state, int unitid)
+{
+	unsigned char *p1;
+
+	if (test_and_set_bit(unitid, state->unitbitmap))
+		return 0; /* the unit already visited */
+
+	p1 = find_audio_control_unit(state, unitid);
+	if (!p1) {
+		snd_printk(KERN_ERR "usbaudio: unit %d not found!\n", unitid);
+		return -EINVAL;
+	}
+
+	switch (p1[2]) {
+	case INPUT_TERMINAL:
+		return 0; /* NOP */
+	case MIXER_UNIT:
+		return parse_audio_mixer_unit(state, unitid, p1);
+	case SELECTOR_UNIT:
+		return parse_audio_selector_unit(state, unitid, p1);
+	case FEATURE_UNIT:
+		return parse_audio_feature_unit(state, unitid, p1);
+	case PROCESSING_UNIT:
+		return parse_audio_processing_unit(state, unitid, p1);
+	case EXTENSION_UNIT:
+		return parse_audio_extension_unit(state, unitid, p1);
+	default:
+		snd_printk(KERN_ERR "usbaudio: unit %u: unexpected type 0x%02x\n", unitid, p1[2]);
+		return -EINVAL;
+	}
+}
+
+/*
+ * create mixer controls
+ *
+ * walk through all OUTPUT_TERMINAL descriptors to search for mixers
+ */
+int snd_usb_create_mixer(snd_usb_audio_t *chip, int ctrlif, unsigned char *buffer, int buflen)
+{
+	unsigned char *desc;
+	mixer_build_t state;
+	int err;
+
+	strcpy(chip->card->mixername, "USB Mixer");
+
+	memset(&state, 0, sizeof(state));
+	state.chip = chip;
+	state.buffer = buffer;
+	state.buflen = buflen;
+	state.ctrlif = ctrlif;
+	desc = NULL;
+	while ((desc = snd_usb_find_csint_desc(buffer, buflen, desc, OUTPUT_TERMINAL, ctrlif, -1)) != NULL) {
+		if (desc[0] < 9)
+			continue; /* invalid descriptor? */
+		set_bit(desc[3], state.unitbitmap);  /* mark terminal ID as visited */
+		state.oterm.id = desc[3];
+		state.oterm.type = combine_word(&desc[4]);
+		state.oterm.name = desc[8];
+		err = parse_audio_unit(&state, desc[7]);
+		if (err < 0)
+			return err;
+	}
+	return 0;
+}
diff -Nru a/sound/usb/usbquirks.h b/sound/usb/usbquirks.h
--- /dev/null	Wed Dec 31 16:00:00 1969
+++ b/sound/usb/usbquirks.h	Mon Oct  7 11:26:06 2002
@@ -0,0 +1,492 @@
+/*
+ * ALSA USB Audio Driver
+ *
+ * Copyright (c) 2002 by Takashi Iwai <tiwai@suse.de>,
+ *                       Clemens Ladisch <clemens@ladisch.de>
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+
+/*
+ * The contents of this file are part of the driver's id_table.
+ *
+ * In a perfect world, this file would be empty.
+ */
+
+#define USB_DEVICE_VENDOR_SPEC(vend, prod) \
+	.match_flags = USB_DEVICE_ID_MATCH_VENDOR | \
+		       USB_DEVICE_ID_MATCH_PRODUCT | \
+		       USB_DEVICE_ID_MATCH_INT_CLASS, \
+	.idVendor = vend, \
+	.idProduct = prod, \
+	.bInterfaceClass = USB_CLASS_VENDOR_SPEC
+
+#if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE)
+
+/* Yamaha devices */
+{
+	USB_DEVICE_VENDOR_SPEC(0x0499, 0x1000),
+	.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
+		.vendor_name = "Yamaha",
+		.product_name = "UX256",
+		.ifnum = QUIRK_ANY_INTERFACE,
+		.type = QUIRK_MIDI_YAMAHA
+	}
+},
+{
+	USB_DEVICE_VENDOR_SPEC(0x0499, 0x1001),
+	.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
+		.vendor_name = "Yamaha",
+		.product_name = "MU1000",
+		.ifnum = QUIRK_ANY_INTERFACE,
+		.type = QUIRK_MIDI_YAMAHA
+	}
+},
+{
+	USB_DEVICE_VENDOR_SPEC(0x0499, 0x1002),
+	.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
+		.vendor_name = "Yamaha",
+		.product_name = "MU2000",
+		.ifnum = QUIRK_ANY_INTERFACE,
+		.type = QUIRK_MIDI_YAMAHA
+	}
+},
+{
+	USB_DEVICE_VENDOR_SPEC(0x0499, 0x1003),
+	.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
+		.vendor_name = "Yamaha",
+		.product_name = "MU500",
+		.ifnum = QUIRK_ANY_INTERFACE,
+		.type = QUIRK_MIDI_YAMAHA
+	}
+},
+{
+	USB_DEVICE_VENDOR_SPEC(0x0499, 0x1004),
+	.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
+		.vendor_name = "Yamaha",
+		.product_name = "UW500",
+		.ifnum = 3,
+		.type = QUIRK_MIDI_YAMAHA
+	}
+},
+{
+	USB_DEVICE_VENDOR_SPEC(0x0499, 0x1005),
+	.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
+		.vendor_name = "Yamaha",
+		.product_name = "MOTIF6",
+		.ifnum = QUIRK_ANY_INTERFACE,
+		.type = QUIRK_MIDI_YAMAHA
+	}
+},
+{
+	USB_DEVICE_VENDOR_SPEC(0x0499, 0x1006),
+	.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
+		.vendor_name = "Yamaha",
+		.product_name = "MOTIF7",
+		.ifnum = QUIRK_ANY_INTERFACE,
+		.type = QUIRK_MIDI_YAMAHA
+	}
+},
+{
+	USB_DEVICE_VENDOR_SPEC(0x0499, 0x1007),
+	.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
+		.vendor_name = "Yamaha",
+		.product_name = "MOTIF8",
+		.ifnum = QUIRK_ANY_INTERFACE,
+		.type = QUIRK_MIDI_YAMAHA
+	}
+},
+{
+	USB_DEVICE_VENDOR_SPEC(0x0499, 0x1008),
+	.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
+		.vendor_name = "Yamaha",
+		.product_name = "UX96",
+		.ifnum = QUIRK_ANY_INTERFACE,
+		.type = QUIRK_MIDI_YAMAHA
+	}
+},
+{
+	USB_DEVICE_VENDOR_SPEC(0x0499, 0x1009),
+	.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
+		.vendor_name = "Yamaha",
+		.product_name = "UX16",
+		.ifnum = QUIRK_ANY_INTERFACE,
+		.type = QUIRK_MIDI_YAMAHA
+	}
+},
+{
+	USB_DEVICE_VENDOR_SPEC(0x0499, 0x100a),
+	.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
+		.vendor_name = "Yamaha",
+		.product_name = "EOS BX",
+		.ifnum = 3,
+		.type = QUIRK_MIDI_YAMAHA
+	}
+},
+{
+	USB_DEVICE_VENDOR_SPEC(0x0499, 0x100e),
+	.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
+		.vendor_name = "Yamaha",
+		.product_name = "S08",
+		.ifnum = QUIRK_ANY_INTERFACE,
+		.type = QUIRK_MIDI_YAMAHA
+	}
+},
+{
+	USB_DEVICE_VENDOR_SPEC(0x0499, 0x100f),
+	.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
+		.vendor_name = "Yamaha",
+		.product_name = "CLP-150",
+		.ifnum = QUIRK_ANY_INTERFACE,
+		.type = QUIRK_MIDI_YAMAHA
+	}
+},
+{
+	USB_DEVICE_VENDOR_SPEC(0x0499, 0x1010),
+	.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
+		.vendor_name = "Yamaha",
+		.product_name = "CLP-170",
+		.ifnum = QUIRK_ANY_INTERFACE,
+		.type = QUIRK_MIDI_YAMAHA
+	}
+},
+
+/*
+ * Once upon a time people thought, "Wouldn't it be nice if there was a
+ * standard for USB MIDI devices, so that device drivers would not be forced
+ * to know about the quirks of specific devices?"  So Roland went ahead and
+ * wrote the USB Device Class Definition for MIDI Devices, and the USB-IF
+ * endorsed it, and now everybody designing USB MIDI devices does so in
+ * agreement with this standard (or at least tries to).
+ *
+ * And if you prefer a happy end, you can imagine that Roland devices set a
+ * good example. Instead of being completely fucked up due to the lack of
+ * class-specific descriptors.
+ */
+{
+	USB_DEVICE_VENDOR_SPEC(0x0582, 0x0000),
+	.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
+		.vendor_name = "Roland",
+		.product_name = "UA-100",
+		.ifnum = 2,
+		.type = QUIRK_MIDI_FIXED_ENDPOINT,
+		.data = & (const snd_usb_midi_endpoint_info_t) {
+			.epnum = -1,
+			.out_cables = 0x0007,
+			.in_cables  = 0x0007
+		}
+	}
+},
+{
+	USB_DEVICE_VENDOR_SPEC(0x0582, 0x0002),
+	.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
+		.vendor_name = "EDIROL",
+		.product_name = "UM-4",
+		.ifnum = 2,
+		.type = QUIRK_MIDI_FIXED_ENDPOINT,
+		.data = & (const snd_usb_midi_endpoint_info_t) {
+			.epnum = -1,
+			.out_cables = 0x000f,
+			.in_cables  = 0x000f
+		}
+	}
+},
+{
+	USB_DEVICE_VENDOR_SPEC(0x0582, 0x0003),
+	.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
+		.vendor_name = "Roland",
+		.product_name = "SC-8850",
+		.ifnum = 2,
+		.type = QUIRK_MIDI_FIXED_ENDPOINT,
+		.data = & (const snd_usb_midi_endpoint_info_t) {
+			.epnum = -1,
+			.out_cables = 0x003f,
+			.in_cables  = 0x003f
+		}
+	}
+},
+{
+	USB_DEVICE_VENDOR_SPEC(0x0582, 0x0004),
+	.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
+		.vendor_name = "Roland",
+		.product_name = "U-8",
+		.ifnum = 2,
+		.type = QUIRK_MIDI_FIXED_ENDPOINT,
+		.data = & (const snd_usb_midi_endpoint_info_t) {
+			.epnum = -1,
+			.out_cables = 0x0003,
+			.in_cables  = 0x0003
+		}
+	}
+},
+{
+	USB_DEVICE_VENDOR_SPEC(0x0582, 0x0005),
+	.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
+		.vendor_name = "EDIROL",
+		.product_name = "UM-2",
+		.ifnum = 2,
+		.type = QUIRK_MIDI_FIXED_ENDPOINT,
+		.data = & (const snd_usb_midi_endpoint_info_t) {
+			.epnum = -1,
+			.out_cables = 0x0003,
+			.in_cables  = 0x0003
+		}
+	}
+},
+{
+	USB_DEVICE_VENDOR_SPEC(0x0582, 0x0007),
+	.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
+		.vendor_name = "Roland",
+		.product_name = "SC-8820",
+		.ifnum = 2,
+		.type = QUIRK_MIDI_FIXED_ENDPOINT,
+		.data = & (const snd_usb_midi_endpoint_info_t) {
+			.epnum = -1,
+			.out_cables = 0x0013,
+			.in_cables  = 0x0013
+		}
+	}
+},
+{
+	USB_DEVICE_VENDOR_SPEC(0x0582, 0x0008),
+	.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
+		.vendor_name = "Roland",
+		.product_name = "PC-300",
+		.ifnum = 2,
+		.type = QUIRK_MIDI_FIXED_ENDPOINT,
+		.data = & (const snd_usb_midi_endpoint_info_t) {
+			.epnum = -1,
+			.out_cables = 0x0001,
+			.in_cables  = 0x0001
+		}
+	}
+},
+{
+	USB_DEVICE_VENDOR_SPEC(0x0582, 0x0009),
+	.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
+		.vendor_name = "EDIROL",
+		.product_name = "UM-1",
+		.ifnum = 2,
+		.type = QUIRK_MIDI_FIXED_ENDPOINT,
+		.data = & (const snd_usb_midi_endpoint_info_t) {
+			.epnum = -1,
+			.out_cables = 0x0001,
+			.in_cables  = 0x0001
+		}
+	}
+},
+{
+	USB_DEVICE_VENDOR_SPEC(0x0582, 0x000b),
+	.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
+		.vendor_name = "Roland",
+		.product_name = "SK-500",
+		.ifnum = 2,
+		.type = QUIRK_MIDI_FIXED_ENDPOINT,
+		.data = & (const snd_usb_midi_endpoint_info_t) {
+			.epnum = -1,
+			.out_cables = 0x0013,
+			.in_cables  = 0x0013
+		}
+	}
+},
+{
+	USB_DEVICE_VENDOR_SPEC(0x0582, 0x000c),
+	.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
+		.vendor_name = "Roland",
+		.product_name = "SC-D70",
+		.ifnum = 2,
+		.type = QUIRK_MIDI_FIXED_ENDPOINT,
+		.data = & (const snd_usb_midi_endpoint_info_t) {
+			.epnum = -1,
+			.out_cables = 0x0007,
+			.in_cables  = 0x0007
+		}
+	}
+},
+{
+	USB_DEVICE_VENDOR_SPEC(0x0582, 0x0012),
+	.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
+		.vendor_name = "Roland",
+		.product_name = "XV-5050",
+		.ifnum = 0,
+		.type = QUIRK_MIDI_FIXED_ENDPOINT,
+		.data = & (const snd_usb_midi_endpoint_info_t) {
+			.epnum = -1,
+			.out_cables = 0x0001,
+			.in_cables  = 0x0001
+		}
+	}
+},
+{
+	USB_DEVICE_VENDOR_SPEC(0x0582, 0x0014),
+	.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
+		.vendor_name = "EDIROL",
+		.product_name = "UM-880",
+		.ifnum = 0,
+		.type = QUIRK_MIDI_FIXED_ENDPOINT,
+		.data = & (const snd_usb_midi_endpoint_info_t) {
+			.epnum = -1,
+			.out_cables = 0x01ff,
+			.in_cables  = 0x01ff
+		}
+	}
+},
+{
+	USB_DEVICE_VENDOR_SPEC(0x0582, 0x0016),
+	.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
+		.vendor_name = "EDIROL",
+		.product_name = "SD-90",
+		.ifnum = 2,
+		.type = QUIRK_MIDI_FIXED_ENDPOINT,
+		.data = & (const snd_usb_midi_endpoint_info_t) {
+			.epnum = -1,
+			.out_cables = 0x000f,
+			.in_cables  = 0x000f
+		}
+	}
+},
+{
+	USB_DEVICE_VENDOR_SPEC(0x0582, 0x0023),
+	.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
+		.vendor_name = "EDIROL",
+		.product_name = "UM-550",
+		.ifnum = 0,
+		.type = QUIRK_MIDI_FIXED_ENDPOINT,
+		.data = & (const snd_usb_midi_endpoint_info_t) {
+			.epnum = -1,
+			.out_cables = 0x003f,
+			.in_cables  = 0x003f
+		}
+	}
+},
+{
+	USB_DEVICE_VENDOR_SPEC(0x0582, 0x0027),
+	.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
+		.vendor_name = "EDIROL",
+		.product_name = "SD-20",
+		.ifnum = 0,
+		.type = QUIRK_MIDI_FIXED_ENDPOINT,
+		.data = & (const snd_usb_midi_endpoint_info_t) {
+			.epnum = -1,
+			.out_cables = 0x0003,
+			.in_cables  = 0x0007
+		}
+	}
+},
+{
+	USB_DEVICE_VENDOR_SPEC(0x0582, 0x0029),
+	.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
+		.vendor_name = "EDIROL",
+		.product_name = "SD-80",
+		.ifnum = 0,
+		.type = QUIRK_MIDI_FIXED_ENDPOINT,
+		.data = & (const snd_usb_midi_endpoint_info_t) {
+			.epnum = -1,
+			.out_cables = 0x000f,
+			.in_cables  = 0x000f
+		}
+	}
+},
+{
+	USB_DEVICE_VENDOR_SPEC(0x0582, 0x002b),
+	.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
+		.vendor_name = "EDIROL",
+		.product_name = "UA-700",
+		.ifnum = 3,
+		.type = QUIRK_MIDI_FIXED_ENDPOINT,
+		.data = & (const snd_usb_midi_endpoint_info_t) {
+			.epnum = -1,
+			.out_cables = 0x0003,
+			.in_cables  = 0x0003
+		}
+	}
+},
+
+/* Midiman/M-Audio devices */
+{
+	USB_DEVICE_VENDOR_SPEC(0x0763, 0x1002),
+	.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
+		.vendor_name = "M-Audio",
+		.product_name = "MidiSport 2x2",
+		.ifnum = QUIRK_ANY_INTERFACE,
+		.type = QUIRK_MIDI_MIDIMAN,
+		.data = (void*) 2
+	}
+},
+{
+	USB_DEVICE_VENDOR_SPEC(0x0763, 0x1011),
+	.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
+		.vendor_name = "M-Audio",
+		.product_name = "MidiSport 1x1",
+		.ifnum = QUIRK_ANY_INTERFACE,
+		.type = QUIRK_MIDI_MIDIMAN,
+		.data = (void*) 1
+	}
+},
+{
+	USB_DEVICE_VENDOR_SPEC(0x0763, 0x1015),
+	.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
+		.vendor_name = "M-Audio",
+		.product_name = "Keystation",
+		.ifnum = QUIRK_ANY_INTERFACE,
+		.type = QUIRK_MIDI_MIDIMAN,
+		.data = (void*) 1
+	}
+},
+{
+	USB_DEVICE_VENDOR_SPEC(0x0763, 0x1021),
+	.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
+		.vendor_name = "M-Audio",
+		.product_name = "MidiSport 4x4",
+		.ifnum = QUIRK_ANY_INTERFACE,
+		.type = QUIRK_MIDI_MIDIMAN,
+		.data = (void*) 4
+	}
+},
+{
+	USB_DEVICE_VENDOR_SPEC(0x0763, 0x1033),
+	.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
+		.vendor_name = "M-Audio",
+		.product_name = "MidiSport 8x8",
+		.ifnum = QUIRK_ANY_INTERFACE,
+		.type = QUIRK_MIDI_MIDIMAN,
+		.data = (void*) 9
+	}
+},
+{
+	USB_DEVICE_VENDOR_SPEC(0x0763, 0x2001),
+	.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
+		.vendor_name = "M-Audio",
+		.product_name = "Quattro",
+		.ifnum = 9,
+		.type = QUIRK_MIDI_MIDIMAN,
+		.data = (void*) 1
+	}
+},
+{
+	USB_DEVICE_VENDOR_SPEC(0x0763, 0x2003),
+	.driver_info = (unsigned long) & (const snd_usb_audio_quirk_t) {
+		.vendor_name = "M-Audio",
+		.product_name = "AudioPhile",
+		.ifnum = 9,
+		.type = QUIRK_MIDI_MIDIMAN,
+		.data = (void*) 1
+	}
+},
+
+#endif /* CONFIG_SND_SEQUENCER(_MODULE) */
+
+#undef USB_DEVICE_VENDOR_SPEC
